We build a resume builder. It started Chinese-first, then we opened an English side. Yesterday I finally counted the template library from the same file the renderer reads, instead of trusting the marketing number.
47 templates are Chinese-style layouts: ID photo block, a "job objective" paragraph, gender and date of birth in a grid. That is what a resume looks like in China, and it is correct there.
US-style templates we had labeled: 12. Actually selectable: 7. The other five were retired months ago and nobody updated the label. Of those 7, three had near-identical single-column headers. So an English visitor picking a layout had maybe two real choices, while we told ourselves we had 60-something templates.
The worse part was a bug I want to write down because the name is the lesson.
Skins had a flag called "photoless". I had read that name a dozen times and never checked it. It does not mean "this layout has no photo". It only controlled whether the preview drew a grey placeholder box. The real renderer had exactly one condition: if the user uploaded an avatar, draw it. Template style was not part of that condition at all.
So: a user uploads an ID photo for their Chinese resume, which is normal and expected. They switch to a US-style template to apply to American jobs. They export the PDF. The photo is still there. US employers deliberately avoid photos on resumes to keep discrimination claims off the table, so this is not a cosmetic slip, it is the exact thing the template existed to remove. Shipped under a flag literally named "photoless".
The fix was to make template style the only source of truth instead of the flag: photo, objective block, gender and birth date are all suppressed when the style is English, with no per-template allowlist, so anything we add later is covered by default. Nothing is deleted from the user's data. Switch back to a Chinese layout and it all comes back.
Then we added six US layouts chosen by tier rather than by color scheme: conservative (finance, law, medicine), modern general, pure ATS with zero lines and zero icons, executive, new grad, and engineer. Plus seven template-type landing pages, because people search by layout type and we had no page for any of them.
Two things I would take from this if you are localizing anything:
Has anyone else found a flag that named an intent nobody had actually implemented? I suspect they cluster in exactly these localization seams.
Buying an asset for $56k and growing it to $200M over 21 years is one of the great long horizon acquisition stories. Curious what the diligence looked like back then, was there any formal process at all, or was it mostly a bet on the brand and the domain itself? Deals that small rarely get the kind of scrutiny a $56k price tag would suggest they deserved in hindsight
"photoless" turning out to mean "no grey placeholder" while the export still includes the actual photo is such a nasty bug. the fix being at the template-style level makes sense. did any user catch it first, or did this only come out when you compared the rendered PDF against the preview?
Nobody reported it. I found it comparing an exported PDF against the preview, which is a little scary: it means users probably just left instead of telling us.
After adding the new US-oriented layouts, what user behavior would tell you the localization fix actually improved the English funnel—template selection, completed exports, or something further downstream?
The metric that would actually convince me: English-visitor completed exports per session, before vs after the fix — not template selection. Template clicks will spike just from novelty and the six new layouts; completion is the only number that says the funnel got better.
Two things worth separating that are easy to mix. First, did the fix remove the bug? That you verify by exporting as a user across every style and diffing what comes out — config passing a test does not prove the artifact is right. Second, did removing the bug move the funnel? That you only know if English completion was measurably worse than Chinese before the fix. If English visitors were not exporting much anyway, the photo bug was a real defect but not the funnel's constraint — and the actual constraint might just be that the people arriving at the English side are not job-seekers, or that templates are not the trust signal that matters to them.
One cheap check to tell the two apart: compare the completion rate of users who started in an English template against those who switched into one mid-session. The switchers are the people who actually hit the bug. If their completion converges with the starters after your fix, the bug was the leak. If it does not, the leak is somewhere else and the templates were never the binding constraint.
The starters-vs-switchers split is the part I wouldn't have thought of, thanks. I'll measure completed exports, not template clicks, and look at that cohort once there's enough traffic to say anything.
That’s a much cleaner way to separate the bug from the actual funnel constraint. Could be useful to compare notes on this as the post-fix data comes in sometime by email, if you’re open to it.
Merging a translation layer directly to production without formatting the context. The copy pipeline is broken, but Simon's tech debt realization is pure engineering grit. 📈
"Count from what the renderer reads" is going straight on a sticky note for me. I build templates too, and your point about the inventory number being a marketing number hit home - a library count means nothing if it doesn't match what the user actually receives and can use. The "photoless" flag story is a great reminder that the most dangerous bugs are the ones with confident names. Thanks for writing this up so plainly; lesson #1 generalizes to basically every product with a config layer.
I’ve found the same seam in localized workflows: treat locale as policy, not presentation. I’d add one fixture per locale that renders from the real data path and assert the forbidden fields are absent in the exported artifact. That catches a misleading flag before it becomes a customer incident.
This is the same trap I watch for in a small info product: a big inventory number can hide that the buyer is choosing between only a couple of meaningful outcomes. I’d put a plain-language “for / not for” line beside each tier or template and test the exported artifact, not just the preview.
For a $27 guide, the strongest trust signal was showing exactly what job it helps with—and what it won’t cover—rather than padding it with extra sections. A tiny end-to-end test (select → export → use) for each variant catches the gap between the promise and the thing people actually receive.
Are you separating application country from interface language? Someone using the English UI could still be applying in China, so tying resume rules to language might create another edge case.
Good catch. Right now it's tied to the site language, which is exactly the edge case you describe. Splitting "where are you applying" from UI language is on my list, and it's probably the right unit for the photo rule too.
The inventory point is easy to underestimate: a template count only matters if it matches what a user can actually select and export. I like the style-level fix because it makes the safe behavior the default instead of relying on each new template author to remember a fragile flag. I’d also add a small export test for each style so preview and PDF can’t silently drift apart again.
Your second lesson is worth applying to the seven new landing pages too. If each layout page shows a sample exported by the real renderer at build time, instead of a screenshot someone made once, the marketing can't drift from the product again. A template you retire drops off its page the same day, and the page can never show a photo that the export won't.
Disclosure: I build Mythex, an AI app builder, and we did a version of this with our pricing page this week. The plans in the page's HTML are now generated from the same constants as the pricing cards in the app, so the two can't disagree.
Were any users still on the five retired templates, and what did they get moved to?
This is a great example of how localization problems are often product architecture problems in disguise. The biggest lesson for me is that labels and template counts can create false confidence if nobody verifies what the actual renderer is doing.
The “photoless” flag is especially interesting — one misleading name can survive for months and affect an entire user experience.
The localization seam here looks like a good candidate for a forbidden-field matrix rather than template-by-template tests: for each market or style, list fields that must never appear in the exported artifact, then run every selectable template against the same invariant. That catches the current photo case and future variants without trusting names like
photoless. I’d also seed one deliberately broken template in CI; if the suite stays green, the rule is not testing the exported outcome.This resonates a lot — how long did it take before you saw any real signal on it?
Yes, and your diagnosis of why generalises: the bug was not the logic, it was that the name described an intention while the code enforced a different condition. Nobody audits a boolean that already claims to be correct.
The same shape recurs wherever a config layer and a render layer both believe they own one decision. Three that show up again and again:
A "hidden" flag that stops an element rendering in the list view while the detail page reads straight from the record. Identical to yours: the flag governed one of the two places that drew the thing.
An "anonymous" toggle on a shared report that scrubs the display name and leaves the author id sitting in the JSON the client fetches.
A "noindex" setting in a CMS that writes the meta tag on the page, while the sitemap generator reads a different field and keeps submitting the URL.
The cure is the one you arrived at, stated more generally: assert on the artefact the user receives, never on the flag that was supposed to produce it. Template style as the single source of truth holds precisely because it is evaluated at render. A test that reads config passes forever regardless of what ships.
Which is also why a check against the live page is more useful than it sounds for a small team. nexusbro.com/audit runs its accessibility pass on the live DOM rather than a static snapshot, alongside indexation, canonical and schema checks, 125+ in total, on any URL with no signup. It would not have caught your photo bug, that needed a real user with an avatar switching styles and no crawler simulates it, but it is the right instrument for those seven new template-type landing pages: a brand new page that is quietly unindexable fails in exactly the way your template count did, true on the landing page and useless in the data.
One honest limit on the broader lesson, since you are evidently collecting them. Counting from the renderer's own file is right, and it still only moves the trust problem down one layer. The check that actually holds is exporting the PDF as a user in each style and diffing what came out.
Ha, same bug in a different costume. A flag named after an intent nobody verified. Did you end up checking the content language itself, or the rendered output per locale?
The "photoless" pattern shows up constantly in SEO markup. We scan sites at UtilitySEO and regularly find hreflang tags declaring a page is the Spanish version when the content is still English. Someone set the tag, nobody checked that the content matched. Same shape: a flag named after an intent the page never fulfilled.
Your fix is the right architecture. Making template style the single source of truth means new templates inherit correct behavior by default. We learned the same lesson localizing our site to seven languages — derive the language from the translation pipeline, not from a manual locale field someone sets once and forgets.
"Count from what the renderer reads" generalizes beyond templates. Any time the marketing number lives in a different file from the production number, they drift apart silently the moment someone retires a variant. The drift is always toward optimism.