Between 6:30pm and 1am one night I iterated on a checkout redesign across 23 commits. The new presentation hid behind a ?prototype=1 query-string path while the default page continued to serve the old flow. That separation was useful for controlled internal review, but it did not by itself establish that the prototype was safe for public exposure or shared backend use.
The useful part was not that a query-string flag is universally safe or sufficient. In this old Classic ASP application, it provided a low-friction internal presentation seam. It still needed to sit behind appropriate access controls, with a defined removal plan, rather than become an accidental public feature switch.
The setup
The work was a from-scratch redesign of how a tournament organizer pays us. The old checkout conflated two different costs onto one screen: the recurring monthly platform membership, and the one-time cost of running a single event. I wanted a new two-layer flow that separated them, with a v2 event cart sitting alongside the legacy membership purchase. Big change. Done the obvious way, it means a long-lived branch that drifts for a week and lands as one terrifying 3,000-line diff nobody can review.
I didn’t want that. I wanted to build it on the real routes, see it render with real data, and keep the live page untouched the entire time.
So the first commit of the night was the “prototype V2 checkout bar” commit, and it did exactly one thing: it gated the new bar behind ?prototype=1. Visit the page normally and you got the production flow. Append ?prototype=1 and you got the new one. From that point forward, the default path remained the old checkout while the prototype view was available for controlled review.
Review happened on the preview branch with the flag flipped on. The branch names tell the story of how this evolved: feature branch, then feature-prototype, then feature-prototype-v2. The flag let the prototype and legacy presentation paths coexist in the same files and deployment pipeline. It reduced presentation risk, but it did not remove the need to evaluate shared state and backend effects.
The cadence: one visible change per commit
Here is what the commit log reads like once the flag is in place. Each of these is a separate commit, most a few minutes apart, none more than about half an hour:
- MOST POPULAR ribbon
- card hover lift
- graceful image fallback (show the sport label instead of a broken icon)
- readable badge font, fix the monthly/annually toggle
- softer unpaid badges, empty state
That’s not a changelog I wrote afterward. That’s the literal git history. Each commit is one decision you can see on screen. If the card hover felt wrong, I could revert exactly that and nothing else. If the image fallback was the thing that broke, git bisect would land on it in one step, not somewhere inside a 40-file mega-commit.
This is design-by-iteration, except the iterations are captured in version control instead of evaporating. When you tune a UI by feel, you normally throw away the path you took and keep only the final state. Committing each visible improvement turns that throwaway exploration into a legible trail of micro-decisions. Three weeks later when someone asks “why is the toggle laid out like that,” the answer is a commit, not a shrug.
The flag made the presentation cadence easier to inspect. Small, focused commits also made individual changes easier to review and revert. A visual change still needed normal code review and validation; “looks right” was not a release criterion by itself.
The hard part wasn’t the pixels
The ribbon and the hover lift are the fun part and the easy part. The actual difficulty of this night was a commit called, in my own words, “hybrid checkout: legacy membership path + v2 event cart, real proration math, combined grand total.”
The new event cart could not replace the old membership purchase. It had to coexist with it. A customer might be buying a monthly membership and paying for one event in the same transaction, and the grand total had to be correct across both, with real proration math, not a placeholder. That math fed the same shared total the legacy path computed, and whatever the new cart produced still had to hand off to the existing checkout endpoint, because that’s where payment actually happens and I was not about to reimplement the payment leg.
This is the part the flag pattern doesn’t make easy. A query flag gives you a clean way to show two flows. It does nothing to help two flows share state correctly underneath. The new cart and the legacy path both feed one order, one total, one POST. That coexistence, getting the combined grand total right while the old path still works untouched, was where the real hours went. Everything visual was minutes. The hybrid total was the wall.
A presentation flag does not protect a shared backend contract. When a prototype reaches the same endpoint as the default path, that seam is the risk: define the expected writes and totals, test the relevant account states, verify the target behavior, and review the payment or billing boundary appropriately.
Ending the burst by writing the ADR
A prototype behind a flag has a known failure mode: it never graduates or gets removed. A temporary path can become load-bearing without a decision record, ownership, and a defined exit condition.
The burst did not end on a pixel. It ended with the product rules and a decision record: account-state handling, capacity warnings, downgrade behavior, and the architecture rationale. The record captured the context, the decision, the alternatives considered, the rollout assumptions, and the conditions that would require the temporary path or the model to be revisited.
Writing a decision record while the reasoning is fresh turns exploratory work into a reviewable choice. It should also name the owner, exit conditions, and removal plan for the prototype path, so a flag does not survive merely because no one remembers its original purpose.
Related
- The Bundle-Ceiling Model: Separating Subscription and Per-Event Cost: the pricing-architecture decision this prototype helped explore
- The Pricing Rewrite Only Got Real When I Wrote the Gates: the companion account of how prototype choices met real checkout, account-state, and entitlement constraints