Overview
Vibe coding is the practice of building software through conversation with an AI code generator: you describe what you want in natural language, the model generates code, you observe the result, and you steer the model toward correctness through further prompts, test results, and error messages. This chapter treats vibe coding not as a casual shortcut but as a disciplined engineering methodology with a formal loop: specify, generate, observe, steer/verify, commit or repair.
The central insight is that a natural-language prompt is a partial specification, and the gap between what you said and what you meant is the primary source of bugs in AI-generated code. We close that gap with executable contracts (test suites, type annotations, property-based checks) that make implicit requirements explicit. The result is a workflow where the human supplies intent and judgment while the AI supplies implementation velocity.
This chapter builds directly on the AI-assisted engineering foundations of Chapter 8 and sets the stage for Chapter 10: Prompting to Programming, which extends these ideas to more complex prompt engineering strategies. The verification techniques here resurface in Chapter 18: AI-Assisted Testing and QA, and the specification patterns feed into Chapter 13: Discovery of Requirements.
Prerequisites
Readers should have completed Chapter 7: Software Development as a Discovery Process and Chapter 8: Foundations of AI-Assisted Software Engineering. Familiarity with Python, pytest, and basic command-line usage is assumed. The search framework from Chapter 1 provides useful context for understanding the specification gap as a search problem.
Learning Outcomes
- Formalize the vibe coding loop as an iterative refinement process with measurable convergence.
- Characterize natural-language prompts as partial specifications and quantify the specification gap.
- Write failing tests before generating code, using the test suite as an executable contract.
- Apply property-based testing (Hypothesis) and schema validation (Pydantic) to catch classes of bugs that example-based tests miss.
- Use structured verification workflows combining static analysis, runtime checks, and end-to-end tests.
- Build a complete web application through conversation, from failing tests to deployment-ready code.
Sections
9.1 Natural Language as Specification
Prompts as partial specifications. The specification gap. Ambiguity, underspecification, and implicit assumptions. Measuring specification completeness. Executable contracts as gap-closers.
9.2 The Vibe Coding Loop
The five-phase loop: specify, generate, observe, steer, commit. Convergence dynamics. Session management. When to reset versus repair. Prompt refinement strategies.
9.3 Verification and Repair
Test suites as executable contracts. Property-based testing with Hypothesis. Schema validation with Pydantic. Static analysis integration. The repair loop: feeding errors back to the model.
9.4 Building Through Conversation
Recipe: building a scientific data explorer web application from scratch through iterative conversation. Failing tests first, then generation, then verification, then deployment.
Bibliography
Foundational References
The origin of the term "vibe coding," describing a workflow where programmers describe intent in natural language and let AI generate the implementation.
Demonstrates that LLMs can decompose complex coding tasks into plans, then generate code step-by-step, improving correctness on benchmarks.
The Codex paper introducing HumanEval, establishing pass@k as the standard metric for code generation quality.
Verification and Testing
The leading property-based testing library for Python. Generates test inputs automatically to find edge cases that hand-written tests miss.
Runtime data validation through type annotations, providing executable schema contracts for function inputs and outputs.
Cross-browser end-to-end testing framework for web applications, used in Section 9.4 for verifying the Discovery Workbench UI.
Tools and Platforms
The primary vibe coding tool used throughout this chapter, operating as an agentic command-line assistant that reads, writes, and tests code.
The standard Python testing framework used throughout this book for executable contracts and verification.
Static type checker that catches type errors before runtime, complementing the dynamic verification approach of testing.
Research
Empirical study of LLM self-repair capabilities, showing that feeding error messages back to the model improves pass rates but with diminishing returns.
Introduces flow engineering for code generation, demonstrating that structured generate-test-repair loops outperform single-shot generation.
Shows how step-by-step runtime verification can guide LLM debugging, achieving significant improvements on code generation benchmarks.