Skip to content

From Plan to Code

The gap between a brilliant plan and working software is where most projects stumble. Claude Code bridges this chasm with autonomous implementation capabilities that can turn your architectural decisions into production code at terminal velocity. This lesson explores how to leverage these capabilities effectively.

Scenario: You’ve just finished planning a complex feature - a real-time notification system spanning frontend, backend, and mobile. The architecture is solid, the plan is detailed, but now comes the daunting task of implementation across 50+ files. Traditional approach? Days of coding, context switching, and inevitable inconsistencies. With Claude Code? Watch.

Terminal window
# Day 1: Backend setup
- Create notification models
- Write database migrations
- Implement WebSocket handlers
- Add REST endpoints
# Day 2: Frontend integration
- Create notification components
- Set up WebSocket client
- Implement state management
- Add UI interactions
# Day 3: Mobile updates
- Port logic to mobile
- Handle push notifications
- Test across platforms
# Day 4-5: Bug fixes and integration
  1. Configure Yolo Mode

    Terminal window
    # For maximum autonomy
    claude config set autoApprove true
    # Or start session with:
    claude --auto-approve
  2. Set Implementation Preferences Create .claude/settings.json:

    {
    "implementation": {
    "testFirst": true,
    "runTestsAfterChanges": true,
    "commitAfterFeature": true,
    "lintBeforeCommit": true
    },
    "codeStyle": {
    "language": "typescript",
    "framework": "react",
    "testFramework": "jest"
    }
    }
  3. Prepare the Workspace

    > Before we implement, ensure:
    > - All dependencies are installed
    > - Tests are passing
    > - We're on a feature branch
    > - The plan is in docs/notification-plan.md
  4. Create Implementation Commands Save in .claude/commands/implement-feature.md:

    Implement feature: $ARGUMENTS
    1. Read the plan document
    2. Create necessary files and folders
    3. Implement backend first
    4. Add comprehensive tests
    5. Implement frontend
    6. Update documentation
    7. Run all tests
    8. Create descriptive commits

Start with high-level structure, then fill in details:

> Implement the notification service top-down:
> 1. Create the overall file structure
> 2. Define interfaces and types
> 3. Implement core logic
> 4. Add error handling
> 5. Write tests

Claude’s approach:

Phase 1: Structure

Creating notification system structure...
src/
notifications/
├── index.ts
├── types.ts
├── service.ts
├── handlers/
│ ├── websocket.ts
│ └── push.ts
├── storage/
│ ├── redis.ts
│ └── postgres.ts
└── __tests__/

Phase 2: Interfaces

types.ts
export interface Notification {
id: string;
userId: string;
type: NotificationType;
title: string;
body: string;
data?: Record<string, any>;
read: boolean;
createdAt: Date;
}
export interface NotificationService {
send(notification: Notification): Promise<void>;
markAsRead(id: string): Promise<void>;
getUnread(userId: string): Promise<Notification[]>;
}

Phase 3: Implementation

service.ts
export class NotificationServiceImpl implements NotificationService {
constructor(
private storage: NotificationStorage,
private broadcaster: NotificationBroadcaster
) {}
async send(notification: Notification): Promise<void> {
// Store notification
await this.storage.save(notification);
// Broadcast to connected clients
await this.broadcaster.broadcast(notification);
// Queue push notification
await this.queuePushNotification(notification);
}
}

Phase 4: Testing

__tests__/service.test.ts
describe('NotificationService', () => {
it('sends notifications to storage and broadcaster', async () => {
const mockStorage = createMockStorage();
const mockBroadcaster = createMockBroadcaster();
const service = new NotificationServiceImpl(
mockStorage,
mockBroadcaster
);
await service.send(testNotification);
expect(mockStorage.save).toHaveBeenCalledWith(testNotification);
expect(mockBroadcaster.broadcast).toHaveBeenCalledWith(testNotification);
});
});

Implement complete vertical slices:

> Implement the notification system feature by feature:
> 1. User receives notification (full stack)
> 2. User marks as read (full stack)
> 3. Real-time updates (full stack)
> Each feature should work end-to-end before moving to the next

This approach ensures each feature is fully functional before moving on.

For microservices or multiple repositories:

> We need to implement notification support across services:
> - API Gateway: Add notification endpoints
> - User Service: Add notification preferences
> - Notification Service: Core implementation
> - Frontend: UI components
>
> Coordinate the changes to maintain compatibility

