Skip to content

How to Build Apps with Claude Code: A Complete Workflow Guide

The Problem

I asked Claude Code to build a complete feature from scratch. I gave it a detailed PRD with all the requirements. It started coding immediately, created dozens of files, and after two hours of work, the codebase was a mess of half-finished components, broken imports, and functionality that didn’t match the requirements.

This pattern repeated every time I tried to “one-shot” an application:

One-Shot Building Failure Pattern
PRD → Claude generates 50+ files → 30% work → Broken tests
→ Context dilution → Lost requirements → Give up and start over

The Reddit community confirmed I wasn’t alone:

Common Failure Symptoms
- Context dilution: After 20 files, Claude forgets earlier decisions
- No direction: Generates code without architectural consistency
- No feedback loop: No opportunity to course-correct mid-way
- Overconfident output: Produces broken code confidently

The key insight from experienced users: Treat Claude Code like a mid-level engineer who is onboarding. Give incremental tasks. Break PRDs into tiny vertical slices. Iterate through prototype, architecture, implementation, test, and debug cycles.

The Solution: Incremental Development Workflow

The correct approach looks like this:

Incremental Development Flow
PRD → Phase 0: Setup/Specs → Phase 1: Foundation → Phase 2: Slices
→ Phase 3: Iterate → Working application
Each phase builds on the previous. Each step is small enough to verify.

Phase 0: Setup and Specification

Before writing any code, create a CLAUDE.md file with architecture decisions and constraints:

CLAUDE.md Example
# Project Architecture
## Tech Stack
- Backend: Python Flask with SQLAlchemy
- Frontend: HTML + Alpine.js + Tailwind (no CDN, local assets)
- Database: PostgreSQL
## Constraints
- No remote CDN dependencies
- All user input must be validated with Zod schemas
- Maximum file size: 400 lines
## Key Decisions
- Use repository pattern for database access
- API responses follow standard format with success/error fields
- All state changes go through dedicated hooks

This specification serves as a contract. Claude references it throughout development. When it forgets context, the spec keeps it on track.

Phase 1: Foundation Layer

Build the foundation first. No features yet:

Foundation Layer Tasks
1. Project structure (directories, initial files)
2. Database schema and migrations
3. Base configuration files
4. Core utilities and helpers
5. Authentication/authorization framework

Here’s how I prompt this phase:

Foundation Prompt Example
Create the foundation layer for this project:
1. Set up project structure with these directories:
- /backend (Flask app)
- /frontend (static assets)
- /migrations (database migrations)
2. Create base configuration:
- config.py with environment variable loading
- requirements.txt with pinned versions
3. Define database schema for User and Post models
4. Create base repository pattern with CRUD operations
DO NOT implement any features yet. Only the foundation.

I verify each foundation piece before moving forward:

Foundation Verification Checklist
[ ] Project structure matches spec
[ ] Config loads environment variables correctly
[ ] Database models match schema
[ ] Base repository has all CRUD methods
[ ] Tests pass for foundation code

Phase 2: Vertical Slices

Instead of building all features horizontally (all models, then all routes, then all views), build vertical slices. One complete feature at a time:

Vertical Slice Approach
WRONG (Horizontal):
Model A → Model B → Model C → Route A → Route B → Route C → View A → View B → View C
CORRECT (Vertical):
Feature A (Model + Route + View + Test)
→ Feature B (Model + Route + View + Test)
→ Feature C (Model + Route + View + Test)

Each slice is a complete, working piece:

Slice Breakdown Example
Feature: User Registration
Slice 1: Registration endpoint
- Create /register route
- Add input validation
- Test registration success
- Test validation failures
Slice 2: User creation
- Add user repository method
- Hash passwords
- Test user creation
- Test duplicate email handling
Slice 3: Registration UI
- Create registration form
- Add Alpine.js validation
- Connect to endpoint
- Test UI interactions
Each slice ends with working, tested code.

I never let Claude start a new slice until the current one works:

Slice Completion Criteria
- Feature works end-to-end
- Tests pass (unit + integration)
- No console errors
- Code matches CLAUDE.md constraints
- Linter shows no errors

Phase 3: Iteration Cycles

The iteration cycle is where real development happens:

Iteration Cycle
┌─────────────────────────────────────────────────────────────────┐
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Prototype │────▶│ Arch/Design│────▶│ Implement │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ▲ │ │
│ │ ▼ │
│ │ ┌──────────┐ │
│ │ │ Test │ │
│ │ └──────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────┐ │
│ └────────────────────────────│ Debug │ │
│ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Prototype: Quick implementation to validate approach

Prototype Prompt
Create a minimal prototype for [feature].
Focus on the happy path only.
Don't worry about edge cases or error handling yet.
Goal: Prove this approach works.

Architecture/Design: Refine for production

Architecture Prompt
Review the prototype. Refine the architecture:
- Add proper error handling
- Consider edge cases
- Apply design patterns from CLAUDE.md
- Ensure consistency with existing code

Implementation: Build the real version

Implementation Prompt
Implement [feature] following the refined architecture.
Reference CLAUDE.md for constraints.
Write tests as you implement.

Test: Verify everything works

Test Prompt
Run all tests for [feature].
Report any failures.
If failures exist, list them clearly.

Debug: Fix issues and loop back

Debug Prompt
Analyze the test failures:
1. Root cause of each failure
2. Proposed fix for each
3. Impact on other code
Fix issues and run tests again.

