Phase-Based Development at Scale
On Vandoko, I completed 519 commits across 26 development phases in about six weeks. That number sounds like chaos. It was not. The phase structure is why it was not.
What a Phase Is
Each phase had the same five steps: docs, research, plan, implement, UAT.
Docs meant writing down what the phase was supposed to accomplish before touching any code. Not a specification document, just a short clear statement of scope. Research meant reading the relevant library docs, looking at existing patterns in the codebase, and identifying likely friction points. Plan meant sequencing the implementation work before starting it. Implement meant building. UAT meant running acceptance tests before calling the phase complete.
The phases were small enough to complete in a day or two. Phase 1 was infrastructure. Phase 2 was Census API integration. Phase 3 was Google Places. Each phase built on the last and had a defined boundary where it ended.
Why It Worked
The docs-first step is the part people skip. It felt slow at the start. By phase 10 I could see why it mattered.
When you write down what a phase does before you start it, you are forced to decide what is in scope. Features that belong in later phases stay out. Scope creep that would have made the phase unpredictable gets deferred instead of absorbed. The phase stays small enough to UAT cleanly.
The UAT step at the end of each phase meant I knew the state of the system at every boundary. When phase 14 introduced a bug, I knew it was phase 14. I did not have to bisect across three weeks of undifferentiated commits to find it.
By v1.2, the full 7/7 UAT suite passed. The type alignment across all 26 phases was clean. That result came from the discipline of closing each phase before opening the next.
The Friction Points
32% of the 519 commits were fix commits. That is roughly 167 fixes across six weeks. Some phases had more friction than others.
Phase 17 through 26 covered Docker deployment, type alignment, and the TweakCN theme integration. Docker was the hardest. On March 1, the deployment broke in six distinct ways in a single day: Prisma client not copying from the build stage to the runner, pnpm hoisted node_modules not resolving in the container, missing .npmrc configuration. Six commits, six fixes, one day.
The auth race condition in mid-February was subtler. Clerk and React Query had a timing conflict on page load. The gate pattern I built to solve it became a documented skill I carry forward.
The MarketCheck API debugging in mid-February cost four commits to resolve a nested response structure with incomplete listings. Not complicated once I understood it. The documentation for the API did not match the actual response shape.
What I Would Change
The mega-commit on February 28 was a mistake. I had accumulated changes across multiple phases and committed them together. That created a commit that touched everything and documented nothing. Future me reading that commit would have no idea what changed or why.
The right move is to commit at each phase boundary, not to let changes accumulate and batch them later. The phase structure exists to prevent exactly that kind of accumulated state.
Carrying It Forward
The phase-based approach is now how I start any project with more than a few weeks of scope. The upfront investment in docs and planning pays back in UAT confidence and reduced debugging time. It is not a methodology I read about. It is something I built because the alternative, building without it, was slower.
If you are working on something with more than 10 distinct features, break it into phases before you start. Define each phase boundary before crossing it. UAT before moving on. The extra hour per phase saves days of confusion later.