Claude handles this by:

  1. Creating Contracts First

    shared/contracts/notification.ts
    export interface NotificationAPI {
    '/notifications': {
    GET: {
    response: Notification[];
    query: { unreadOnly?: boolean };
    };
    POST: {
    body: CreateNotificationDto;
    response: Notification;
    };
    };
    }
  2. Implementing Services Each service implements its part of the contract

  3. Maintaining Compatibility Uses feature flags or versioning during rollout

Example 1: Database Migration with Zero Downtime

Section titled “Example 1: Database Migration with Zero Downtime”

Challenge: Migrate from single PostgreSQL to sharded setup while keeping the service running.

> Implement zero-downtime database migration:
> - Current: Single PostgreSQL with 10M records
> - Target: Sharded across 4 instances by user_id
> - Requirement: No downtime, reversible

Claude’s implementation strategy:

  1. Dual-Write Phase

    class DualWriteRepository implements UserRepository {
    constructor(
    private oldDb: PostgresClient,
    private shardedDb: ShardedPostgresClient
    ) {}
    async save(user: User): Promise<void> {
    // Write to both databases
    await Promise.all([
    this.oldDb.save(user),
    this.shardedDb.save(user)
    ]);
    }
    async findById(id: string): Promise<User> {
    // Read from old DB, fallback to new
    try {
    return await this.oldDb.findById(id);
    } catch (error) {
    return await this.shardedDb.findById(id);
    }
    }
    }
  2. Background Migration

    class MigrationWorker {
    async migrateInBatches() {
    let lastId = '';
    while (true) {
    const batch = await this.oldDb.query(`
    SELECT * FROM users
    WHERE id > $1
    ORDER BY id
    LIMIT 1000
    `, [lastId]);
    if (batch.length === 0) break;
    await this.shardedDb.insertBatch(batch);
    lastId = batch[batch.length - 1].id;
    // Progress tracking
    await this.updateProgress(lastId);
    }
    }
    }
  3. Cutover Logic

    class MigrationController {
    async switchToSharded() {
    // Verify data consistency
    const isConsistent = await this.verifyDataConsistency();
    if (!isConsistent) {
    throw new Error('Data inconsistency detected');
    }
    // Update feature flag
    await this.featureFlags.enable('use_sharded_db');
    // Monitor for issues
    this.startHealthChecks();
    }
    }

Challenge: Modernize a jQuery-based frontend to React while maintaining all functionality.

> Modernize our legacy jQuery dashboard to React:
> - 50+ jQuery files with complex interactions
> - Must work alongside legacy code during migration
> - Preserve all current functionality
> - Add TypeScript and modern tooling

Claude’s incremental approach:

// 1. Create React wrapper for legacy pages
const LegacyWrapper: React.FC = () => {
useEffect(() => {
// Initialize jQuery components
window.initializeLegacyDashboard();
return () => {
// Cleanup jQuery handlers
window.cleanupLegacyDashboard();
};
}, []);
return <div id="legacy-mount-point" />;
};
// 2. Set up hybrid routing
const App = () => {
return (
<Router>
<Route path="/dashboard" component={ModernDashboard} />
<Route path="/legacy/*" component={LegacyWrapper} />
</Router>
);
};

Leverage Claude’s ability to work on multiple aspects simultaneously:

> Use subagents to implement these features in parallel:
> 1. REST API endpoints
> 2. GraphQL schema and resolvers
> 3. Database migrations
> 4. Frontend components
> 5. Mobile app updates
>
> Coordinate the results into a cohesive implementation

Claude doesn’t just write code - it understands patterns:

> Implement CRUD operations for our Product model.
> Follow our existing patterns from User and Order models.
> Include:
> - REST endpoints with validation
> - Service layer with business logic
> - Repository with caching
> - Comprehensive tests
> - API documentation

Claude analyzes existing patterns and generates consistent code:

// Recognizes and follows your patterns
@Controller('/products')
export class ProductController {
constructor(private productService: ProductService) {}
@Get()
@UseGuards(AuthGuard)
@ApiOperation({ summary: 'List all products' })
async findAll(@Query() query: ListProductsDto) {
// Follows same pattern as UserController
return this.productService.findAll(query);
}
@Post()
@UseGuards(AuthGuard, RoleGuard('admin'))
@ApiOperation({ summary: 'Create new product' })
async create(@Body() dto: CreateProductDto) {
// Includes same validation approach
return this.productService.create(dto);
}
}

