Over the last eighteen months I have taken three systems into production on my own: an exchange for Connected TV inventory, an exchange with two-sided billing, and a platform for creators. In each case the gap between the first demo that worked and the first version anyone could depend on was somewhere between two and four months.
That ratio is the whole story of why so much AI work is currently stuck. Getting to a working demo is genuinely ten times cheaper than it was three years ago. Getting from that demo to production is exactly as expensive as it always was. The gap did not close. It widened, and now a lot of organisations are standing on the near side of it holding something that works on a laptop.
What the month after actually contains
Not a list of best practices. This is what genuinely consumed the time, in the order it hurt.
Tenancy, applied at the query and not the view
Every reporting query has to be scoped by the tenant taken from the token, at the point the data is fetched. Not filtered in the interface, not checked in a guard the caller might forget. The demo had one customer, so this cost nothing. The second customer is when the demo stops being a demo, and every unscoped query written before that moment is a data leak waiting for the right URL.
Money that reconciles under replay
A charge is not an operation, it is a row. Once you accept that, the rest follows: a replayed event must not charge twice, a refund must not leave an agency's commission stranded on spend that no longer exists, and a crash halfway through has to leave a state you can reason about rather than a number that is quietly wrong.
I learned this the expensive way — by writing the send and then recording it, instead of claiming the row and then sending. Four processes went through that window at once before anything was recorded. Claim first. Act second.
The decision path that cannot touch the database
A television ad break does not wait for your query planner. The serving path for one of these systems runs with zero database reads: every rule, budget and floor lives in an in-memory snapshot that is rebuilt from the control plane every fifteen seconds and swapped atomically.
That constraint is easy to state and reorganises everything. It means every targeting rule has to be expressible in a structure you can rebuild cheaply, and it means you have to be honest about what happens in the seconds when the snapshot is stale.
Knowing which of your traffic is real
Test traffic is indistinguishable from real traffic unless something marks it, and nothing marks it by default. Auditing one reporting surface properly turned up 99.8% of its events originating from our own testing — which means every number on it had been describing us rather than the market.
Test mode cannot be a filter you apply at the end. It has to be a dimension carried on the event from the moment it is created, all the way through aggregation, or it contaminates everything downstream and you will not notice for months.
Words that quietly mean nothing
Forty line items in one system were marked ACTIVE and had no creative attached. Nothing was broken. Nothing threw. They were active in the sense that a column said so, and inert in the sense that they could never serve.
Any status that nothing validates will eventually describe something that is not true. If a state has requirements, something has to enforce them at the moment the state is set.
Counting people, not events
Open and click totals that count every event rather than every person inflate every number a customer shows to their own client. Fixing that retroactively, across data already collected, is more delicate than building it correctly once — and it means telling people their numbers are going down.
Someone has to answer at three in the morning
Alerting is not a feature you add at the end; it is a claim about who is responsible. The failure mode to design against is the alert that becomes the outage: a cooldown that never resets will emit roughly ninety-six identical messages a day, and an alerting channel people have learned to ignore is worse than no channel at all. Every alert I ship now has to answer two questions first — who acts on this, and what stops it repeating.
Why this is worth saying out loud
None of the above is novel. Every one of these is the sort of thing an experienced team knows. The reason it matters right now is that the tooling has made it possible to arrive at a convincing prototype without having met any of it — and a convincing prototype creates a deadline.
So the projects stall in a specific and recognisable way. There is a demo everyone has seen. There is a date. And there is nobody in the room who has crossed this particular gap before and can say which three of the remaining twenty problems are the ones that actually matter.

Written by Manmohan Singh, who builds the systems that move the money. About.
The build log, by mail
New notes, when I publish them.
One mail per note: what broke, the theory I held before I understood it, and the rule I kept afterwards. Irregular on purpose — I write these when something happens, not to a schedule, and I would rather send you nothing than send you filler.
Also in the log
Thirty per cent of our volume stopped earning money
Yield was falling and every instinct in the room said send more. We sent less of what paid, and yield rose about eighty per cent.
The ad you cannot change after you send it
An image URL inside a delivered email is immutable, and Google caches it. Both facts are fixed. The ad still had to change on every open.