From Incomplete to Production-Ready: Using Claude Code for Systematic Gap Discovery

A customer came to us with a 175,000-line codebase that wasn't working. It had been built through vibe coding—business stakeholders directing AI code generation without engineering oversight. They were convinced the pieces worked individually and just needed integration help. The reality was worse. Here's how we used Claude Code to systematically find every gap and get the project to production.

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:

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.

Apple-style keyboard with four keys: Allow once, Allow always, Reject, and claude --dangerously-skip-permissions

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.

← Back to All Blog Posts