AI Rescue · Entrepreneur · 2025
Stitchwork
A two-sided marketplace for custom tailoring, built to an impressive demo with AI — and already taking real payments it wasn't ready to handle.
- Next.js
- PostgreSQL
- Stripe
- Vercel
- $0
- in mispaid payouts since the ledger rewrite
- 11 days
- to a deploy the founder could safely change on her own
- 2
- security issues found and closed before public launch
What we inherited
The founder had shipped a genuinely impressive demo: tailors could list services, customers could book and pay, money moved. She had raised a small pre-seed round on it. The problem was that it was already taking real money from real people, and the payment logic had been assembled across a dozen AI sessions with no consistent model of who was owed what.
What was broken
-
Payouts calculated from the order row at read time
Change a price or refund an order and historical payouts silently changed with it. There was no durable record of what had actually been owed at the time.
-
Webhooks with no idempotency
Payment provider retries are normal and expected behavior. Here, every retry created a duplicate payout record.
-
A server-side secret key inlined into client code
It had been moved into a component during one AI session to make something work, and never moved back.
-
Three different ways to do the same thing
Sessions spread over weeks left three data-fetching patterns and two auth patterns in one codebase. Each session had reinvented the conventions of the last, because it couldn't see them.
What we fixed
-
1
Built an append-only ledger
Money events are now recorded as immutable entries. Balances are derived from them, never edited in place. A past payout cannot change retroactively, which is the property the whole business rests on.
-
2
Made every webhook idempotent
Keyed on the provider's event ID, with a test that replays the same event twenty times and asserts exactly one payout results.
-
3
Rotated the exposed key and moved all secrets server-side
Plus a CI check that fails the build if a secret-shaped string appears in client-side code again.
-
4
Picked one convention and converted the codebase to it
Then documented it in the repository, so the next AI session — or the next developer — extends the existing pattern instead of inventing a fourth one.
Why AI code drifts
The pattern in this codebase wasn’t carelessness. It was structural, and it’s worth understanding if you’re building this way.
An AI session can only act on what it can see. Start a fresh session on a codebase with no documented conventions and no obvious structure, and the model will do something reasonable — but “reasonable” chosen independently, twelve times, produces twelve local optima that don’t compose. That’s how one application ends up with three data-fetching patterns, each of which works.
The fix isn’t to stop using AI. The founder is still building with it, faster than before. The fix was to give the codebase a shape that survives between sessions: documented conventions, a test suite that fails loudly when a pattern is violated, and clear module boundaries so a session only needs to hold one part in context at a time.
Good structure makes AI assistance dramatically more effective. It’s the same property that makes a codebase easy for a new human developer to join.
“They found a payments bug in the first afternoon that would have taken me six months and a lot of angry tailors to find on my own.”
Got something that looks like this?
Thirty minutes, no pitch. We'll tell you what it would actually take.
Book a free 30-min callRather not book a time? Send a message instead.