This cycle repeats until the feature is complete. Each iteration is small enough to understand and verify.

Why This Matters

The compound effect of good process is dramatic:

Compound Effect Comparison
BAD PROCESS:
Time wasted on broken code: 4 hours
Time fixing context issues: 2 hours
Time debugging architectural mess: 3 hours
Total: 9 hours, may or may not work
GOOD PROCESS:
Phase 0 (Specs): 30 minutes
Phase 1 (Foundation): 1 hour
Phase 2 (Slices): 3 hours
Phase 3 (Iterations): 2 hours
Total: 6.5 hours, working application

Beyond time savings, the result is:

Quality Improvements
+ Consistent architecture across all features
+ Every piece has tests
+ No context dilution (small steps)
+ Clear progress tracking
+ Easy to review (small commits)
+ Easy to rollback (small changes)

Common Mistakes to Avoid

I’ve made every mistake on this list:

Mistake 1: Skipping the specification phase

Wrong vs Right
WRONG:
"Just build me a todo app with users and tasks"
RIGHT:
"First, let me create a CLAUDE.md with architecture decisions.
Then we'll build the foundation layer.
Then we'll implement features one at a time."

Mistake 2: Asking for complete features

Wrong vs Right
WRONG:
"Implement the entire user authentication system with registration,
login, password reset, and email verification"
RIGHT:
"Let's break this into slices:
Slice 1: Registration endpoint only
Slice 2: Login endpoint only
Slice 3: Password reset only
..."

Mistake 3: No verification between steps

Wrong vs Right
WRONG:
Claude: "I've created the user model"
Me: "Great, now add the post model"
[Claude creates post model without testing user model]
RIGHT:
Claude: "I've created the user model"
Me: "Let me verify. [runs tests, checks schema]
Found an issue with the email validation.
Fix this before we move to post model."

Mistake 4: Horizontal instead of vertical development

Wrong vs Right
WRONG:
"Create all the models first: User, Post, Comment, Tag, Category"
[20 minutes later, 5 models, no tests, no working functionality]
RIGHT:
"Create the User model with full CRUD and tests"
[verify it works]
"Now create the Post model with full CRUD and tests"
[verify it works]
"Create the relationship between User and Post"
[verify it works]

Mistake 5: Letting Claude run too long

Wrong vs Right
WRONG:
[One prompt generates 30 files over 45 minutes]
Result: Context dilution, forgotten requirements, broken code
RIGHT:
[Each prompt generates 1-3 files]
[Verify between each prompt]
[Maximum 10-15 minutes per prompt]
Result: Consistent quality, no lost context

Effective Task Breakdown

Here’s a template I use for breaking down features:

Task Breakdown Template
Feature: [Name]
Phase 0 Checklist:
[ ] CLAUDE.md created with architecture decisions
[ ] Dependencies documented
[ ] Database schema defined
Phase 1 Checklist:
[ ] Project structure created
[ ] Base configuration working
[ ] Database models defined
[ ] Core utilities implemented
[ ] Foundation tests pass
Phase 2 Slices:
[ ] Slice 1: [Smallest vertical piece]
- Implementation: ____
- Tests: ____
- Verified: ____
[ ] Slice 2: [Next vertical piece]
- Implementation: ____
- Tests: ____
- Verified: ____
[ ] Slice 3: [Next vertical piece]
- Implementation: ____
- Tests: ____
- Verified: ____
Phase 3 Iterations:
[ ] Prototype done
[ ] Architecture refined
[ ] Implementation complete
[ ] All tests pass
[ ] Code reviewed

Effective Prompting

The way I prompt determines success:

Prompt Structure for Slices
Context: [Brief description of what we're building]
Current state: [What's already done]
This task: [Specific slice to implement]
Constraints: [Reference to CLAUDE.md rules]
Success criteria: [How we verify this works]

Example:

Concrete Prompt Example
Context: Building a blog management app where users can
create, edit, and publish posts.
Current state:
- User authentication complete (login, logout, sessions)
- Post model exists with title, content, status fields
- No routes or UI yet
This task:
Implement the "create post" slice:
1. /posts/new route (GET) - shows form
2. /posts route (POST) - creates post
3. Form with title and content fields
4. Validation: title required, max 200 chars
5. Redirect to /posts/{id} on success
6. Show errors on validation failure
Constraints from CLAUDE.md:
- Use repository pattern for database access
- Validate all inputs with Zod schemas
- Maximum file size 400 lines
- No console.log statements
Success criteria:
- Form renders at /posts/new
- Creating a post saves to database
- Validation errors display correctly
- Redirect works after creation
- All tests pass

This level of specificity eliminates ambiguity. Claude knows exactly what to build, what constraints to follow, and how success is measured.

Summary

In this post, I explained why one-shot building with Claude Code fails and how to fix it. The solution is treating Claude Code like a mid-level engineer who is onboarding:

  1. Phase 0: Create CLAUDE.md with architecture decisions and constraints
  2. Phase 1: Build foundation layer (structure, config, models, utilities)
  3. Phase 2: Implement vertical slices (one complete feature at a time)
  4. Phase 3: Iterate through prototype → architecture → implementation → test → debug cycles

The key is small, verifiable steps. Each step completes before the next begins. Each slice works end-to-end. The spec keeps Claude consistent when context fades.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments