What We Walked Into
The customer had built a full-stack solution: Raspberry Pi embedded systems, a cloud backend, mobile web interface, and Terraform orchestration infrastructure. Business stakeholders had used AI tools to generate the codebase, and the results looked impressive on paper:
- 175,000 lines of C for embedded systems
- 50,000+ lines of TypeScript for backend services
- 40,000+ lines of Terraform for infrastructure
- 2,000+ pages of requirements, specifications, and generated documentation
The documentation claimed "100% implemented" with "minimal gaps." The customer believed every component worked and they just needed help wiring them together.
Then we ran code coverage. Of those 175,000 lines of C, only 45,000 were actual executable code. The rest was beautifully written comments explaining what the code was supposed to do, not what it actually did. Functions were stubbed out. Integration points were placeholders. The vibe-coded codebase passed a casual reviewâit looked like a real systemâbut it wasn't one.
And the documentation was no better. Those 2,000+ pages of requirements had also been generated through vibe coding. Multiple versions of the same spec, no clear indication of which was current, contradictions between documents. We couldn't just open a requirements doc and trust it. Figuring out what the system was actually supposed to do was its own project before we could even start testing whether it did it.
This is the full vibe coding pattern: not just the code, but the documentation around it. Everything looks thorough and professional. Nothing is reliable.
Calibrating Claude Code to the System
We didn't start by trying to fix everything. We picked one feature and wrote a handful of integration testsânot to find gaps, but to learn how Claude Code behaves in this specific codebase.
Every project is different. The architecture, the conventions, the dependenciesâClaude Code needs to be directed differently depending on what it's working with. Those first tests taught us what level of detail the prompts needed, how Claude Code handled cross-component dependencies, and where it made assumptions we had to correct.
The Key Prompt Shift
Early on, Claude Code would write tests that validated the code as written. If a function was a stub, it would test the stub's behavior. That's not a flawâit's working exactly as directed.
The fix was to shift from "write tests for this code" to "write tests that validate this requirement." That reframing changed everything. Claude Code stopped testing what the code does and started testing what it should do. Suddenly, stubs failed.
Of course, "test against the requirement" assumes you have clear requirements. We didn'tânot really. The vibe-coded documentation was a maze of overlapping, sometimes contradictory specs. Before we could test against a requirement, we had to establish what the actual requirement was. That meant reconciling multiple document versions, cross-referencing with the customer, and sometimes just making a judgment call about intent. It added real time to every phase.
After a few cycles of write-test, review-result, adjust-prompt, we had a reliable pattern. We knew what to ask for. We knew what Claude Code would deliver. That's when we could scale.
The Four Phases
Phase 1: Orthogonal Integration Tests
We generated tests from the requirementsânot from the code. But given the state of the documentation, this wasn't as simple as opening a spec and writing tests. We had to sift through overlapping documents, identify which version reflected the actual intent, and distill that into something testable. Once we had a clean requirement, the test was straightforward: "Does the system do this?" When a test failed, we knew exactly which requirement wasn't implemented.
Phase 2: Targeted Gap Fixing
For each failing test, one precise instruction to Claude Code: "This test validates [requirement]. Generate the code to make it pass." No refactoring, no architecture changes, no optimization. Just: fill this specific gap. Narrow scope meant Claude Code had everything it needed in a single prompt. The fix rate was high because the instructions were unambiguous.
Phase 3: Business Workflow Tests
Once the individual requirements passed, we layered on end-to-end scenarios. User logs in, performs an action, system records it, generates a report. These caught integration failures between components that passed their individual tests. By this point, Claude Code was working with a codebase that had real, verified foundationsânot stubs.
Phase 4: Code Coverage Analysis
Only now did code coverage become meaningful. We weren't using it to find fundamental gapsâthose were already caught by Phases 1-3. Coverage analysis found the remaining edge cases and untested code paths. The difference between running coverage on a vibe-coded codebase versus a tested one is the difference between drowning in noise and finding actual signal.
Scaling with claude --worktree
After 10-15 successful prompt-test-fix cycles, we had high confidence in how Claude Code would interpret our instructions. The prompts were validated. That unlocked parallel processing.
Using claude --worktree, we ran Claude Code on multiple gaps simultaneously. Under the hood, it manages git worktree for youâcreating isolated copies of the repo so each task runs independently without stepping on the others. Separate worktrees for separate features, each with its own validated prompt pattern. Instead of fixing gaps sequentially, we ran gap A, B, and C in parallel, merged each back after QA passed.
If you know, you know.
On Skipping Permissions
This is also where --dangerously-skip-permissions becomes viableâbut only because worktree isolation, automated CI/CD, and validated prompts replace manual approval as the safety mechanism. Without that foundation, don't do it.
What Actually Happened
The vibe-coded codebase that "just needed integration help" required substantial implementation work across every layer. The embedded C had critical functions stubbed out. The TypeScript backend had endpoints that returned hardcoded responses. The Terraform had resources defined but not properly connected.
The phased approach meant we found these gaps systematically instead of stumbling into them during integration. Each phase built on verified work from the previous one. By Phase 4, we were polishing edge casesânot discovering that core functionality didn't exist.
The project is now entering full integration testing and focus groups are being scheduled. That's a long way from "the pieces don't connect."
The Takeaway
The methodologyâstart small, calibrate your prompts, scale in phasesâworks regardless of the stack. What made this project unusual was the starting point: a codebase that looked complete but wasn't. Vibe coding created the illusion of progress. Systematic testing with Claude Code cut through it.
The teams that succeed with Claude Code at scale aren't the ones with the most automation. They're the ones patient enough to calibrate first, and precise enough to ask clear questions.