A repository tells you what it thinks it is in its oldest commit. The requirements repo from post 3 — the one that instructs the workforce — has an oldest commit dated March 19th, 12:08. The message is in Polish; translated, it says: "Sales strategy, with deep-link support." It adds 1,662 lines across three files: a 39-line README, a 700-line sales strategy, and a 923-line technical specification for deep links. Not one line of it is a requirement in the sense I was taught to write requirements. Six days later, the first mock existed. The product, at that point, did not.
Here is why I no longer find that embarrassing. The product is consumer security — phishing protection sold as a subscription through a European mobile operator. For a product like that, the code was never going to be the hard part; the hard part is the eleven seconds between a subscriber deciding to try it and the app running on their phone, activated, knowing who they are. Every second of friction in that gap is measured in customers who never arrive.
So the strategy document ranks seven sales channels — the operator's self-care app, retail stores, the call center after a phishing incident, SMS campaigns, referral pairing, organic app-store installs, a landing page — by the product of expected conversion and cost of reaching the customer. (The percentages in that table are the document's own estimates, written before launch; whether they held is a question for the operator's dashboards, not for git.) And buried in the onboarding section sits the number that turned a sales document into an engineering problem: for the operator's own channels, an estimated 60–70% of installs could skip asking the customer who they are entirely — if a token can ride the link from the operator's hands into the running app.
That if is the 923 lines.
The problem, for readers who haven't fought it: an app store is a wall. A subscriber taps a link in an SMS from their operator; the link opens the store; the store installs the app; the app launches — and every scrap of context about where that install came from has been stripped somewhere along the way. The customer the operator already identified arrives at your front door anonymous.
The spec's answer is a ladder. A provisioning token — random, single-use, thirty-minute lifetime, created when the operator's channel generates the link, claimed exactly once by the app, with the subscriber's phone number never leaving the backend. Then, in descending order of dignity: the platform's own app-link mechanism when it survives the trip; the install-referrer channel on Android, which the spec estimated would carry ~95% of cases; device fingerprint matching on iOS, estimated at ~85%; and at the bottom of the ladder, asking the customer to type a code from an SMS — the rung that always works and always costs the most patience. There is also a resolver that forces the request onto the cellular interface so the mobile network itself can vouch for the subscriber — the operator's network knows who you are even when your phone won't say. Five implementation sprints, laid out before a single screen of the product existed.
What happened to those five sprints is the part git tells with timestamps, and it is not the story I would have written in advance. March 23rd: the backend records "early integration with deep link server." March 26th: a deep-link module lands in the core backend — with an mTLS guard on its endpoints the same day. April 4th: a feature flag appears, so the subsystem can be switched off. April 9th: its store is simplified. April 10th: a commit removes the module — message: "migrated to LP," the landing-page server at the edge. And on April 18th, a single commit purges the subsystem from the core backend entirely: four controllers, seven services, six DTOs, seven error codes, a dropped database table, the env vars, the docs, the diagrams. The subsystem the first commit specified lived in the core backend for twenty-six days.
I want to dwell on that removal commit, because it is the best thing in this story. It enumerates everything it kills, path by path. It carries a verification block: typecheck, build, 194 of 194 unit tests, and — my favorite line in the whole repo — eight failing end-to-end tests explicitly proven to be pre-existing, by stashing the change, re-running the suite on the untouched branch, and confirming the same eight failures were already there. A deletion that accounts for itself. In post 3 the fleet learned to write down what it rejected; here it wrote down, line by line, what it destroyed.
The subsystem died; the ideas did not. By May 19th the landing-page repo has an SMS-campaign flow — a token in the link, a pre-filled web form on the other end. By June 11th it has activation flows built on the network-vouches-for-you mechanism, with the SMS code kept as the floor beneath everything. The ladder from the March spec is recognizably there, rung by rung — running in a different house than the one the spec drew. The strategy survived contact with the architecture; the architecture just moved it to the edge, where anonymous clicks from the open internet belong.
Why it moved is the gap in the record. The April 10th commit message spends two words on it: "migrated to LP." Memory offers a reason that sounds right — the core backend is the high-trust zone, mutually-authenticated on every endpoint, and a service whose whole job is catching unauthenticated clicks from the public internet was the wrong tenant for that building — but I cannot point at a decision record that says so, because (post 3 again) the decisions directory got its charter after this migration happened. The reasoning lived in sessions and evaporated. The armor came later; this incident is one of the dents that justified it.
The rule this commit taught me: the route to the user is the first requirement — write it before the product exists, because it decides what the product must be. The mocks, the extraction, the tech-stack conventions — all of it, this repo insists, came second. And the corollary the removal commit models: subsystems are allowed to move and allowed to die, but only with a full account — what was destroyed, what was verified, what was already broken before you touched it. The protocol keeps both habits: intent written before work starts, and teardown that enumerates what it removes.
Next post: a security product, by definition, sits between the customer and the thing they are trying to do. Which means it has to answer a question most software never faces — when you break, which way do you fall? Fail open, fail closed, and the incident that made the answer concrete.