The Architect-Builder Pattern: A Production Workflow for AI Development

· 10 min read
The Architect-Builder Pattern: A Production Workflow for AI Development

As a product manager who ships code to production for internal tools, I’ve experienced both the magic and frustration of Claude Code. I’m building an AI platform that processes 5,000+ customer signals monthly from 14 sources to generate weekly CockroachDB product health reports in 20 seconds.

It built complex implementations fast: a 500-line Python scraper in 5 minutes. Yet it misunderstood simple questions, treating them as implementation requests. “How does this work?” would trigger “Let me change that for you!” It made assumptions, jumped ahead, and sometimes produced code that broke in CI, or worse, production.

Finding the Pattern

I tried to be more descriptive, or break things into pieces, but Claude works best when it has the entire context for the problem it’s solving. I’d hit window limits and force context compaction slowing development.

Then I discovered a two phase approach that worked leveraging Plan Mode. Plan Mode (Shift + Tab twice) allowed me to iterate and design in Claude with code base context without accidentally implementing a solution as it only has read access. However, Plan Mode, like Claude Code, uses Sonnet 4.5 by default and I wanted something more powerful. Next, I adjusted the /model in Plan Mode to Opus for design to engage with deep reasoning that is validated against the codebase to catch integration issues. Once I’m satisfied with the design, I exit Plan Mode, and switch to a fresh Claude Code session for clean execution control. I also adjust the /model back to Sonnet and implement the design with Sonnet for cost control.

The results were immediate: what took 5+ iterations now worked on the first try. Deployment bugs dropped by 80%. Implementation time fell by 62%.

I wasn’t alone in this discovery. In my product leadership Slack (Supra), other PMs were independently finding the same pattern. One described moving from “fast and ugly” to “well-architected” using a similar phased approach.

The Architect-Builder Pattern is emerging across our community as a repeatable workflow that actually works. The pattern is model provider agnostic and AI-coding agent agnostic.

The Architect-Builder Pattern

The pattern separates AI development into two distinct phases, each with a specific model and purpose:

Phase 1: The Architect uses Claude Opus for high-level design and architectural decisions. This phase focuses on the “why” and “what” of your solution, producing a detailed architecture blueprint with code context. This phase catches integration issues before any code is written—finding naming conflicts, identifying existing patterns to follow, and spotting architectural mismatches. It transforms the abstract design into a concrete implementation plan. Opus excels here because it handles ambiguity and complex reasoning better than any other model.

💡 Phase 1 Tips:

Iterate first: “Help me improve this prompt, but don’t execute it yet”

Design partner mode: “Ask clarifying questions before we begin”

Pattern library: “What are the best practices for [X]? Include real-world examples”

Phase 2: The Builder starts fresh in a new Claude Code session with only the validated implementation plan. No context pollution, no conflicting instructions—just clean execution. The fresh session ensures Claude Code follows the plan without getting confused by earlier exploratory conversations.

The magic is in the handoffs. Each phase produces exactly what the next phase needs. No ambiguity, no assumptions, no lost context.

📋 Full templates and detailed prompts: GitHub Repository

Real-World Example: Adding Consumption into Product Health Analysis

My AI-powered customer intelligence platform automatically collects ~5,000 monthly signals from 14+ sources (GitHub, Slack, Salesforce, Zendesk, etc.) and generates weekly product health reports in 20 seconds using Claude Opus and RAG. It helps product teams identify at-risk ARR, track customer feedback patterns, and make data-driven decisions across, reducing report creation time from days to minutes. I’ll share more about this app in the future (and open-source a version of it).

I added product consumption signals using this pattern with simplified prompts below:

Phase 1: Architectural Design (Opus)

I'm building a customer intelligence platform that collects signals from multiple sources (GitHub, Slack, Salesforce) to generate product health reports. I need to add consumption/usage signals as a new data source.

Current architecture:

- Signal collectors run on schedule, gathering data from APIs
- Data is normalized and stored for analysis
- RAG pipeline processes signals for insights

