WizusLabs Engineering · Craft

Write the canonical form: a lenient reader hid a mess

A strict validator refused an App Store link that every browser on earth accepts without complaint. The reason the browser never complained — a redirect it follows silently — turned out to be the same reason a mess had been accumulating in our own links, unseen.

By WizusLabs Engineering · 2026-07-15 · ~7 min read

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.

One URL read by a lenient reader and a strict reader A schematic. A single bare App Store link branches to two readers. The browser is a lenient reader: it follows the 301 redirect, resolves to the canonical URL, and loads correctly. The validator is a strict reader: it runs a regex, follows no redirect, and rejects the identical URL outright. The bare link apps.apple.com/app/id6764359993 The browser lenient reader Follows the 301 redirect to canonical Loads correctly The validator strict reader Strict regex, no redirect followed Rejected outright
The same App Store link, two readers. The browser follows Apple’s 301 redirect and resolves it; a strict validator matches a regex, refuses to follow the redirect, and rejects the identical URL. Schematic, not measured data.

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 whose trackViewUrl gives 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.

← Back to the Blog