The Vibe-to-Production Pipeline

Module 01: Vibe Coding Fundamentals | Expansion Guide

Back to Module 01

The Problem

You vibe-coded a feature in 30 minutes. It works. Your manager sees it and says "ship it." Two weeks later, it's in production and breaking in ways you never imagined. Users find edge cases. The code that "works" doesn't scale. You're debugging spaghetti at 2am.

The issue: vibes got you to working, but working isn't shipped.

Most developers either harden too early (killing momentum) or never harden at all (shipping prototypes). There's a middle path, but it requires a deliberate pipeline.

The Core Insight

Vibe coding and production coding are different phases, not different approaches.

Think of it like writing: first draft is for getting ideas out (vibes), revision is for clarity (validation), editing is for polish (hardening). You don't edit while drafting - that kills flow. You don't ship first drafts - that kills credibility.

The pipeline has three phases:

Phase Goal AI Mode Output
Explore Make it work Full vibes, accept suggestions freely Working prototype
Validate Prove it's right Challenge AI, verify assumptions Tested, documented code
Harden Make it unbreakable Use AI for edge cases, security review Production-ready code

The Walkthrough

Phase 1: Explore (The Vibe Zone)

This is pure vibe coding. Let the AI drive. Accept suggestions. Don't worry about:

What you're doing: Proving the concept works at all.

Capture during this phase:

Git Strategy for Explore Phase

Work on a feature/explore-* branch. Commit frequently with WIP messages. These commits are for recovery, not history. You'll squash later.

Phase 2: Validate (The Skeptic Zone)

Stop vibing. Start questioning. Every piece of AI-generated code gets challenged:

What you're doing: Finding everything that's wrong before users do.

Actions in this phase:

  1. Write tests for the happy path
  2. Add tests for edge cases you identified in Explore
  3. Ask AI to review for common bugs
  4. Document what the code actually does (not what you hoped)
# Validation prompt template
"Review this code for:
1. Null/undefined handling
2. Array boundary errors
3. Type coercion issues
4. Race conditions (if async)
5. Input validation gaps

Code:
[paste code]"

Phase 3: Harden (The Production Zone)

The code works and is tested. Now make it bulletproof:

What you're doing: Making it survive contact with real users.

The Hardening Trap

Don't over-harden. A CLI tool used by your team doesn't need the same error handling as a public API. Match hardening effort to risk.

Transition Triggers

Explore → Validate when:

Validate → Harden when:

Example: Building a CLI Tool

Let's trace a real example through all three phases.

Explore Phase (~30 minutes)

# Prompt: "Create a CLI that fetches weather for a city"

# AI generates something like:
import requests
import sys

city = sys.argv[1]
response = requests.get(f"https://wttr.in/{city}?format=3")
print(response.text)

It works! But it's fragile. Move to validate.

Validate Phase (~1 hour)

# Questions to ask:
# - What if no city provided?
# - What if API is down?
# - What if city has spaces?
# - What about rate limiting?

# Tests:
def test_no_city_argument():
    # Should exit with helpful message

def test_invalid_city():
    # Should handle gracefully

def test_city_with_spaces():
    # "New York" should work

Harden Phase (~2 hours)

import requests
import sys
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def get_weather(city: str) -> str:
    """Fetch weather for a city from wttr.in."""
    if not city:
        raise ValueError("City name required")

    city_encoded = city.replace(" ", "+")

    try:
        response = requests.get(
            f"https://wttr.in/{city_encoded}?format=3",
            timeout=10
        )
        response.raise_for_status()
        return response.text.strip()
    except requests.Timeout:
        logger.error(f"Timeout fetching weather for {city}")
        raise
    except requests.RequestException as e:
        logger.error(f"Error fetching weather: {e}")
        raise

def main():
    if len(sys.argv) < 2:
        print("Usage: weather ", file=sys.stderr)
        sys.exit(1)

    city = " ".join(sys.argv[1:])
    try:
        weather = get_weather(city)
        print(weather)
    except Exception as e:
        print(f"Error: Could not fetch weather for '{city}'",
              file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()

Failure Patterns

1. Hardening in Explore Phase

Symptom: You spent 4 hours on a feature that should take 1, and you're still not sure it's the right approach.

Fix: Accept messy code until you've proven the concept. You can always delete it.

2. Shipping Explore Code

Symptom: "It works on my machine" becomes a mantra. Production errors spike.

Fix: No code reaches production without passing through Validate. Period.

3. Losing Explore Insights

Symptom: You rewrote something you already solved because you forgot how.

Fix: Capture notes during Explore. Use commit messages and TODO comments liberally.

4. Validate Paralysis

Symptom: You've been writing tests for a week and still haven't shipped.

Fix: Set time limits. If 80% of critical paths are tested, move to Harden.

Quick Reference

Phase Checklist:

Time Ratios (rough guide):

Git Branch Strategy:

feature/explore-weather-cli  # WIP commits, messy
feature/weather-cli          # Clean commits after validate
main                         # Merged after harden