Everything you write down gets read at least twice: once by something forgiving and once by something strict — and the two rarely tell you when they disagree. A web browser will bend over backwards to make a slightly-wrong URL work. A form validator, an API, a linter will not. When only the forgiving reader has ever looked at a piece of your data, everything appears fine and drift accumulates in the dark, because nothing is there to flag it. This is a short devlog about the afternoon a strict reader finally looked at a link we had been writing the lazy way for as long as the link had existed — and about the small mess its forgiving cousin had been quietly covering for us the whole time.
A validator said no to a link that works
We were wiring up Iron
Swarm, our top-down tank game, into LevelPlay — the ironSource ad-mediation
dashboard — and the iOS ‘App store URL’ field lit up red:
Enter store URL for the App Store. The value in the box was a real, working App
Store link: https://apps.apple.com/app/id6764359993. Paste it into any
browser and Iron Swarm’s store page opens. What made the rejection absurd was the
panel directly below the field, which had already taken that exact same URL, resolved it,
and was cheerfully displaying the result: Iron Swarm, ID: 6764359993. One half of
the same form understood the link perfectly. The other half insisted it was not a store
URL at all. When one tool accepts your data and a second tool right next to it refuses the
identical bytes, the interesting question is never ‘which one is broken’ —
it is ‘what does each of them actually read.’
Two forms of the same link
An App Store link comes in two shapes. There is the bare form
we had pasted, https://apps.apple.com/app/id6764359993, and there is the
canonical form Apple itself hands you when you copy a link from the store:
https://apps.apple.com/us/app/iron-swarm/id6764359993. The canonical adds two
things — a storefront/region code (/us/) and a human-readable name slug
(iron-swarm/) — in front of the part that actually does the work. And
only one part does the work: the id6764359993. The region and the slug are
cosmetic. Apple reads the numeric id and nothing else routes on the slug; you can put a
wrong or missing name there and the page still opens to the right app. The resolver panel
in that very form had just proved it, pulling the whole app record out of the id alone.
The id<number> is the identifier. The rest is decoration that happens to
look load-bearing.
Why the browser never complained
So why had the bare form worked flawlessly on our website for
every visitor who ever clicked ‘App Store’? Because Apple answers a request for
the bare URL with a 301 — a permanent redirect — that points at the
full canonical URL, and a browser follows that redirect without a flicker. The visitor
clicks the short link, the server says ‘actually, it lives over here,’ the
browser goes there, and the store page appears. Nobody sees the hop. The bare form was, for
years of clicks, invisible-but-correct: technically the lazy version, functionally
indistinguishable from the right one, because a forgiving reader silently repaired it on
every single request.
That forgiveness has a name. The old networking maxim — Postel’s robustness principle — says to be conservative in what you send and liberal in what you accept, and a browser is about as liberal a reader as software gets: it follows redirects, guesses at malformed markup, and works hard to render something rather than nothing. A form validator is built on the opposite instinct. LevelPlay’s field runs the submitted string against a strict pattern and does not chase a redirect to see where it lands; if the literal text does not match the shape it expects — region, slug, id — it rejects it, full stop. Same URL, two readers, opposite verdicts. The lenient one forgave what the strict one refused, and the gap between them is exactly where this kind of problem lives.
The mess the forgiveness hid
Fixing the one field would have taken ten seconds — paste the canonical URL, move on. But a rejected field is a gift, because it tells you a strict reader exists and has opinions, so the honest move was to ask where else we had been getting away with the lazy form. We audited our own site, and the answer was: nearly everywhere. NeuralSpark’s store links were already canonical, the slugged full form. Sudoku’s were bare. Iron Swarm’s were bare. And every App Store link in our version manifests — the JSON files the apps themselves read to prompt an update — was bare too. The links were inconsistent across the site, some slugged and some not, and no one had ever noticed for the most unsettling reason available: nothing had ever looked broken. A browser had been repairing every one of them on every click, so the inconsistency generated no error, no report, no smell. It was pure latent drift — the kind that stays invisible right up until the day a strict consumer, a partner’s dashboard or an automated import, reads the same data and refuses it.
Writing the strict form, once and everywhere
The fix was not ‘make the validator happy’; it was
‘write the form we should have written in the first place, in every place.’ We
normalized every App Store link across the site to the canonical shape — eighteen URLs
across ten files: the HTML anchors people click, the JSON-LD a search crawler reads, and the
master version manifest plus its per-app mirrors. The one place that needed care was the name
slug, because we refused to guess it. A slug is cosmetic, but writing a made-up one is still
writing something untrue, so we resolved each app’s real slug authoritatively from
Apple’s own iTunes Lookup API, which returns a trackViewUrl — the
canonical store URL — for a given id. That is how we learned Sudoku’s real slug is
sudoku-by-wizuslabs, not the sudoku we would have guessed. One app,
Shatterstone, is not live yet and returned no lookup result, so rather than invent a slug we
left it on the safe region-plus-id fallback, .../us/app/id<number> —
which is still canonical enough for the strict reader and contains nothing we cannot stand
behind. Then we deployed and confirmed the canonical links resolve in a live browser, because
a fix you have not watched work is a theory, not a fix.
Worth saying plainly: this was not a bug fix. Nothing had been broken for any user, and the bare links kept redirecting fine right up to the moment we replaced them. It was a consistency-and-portability fix — the difference between data that happens to work through the one lenient reader you have tested and data that is correct for any reader who shows up next.
Google never handed us the rope
The tidy part of the story is that the same audit on the Google
Play side found nothing to fix, and the reason is instructive. A Play store link is
play.google.com/store/apps/details?id=<package>, and that is the only form
there is. The identifier is the package name, it sits in a query parameter, and there is no
region prefix and no cosmetic slug to add or omit — so there is no bare-versus-canonical
split to drift between. Our Play links were uniform because Google never gave us a second way
to write them. That is the whole asymmetry: this trap is Apple-specific not because Apple is
careless but because Apple offers two forms and a redirect that papers over the difference.
Two representations of the same thing, plus a lenient reader that treats them as equal, is the
exact recipe for silent divergence. Where there is only one form, there is nothing to
diverge.
Write for the reader you don’t control
The lesson is not ‘validators are annoying’ — the validator was right, and it did us a favour by being stricter than the browser we had been testing against. The lesson is about who reads your data next. You almost never control that. Today it is your own browser; tomorrow it is a partner’s dashboard, a regex in a script you did not write, an importer, a linter, a future teammate’s parser that has no patience for the redirect that used to save you. A lenient reader is a comfortable place to live because it forgives your shortcuts, but every shortcut it forgives is drift you are storing up for the strict reader who arrives later — and that reader always arrives. So write the canonical form even where the lenient one works. Write the identifier in full, resolve the cosmetic parts from an authoritative source instead of guessing, and never rely on a redirect to launder a link you could have written correctly the first time. The cost is a few keystrokes now; the alternative is a red field, or worse a silent failure, at the least convenient possible moment. This piece pairs with a companion from the same week — the browser tells the truth, where a real browser revealed a bug our theory had hidden; here a forgiving browser did the reverse and hid a mess by being kind. Same tool, both lessons: the browser is honest about what it will render, and quietly generous about what it will forgive — and it is the generosity you have to watch. More of how we build and where it breaks is on the WizusLabs Engineering blog, including the wider tangle of shipping one codebase to two stores.
Notes
This is a first-hand devlog about a change we made on our own
site and store links. It contains no performance metrics or engagement numbers, because the
piece is about a data-hygiene discipline, not a measurement, and inventing a figure would
undercut the point. The only counts stated are exact and drawn from the change itself: the
canonical normalization touched eighteen App Store URLs across ten files (HTML anchors,
JSON-LD, and the version manifest plus per-app mirrors), NeuralSpark’s links were
already canonical while Sudoku’s and Iron Swarm’s were bare, and Shatterstone is
not yet live and so uses a region-plus-id fallback with no invented slug. The App Store id
6764359993 and Sudoku’s real slug sudoku-by-wizuslabs were
read directly from Apple’s own resolver and iTunes Lookup response. Iron Swarm, Sudoku,
and NeuralSpark are games and apps this studio builds; they are free and ad-supported, each
with a single optional Pro purchase that removes ads — we are not claiming an ad-free
product. The behaviours of the redirect, the validator, and the store URL forms below are
described from what we observed while making the fix; the general mechanisms are documented
in the references.
Sources
- MDN Web Docs — 301 Moved Permanently (the permanent-redirect status a browser follows transparently, which is why the bare link resolved).
- MDN Web Docs — HTTP redirections (how user agents chase redirects to a final resource — the lenient-reader behaviour).
- Apple Developer —
iTunes Search / Lookup API
(the
lookup?id=endpoint whosetrackViewUrlgives an app’s canonical store URL and real name slug). - IETF RFC 761 — the robustness principle (“be conservative in what you do, be liberal in what you accept from others” — the lenient/strict framing named in the post).
These references support only the general mechanisms named above — the redirect status, redirect-following, the lookup endpoint, and the robustness principle. The specific inconsistency, the audit, and the fix are our own first-hand account.
Keep reading: all posts on the WizusLabs Engineering blog.