I want to track customer resource usage patterns and credit consumption trends.

Design the architecture for this consumption signal collector:

1. Data collection strategy: How should I structure collection to match existing patterns?
2. Schema design: What metrics and dimensions should I capture for both real-time monitoring and historical analysis?
3. Integration: How should this integrate with my existing signal pipeline?
4. Performance: Key decisions for caching, batching, and rate limiting at scale?
5. Flexibility: How to adapt to different cloud providers?

Provide a high-level design with clear separation of concerns and extensibility.

The design took 30 minutes and three iterations. Manual research would have taken multiple days.

Because Plan Mode with Opus has access to the code base, it caught 10+ major issues that designing outside of the code base would have missed. I’ll highlight three:

1. Framework Integration Gap

The design proposed creating a new abstract base class hierarchy:

# ❌ WRONG - Design proposed:
class ConsumptionCollector(ABC):
    @abstractmethod
    async def collect_metrics(self) -> List[ConsumptionSignal]:
        pass

But the codebase already had an established pattern:

# ✅ CORRECT - Existing pattern:
class BaseCollector(ABC):
    @abstractmethod
    async def collect(self) -> List[Signal]:
        pass

    def _build_signal(self, signal_data: dict) -> Signal:
        """Standardized signal building method"""
        pass

2. Database Schema Conflicts

Design proposed new tables:

-- ❌ WRONG - Would create parallel storage:
CREATE TABLE consumption_signals (
    id UUID PRIMARY KEY,
    customer_id TEXT,
    provider TEXT,
    metric_type TEXT,
    value DECIMAL
);

CREATE TABLE consumption_aggregates (
    period TEXT,
    aggregated_value DECIMAL
);

But existing schema already supported this:

-- ✅ CORRECT - Existing unified schema:
CREATE TABLE signals (
    id UUID PRIMARY KEY,
    customer_id TEXT,
    source TEXT,  -- Already handles provider tracking
    signal_type TEXT,  -- Can be 'consumption'
    severity TEXT,
    metadata JSONB,  -- Flexible storage for consumption data
    created_at TIMESTAMP,
    occurred_at TIMESTAMP
)

3. Testing Strategy Absent

Design had no testing specifications, but codebase had patterns:

# ✅ CORRECT - Required test structure:
class TestSnowflakeConsumptionCollector:
    @pytest.fixture
    def mock_snowflake_connection():
        with patch('snowflake.connector.connect') as mock:
            mock_cursor = Mock()
            mock_cursor.fetchall.return_value = [
                {
                    'START_TIME': '2024-01-15T10:00:00Z',
                    'WAREHOUSE_NAME': 'COMPUTE_WH',
                    'CREDITS_USED': 23.5
                }
            ]
            yield mock

    async def test_customer_mapping_not_found(self, mock_db):
        """Test fallback when no mapping exists"""
        collector = SnowflakeConsumptionCollector()
        # Should use account_id as customer_id with warning

    async def test_retry_on_transient_failure(self):
        """Test exponential backoff retry logic"""
        with patch('snowflake.connector.connect') as mock:
            mock.side_effect = [TimeoutError, TimeoutError, Mock()]
            # Should succeed on third attempt

Phase 2: Implementation (Fresh Claude Code Session)

Phase 1 revealed the gaps. Now we convert those findings into explicit constraints.

The Phase 2 prompt directly addresses what validation caught:

  • “Extend existing BaseCollector” prevents framework integration gaps
  • “Store in unified signals table” prevents schema conflicts
  • “Testing Requirements” section addresses missing test strategy

The pattern’s value: converting validation findings into implementation constraints. We open a fresh Claude Code session with /model set to Sonnet 4.5 to focus on execution, not analysis.

Implementation Prompt: Data Collection Feature

Task: Implement Data Collection Feature

Objective: Add a new collector that gathers consumption metrics and integrates them into the existing platform as signals, following all established patterns.

Critical Context - Must Follow Existing Architecture

