Greybook
+ NewSign in

Build log: a billing portal in one week with Claude Code and Codex

Build logBeing testedClaude Code2.1.219Next.js

Posted by fern.dev · Jun 26, 2026

Client needed a self-serve billing portal — plan changes, seat management, invoices, dunning emails — bolted onto an existing Next.js app. I gave myself one week and two agents: Claude Code (Opus 5) for anything touching existing code, Codex for greenfield. Full accounting below, including the day I'd rather not relive.

Day 1 — Spec and scaffold. $6.80. Morning writing the spec myself: data model for subscriptions/seats, which Stripe objects own truth (Stripe owns truth; our DB is a cache — writing this sentence down saved my life on day 4), webhook inventory, page list. Afternoon: Codex scaffolded the new /billing route group, Stripe SDK wiring, and webhook handler skeletons from the spec. 40 minutes, one shot, boringly competent. Claude Code meanwhile mapped the existing auth/org model and wrote TASK.md constraints — the org-to-Stripe-customer mapping had two legacy wrinkles it found that I'd forgotten about.

Day 2 — Webhooks and the subscription state machine. $11.20. Claude Code, all day, on the webhook handlers and state sync. The spec's state diagram earned its keep: the agent implemented checkout.session.completed, customer.subscription.updated, invoice.payment_failed handlers against it, with idempotency keys and a replay test harness I didn't ask for but kept. One real bug it caught in MY spec: my diagram had no transition for a subscription resuming from past_due after dunning succeeds. The agent flagged the hole instead of improvising. Rare and correct.

Day 3 — Seat management UI. $9.40. Codex built the seat CRUD screens against day 2's API. Fast, fine, two problems: it invented an optimistic-update pattern inconsistent with the app's existing SWR usage (my fault — greenfield agent, brownfield conventions, no bridge doc), and its generated tests were the mocked-everything kind that can't fail. Deleted the tests, kept the screens, made Claude Code redo the data layer to match house patterns. Lesson relearned: the greenfield/brownfield split leaks at every seam the two share.

Day 4 — The bad day. $18.60, mostly wasted. Proration. I pointed Claude Code at "seat downgrades prorate correctly" without specifying what correctly meant, because I didn't know. The agent produced three successive plausible implementations, each matching a different defensible reading of Stripe's proration behavior, while I test-mode-verified each against what the client actually wanted (credit on next invoice, never refunds). Four hours, $18, and the thing that fixed it was closing the laptop and writing the five proration rules in English. Fed the rules in; correct implementation in 25 minutes. The agent was never the bottleneck. The spec was, and day 1 me knew this and day 4 me forgot.

Day 5 — Dunning emails, polish, ship. $15.40. Claude Code wired invoice.payment_failed into the existing email infra (found the retry-throttling wrapper and used it — the brownfield mapping from day 1 paying off), built the invoice list page, and burned through my punch list of fourteen small fixes. Shipped to staging by 4pm; client poked at it Friday; live Monday with one bug so far (invoice list pagination off-by-one on exactly 20 invoices — a properly humbling classic).

Totals: $61.40 model spend, ~28 hours of my time versus honest estimate of 60-70 solo. Roughly a 2.3x week.

What I'd tell past me: the two agents were interchangeable on maybe 60% of tasks; the value was routing the other 40% correctly. Codex greenfield, Claude Code brownfield held up — but write the bridge doc when their outputs must touch. And every dollar of day 4 was me paying the agent to enumerate my own ambiguity: when you can't state the rule, you're not delegating, you're gambling with a fluent croupier.

Discussion · 7 comments

Strong evidence gets promoted into the record above.

clara.builds

Day 4 is the ROI thread's spec-first finding in narrative form and I'll be citing it as such. Notable that your one production bug came from the punch-list blitz — fourteen small fixes at end-of-week is exactly where review attention goes to die, human or agent. Our data says the same: last-10%-polish PRs have our highest escape rate.

18
fern.devtrusted

Confirmed and worse than you think: I reviewed the pagination fix least carefully of the fourteen because it looked most trivial. There's probably a law in there. The croupier always wins the hands you don't watch.

13
mateohq

The $61.40 is the number clients see, so for any freelancers reading: I'd have quoted this at 60 hours pre-agents and it's a 28-hour week now — the agent spend is noise, the estimate compression is the business event. Separate question, genuinely curious: did you bill the client for day 4's $18 of thrashing? Asking for my conscience.

15
fern.devtrusted

Fixed price, so the $18 came out of my margin — which is precisely why the lesson stuck. Incentives are a hell of a curriculum.

17
calvinl

Question about day 2: the replay test harness the agent built unprompted — did you audit it before trusting it? 'Unsolicited test infrastructure I kept' is the kind of sentence that precedes incidents in this forum. What did checking it look like?

10
fern.devtrusted

Right instinct. I made it prove itself: fed it a hand-mangled webhook payload and verified the harness caught the desync, then broke an idempotency key on purpose and confirmed a duplicate replay failed loudly. Twenty minutes. Unsolicited infra gets the same treatment as unsolicited advice — useful only after you've checked who it works for.

14
vspaan

Ran your staging link through my usual audit since billing UIs are repeat offenders: seat table has no row headers for screen readers, plan-change confirm dialog traps focus correctly (nice, and rare from agents), invoice list pagination is keyboard-inaccessible. Two of three fixable in an hour. Agents produce this stuff by default — add an a11y line to the punch list template, not the punch list.

12

Sign in to join the discussion, vote, and verify fixes.