Implementation isn’t always smooth. Claude handles failures gracefully:

> The implementation failed with a TypeScript error.
> Fix it and continue with the remaining tasks.
Claude: I see the issue. The type definition for NotificationPayload
is missing the 'priority' field. Let me fix this and continue...
[Fixes error]
[Reruns tests]
[Continues implementation]

Break large implementations into reviewable chunks:

> Implement the payment system in stages:
> 1. Core payment processing logic (stop for review)
> 2. Integration with payment providers (stop for review)
> 3. Frontend checkout flow (stop for review)
> 4. Admin dashboard (stop for review)
> For each component:
> 1. Write tests first based on requirements
> 2. Implement minimal code to pass tests
> 3. Refactor for quality
> 4. Add edge case handling
> While implementing, also:
> - Add JSDoc comments to all public methods
> - Update README with setup instructions
> - Create API documentation
> - Add inline comments for complex logic
> After each major component:
> - Run the test suite
> - Check linting rules
> - Verify against requirements
> - Test manually if needed
> Implement a distributed task queue system:
> - Redis for queue storage
> - Multiple worker processes
> - Failure handling and retries
> - Monitoring and metrics
> - Dead letter queue

Claude creates a complete system:

// Queue implementation
export class DistributedQueue<T> {
constructor(
private redis: Redis,
private config: QueueConfig
) {}
async enqueue(task: T): Promise<string> {
const taskId = generateId();
const taskData = {
id: taskId,
payload: task,
attempts: 0,
createdAt: Date.now()
};
await this.redis.lpush(
this.config.queueName,
JSON.stringify(taskData)
);
await this.emitMetric('task.enqueued');
return taskId;
}
async processQueue() {
while (this.running) {
const task = await this.dequeue();
if (!task) {
await sleep(this.config.pollInterval);
continue;
}
try {
await this.processTask(task);
await this.emitMetric('task.completed');
} catch (error) {
await this.handleFailure(task, error);
}
}
}
}
> Implement a high-performance data processing pipeline:
> - Must handle 100k records/second
> - Sub-millisecond latency requirement
> - Minimize memory allocation
> - Use streaming where possible

Claude optimizes for performance:

// Optimized streaming processor
export class StreamProcessor {
private readonly bufferPool: BufferPool;
private readonly workerPool: WorkerPool;
constructor(config: ProcessorConfig) {
// Pre-allocate resources
this.bufferPool = new BufferPool(config.bufferSize);
this.workerPool = new WorkerPool(config.workers);
}
async processStream(input: ReadableStream): Promise<void> {
const pipeline = input
.pipe(this.createParser())
.pipe(this.createTransformer())
.pipe(this.createBatcher(1000))
.pipe(this.createParallelProcessor());
await pipeline.run();
}
private createParallelProcessor() {
return new Transform({
objectMode: true,
transform: async (batch, encoding, callback) => {
// Process batch in parallel
const promises = batch.map(item =>
this.workerPool.process(item)
);
const results = await Promise.all(promises);
callback(null, results);
}
});
}
}

Track these during implementation:

Code Coverage

> After implementation, what's our test coverage?
Claude: Running coverage analysis...
- Statements: 94%
- Branches: 89%
- Functions: 96%
- Lines: 93%

Performance

> Run performance benchmarks on the new code
Claude: Benchmark results:
- API response time: 45ms (p95)
- Memory usage: 128MB steady
- CPU usage: 15% average
- Throughput: 1200 req/s

Code Quality

> Analyze code quality metrics
Claude: Code analysis:
- Cyclomatic complexity: 3.2 avg
- Duplication: 0.8%
- Linting issues: 0
- Type coverage: 98%

Requirements

> Verify all requirements are met
Claude: Requirements checklist:
✅ Real-time notifications
✅ Offline support
✅ Mobile push notifications
✅ Analytics integration
✅ Admin dashboard

You’ve learned how to leverage Claude Code’s autonomous implementation capabilities to turn plans into production-ready code at unprecedented speed. The key is balancing automation with oversight - let Claude handle the mechanical aspects while you focus on architecture, quality, and business logic.

Remember: Implementation is not just about writing code quickly. It’s about writing the right code, well. Claude Code’s ability to understand patterns, maintain consistency, and handle complex multi-file operations makes it an invaluable implementation partner. Use these capabilities wisely, and you’ll deliver features faster without sacrificing quality.