1. Extend Existing Base Classes

# CORRECT: Use existing pattern
class ConsumptionCollector(BaseCollector):
    async def collect(self) -> List[Signal]:
        # Implementation

2. Use Unified Data Model

# Store in existing signals table, NOT new tables
Signal(
    id=str(uuid.uuid4()),
    customer_id="CUST-001",  # from mapping
    source="consumption_collector",
    signal_type="consumption",
    severity=Severity.INFO,
    metadata={
        "provider": "cloud_provider",
        "metric_type": "compute_credits",
        "value": 123.45,
        "dimensions": {...}
    }
)

Implementation Requirements

Data Collection

  • Query last 7 days on first run
  • Incremental: last 48 hours (accounts for data latency)
  • Handle 3-hour reporting lag in source system
  • Aggregate by hour

Testing Requirements

class TestConsumptionCollector:
    async def test_collect_returns_signals(self):
        """Test successful collection"""

    async def test_customer_mapping(self):
        """Test account to customer mapping"""

    async def test_handles_missing_mapping(self):
        """Test fallback behavior"""

    async def test_retry_on_transient_failure(self):
        """Test retry logic"""

File Structure

collectors/
├── consumption_collector.py      # Main implementation
tests/collectors/
├── test_consumption_collector.py
├── fixtures/
   └── sample_data.json
config/
├── collectors.py                 # Add config class

Success Criteria

✅ Extends existing BaseCollector pattern

✅ Stores in unified signals table

✅ Maps provider accounts to customer IDs

✅ All tests pass with >80% coverage

Out of Scope - Do NOT Implement

❌ New database tables for this feature

❌ Real-time streaming

❌ Anomaly detection (Phase 2)

❌ Custom caching layer

❌ New configuration formats

Key Design Decisions

  • Why extend BaseCollector: Maintains architectural consistency
  • Why unified storage: Prevents schema fragmentation
  • Why 48-hour lookback: Accounts for source system latency

Implementation took less than 1 hour. No rollbacks. No hotfixes. First deployment worked.

Advanced Patterns

Progressive Enhancement

Start minimal, layer complexity:

Iteration 1: Core functionality only
Iteration 2: Add error handling and edge cases
Iteration 3: Optimize performance and add monitoring

This prevents over-engineering while ensuring robustness.

Documentation-Driven Development

Maintain two documents per feature:

  • feature.md: Clear specifications that outlive implementation
  • feature_TASKS.md: Task breakdowns for parallelization

This creates automatic documentation for future development. Phase 1 designs become stakeholder-ready documentation. Single-focus phases reduce cognitive load. The workflow creates documentation for easy knowledge transfer.

Context Preservation

Create a PROJECT_CONTEXT.md file that travels between phases:

# Project Context

## Phase 1 Decisions & Discoveries

- [Architectural choices made]
- [Key constraints identified]

## Phase 2 Implementation Notes

- [Deviations from plan]
- [Lessons learned]

Measuring Success

Since adopting this pattern, my metrics transformed:

MetricBeforeAfterImprovement
First-time success rate45%82%+82%
Average implementation time8 hours3 hours-62%
Post-deployment bugs3-5 per feature0-1 per feature-80%
LLM API Cost per feature~$25.00~$9.00-64%

Connection to Multi-Model Orchestration

This pattern is part of a broader shift in how we use AI for development. As I wrote in How I Built My Blog, strategic model selection matters more than any single technical choice.

Different models excel at different tasks:

  • Opus: Complex reasoning, architectural decisions, ambiguous requirements
  • Sonnet: Rapid implementation, codebase analysis, pattern matching
  • Haiku: Validation, testing, simple refactors

The Architect-Builder Pattern formalizes this multi-model approach into a repeatable workflow.

What Patterns Are You Seeing?

The best patterns emerge from practice, not theory.

What patterns have you discovered?

P.S. If you want another example of this pattern in action, check out how I used it to add copy-to-clipboard functionality to code blocks on this website.