SecondBrain/2026-03-13-99-bottles-oop-f...

5.1 KiB

source date tags
hyperthrive_dev 2026-03-13
research
oo-principles
99-bottles
tdd
software-design
process
shameless-green
open-closed
refactoring

99 Bottles OOP — Full Software Design Process Map

A complete map of the software design lifecycle described in 99 Bottles of OOP by Sandi Metz and Katrina Owen. Documented during a session exploring how to better encode OO principles into AI coding conventions.

Overview

The process is an infinite loop with four distinct phases: initial development, a waiting period, a refactoring loop triggered by new requirements, and implementation. The key insight is that refactoring and feature addition are deliberately separated.

Phase 1: Initial Development (Shameless Green)

Goal: get to working, understandable, thoroughly tested code as fast as possible. Do NOT invent requirements or speculate about the future.

  1. Sketch a high-level public API for the problem
  2. Write the first test targeting the simplest, most thoroughly understood piece of that API
  3. Red → Green (write only enough simple code to pass)
  4. Write the next test proving existing code is incomplete
  5. Write the simplest, most concrete code to pass — tolerate duplication
  6. Repeat horizontally until all variations are handled
  7. Stop. You have reached Shameless Green: easy to understand, fully tested, fulfills current requirements

Shameless Green optimizes for understandability, not changeability. If the code never changes, you stop here.

Phase 2: The Waiting Period

Do nothing proactively. Wait for a new requirement. Two voluntary exceptions:

  • Exception A (Purify Tests): Clarify test names, remove echoes of implementation details, ensure every class has its own unit test
  • Exception B (Aesthetic Improvements): Fix Law of Demeter violations, remove hard-coded class names, push object creation to the edges

Do NOT build abstractions speculatively. Save time and money.

Phase 3: The Refactoring Loop (Open/Closed Principle)

Triggered when a new requirement arrives. Separate refactoring from feature addition.

Decision gate:

  1. Is the code "open" to the new requirement? (Can you implement it by adding code, not modifying existing code?)
    • YES → go to Phase 4
    • NO → continue
  2. Do you know how to make it open?
    • YES → refactor → return to step 1
    • NO → continue
  3. Identify the best-understood code smell and remove it using a mechanical recipe:
    • Need to unearth an abstraction → Flocking Rules
    • Conditionals supplying behavior → Replace Conditional with Polymorphism
    • Need to choose which polymorphic class to use → Factory
    • Objects tightly coupled → Dependency Inversion (push object creation to edges, inject dependencies)
  4. Enforce contracts: check LSP (objects return trustworthy types) and LoD (no chaining collaborator messages)
  5. Return to step 1

Phase 4: Implementation

Code is open. Make the change — usually as simple as creating a new polymorphic class and registering it in the factory. Then loop back to Phase 2.

Code Smells and Mechanical Recipes

Switch Statement / Conditionals supplying behavior → Replace Conditional with Polymorphism (or State/Strategy)

Primitive Obsession → Extract Class (give the primitive a domain object)

Duplicated Code → Extract to a single method; apply Flocking Rules to find the abstraction

Large Class → Extract Class (divide responsibilities)

Data Clump → Extract Class or consolidating method

Law of Demeter Violation (chained sends: a.b.c) → Delegation / message forwarding, or redesign from the sender's point of view

LSP Violation (returns unexpected types) → Perform type conversion inside the method

Temporary Variable (used only once) → Inline Temp

Blank Line within a method → SRP violation; separate responsibilities

The Flocking Rules (Unearthing Abstractions)

Apply mechanically when you don't understand the abstraction yet:

  1. Select the things that are most alike
  2. Find the smallest difference between them
  3. Make the simplest change that removes that difference

Apply in four microscopic steps: (a) parse the new code, (b) parse and execute it, (c) parse, execute, and use its result, (d) delete unused code. Make one line change at a time. Run tests after every change. Undo immediately on failure.

Replace Conditional with Polymorphism (Recipe)

  1. Create a subclass for the value you switch on
  2. Copy one switching method into the subclass; keep only the true branch
  3. Create a factory if none exists; register the subclass
  4. In the superclass, remove everything but the false branch
  5. Repeat for all switching methods
  6. Iterate until a subclass exists for every switched value

The Ultimate Goal

Build applications out of trustworthy, loosely-coupled, polymorphic objects that can survive an unknown future. The programming aesthetic: fall in love with polymorphism.

See Also