A design review of the V3 pricing page landed mid-morning on April 13. The card structure was too dense, checkmarks were missing, the typography did not match the rest of the product, and checkout-bar behavior on mobile was undefined. The list was longer than a cosmetic pass could fix.

We had a choice. We could keep iterating on the existing V3 branch, which already had a week of work in it, or look at what that branch was actually doing to the codebase. So before touching anything else, we read through the branch.

What a week of small iterations had built

The pricing prototype had grown inside shared code paths. Account-specific presentation behavior had piled up inside conditionals that also handled an existing purchase flow. Nobody decided that. It was the cumulative result of small iterations with no clear boundary and no dependency map.

The shared purchase path was live and consequential. A presentation change could reach past layout if it altered shared state, totals, eligibility, or navigation. The prototype was not automatically unsafe, but every additional iteration increased how much we needed to understand about the entangled callers and contracts.

We saved the branch. Then we closed it and started over.

One boundary between the prototype and the live path

The restart began by separating the prototype’s presentation methods from the existing path and writing down how eligibility, authorization, and shared data would be handled. An account-type signal could choose a presentation route, but it did not count as authorization. Protected actions still needed their own server-side checks.

That boundary made the later UI iterations easier to review. Card padding, checkmark rendering, typography alignment, mobile overflow, checkout-bar structure, and CTA states could all be judged against a smaller, documented surface, and the chance of incidental changes to the legacy path dropped. It did not remove the need for tests, authorization review, and release verification.

What the release still required

The release work showed how much the phrase “UI iteration” can hide. Asset compilation, legacy headings, navigation behavior, and responsive layout each needed their own checks. Some of those tasks came from how long the prototype had sat next to shared paths. Others were ordinary release work that a more complete review brought to the surface.

AI assistance sped up the iteration. It could not know which legacy state was load-bearing unless that context and the relevant checks were part of the task. Spotting those limits stayed with engineering review.

The sequence worth stealing

We have seen a version of this more than once. A prototype accumulates, and redoing it starts to feel expensive. Then outside feedback makes the scoping problem visible for the first time, whether that is a code review, a failed test, or a list from a designer. The temptation is to patch around it.

Sometimes the right move is to keep the branch as a reference, restart from a known-good point, and define the scope before adding more UI work. That call should come from comparing migration cost, shared dependencies, and rollback options, and a reflex to restart is no substitute.

AI makes UI deltas cheap to generate, and cheap deltas do nothing for mixed-scope code.

When a prototype shares code with a live purchase flow, ask those four questions before adding more work. If the answers are unclear, weigh the cost of restarting against the shared dependencies and rollback options first.