Lesson 0035 · Master Designer · Module 3
Stability vs Speed of Change
Stable systems resist change; fast systems court it. The master's toolkit — feature flags, strangler fig, branch by abstraction — exists to buy both at once.
Mission tie-in: the first Evolution Trade-Off — where the techniques from lessons 0009 and 0025 become instruments of organizational speed.
Knowledge: speed is a design property
The naive framing: stable or fast, pick one. The reframe: change speed is a property of the design, not a personality. A system changes fast when changes are small, local, and reversible (Phase I's whole point). A system changes slowly when every change is big, cross-cutting, and risky.
The three instruments that buy both stability and speed:
- Feature flags — ship the code dark; the behavior's visibility, not its presence, is the deploy. Roll back by flipping a flag, not reverting a release.
- Strangler fig (lesson reference: patterns sheet) — replace a system incrementally: new behavior routes to the new implementation; the old one shrinks slice by slice. The system stays stable while it's being replaced.
- Branch by abstraction — insert an interface over a component, migrate all users to it, swap the implementation behind it. Every step is a small, mergeable, reviewable change.
# feature flag: the code is in production; the behavior waits
def checkout(cart: Cart, flags: FeatureFlags) -> Order:
if flags.is_enabled("new_pricing"):
return new_pricing_checkout(cart) # dark: no users see it yet
return legacy_checkout(cart)
# branch by abstraction: every consumer migrates to the seam first
class StockService(Protocol): # the seam
def reserve(self, sku: str, qty: int) -> None: ...
# then: legacy impl swapped for new impl behind the same seam
The honesty clause: flags and seams are machinery — each carries complexity (lesson 0029's knob cost). A flag that lives forever is config debt; a seam for a component that never changes is indirection. The master removes the machinery when the transition is done.
There is a fourth instrument, for the change the other three cannot touch — data:
# expand / contract: never rename a column in one step
# 1. expand add `delivery_address`; write BOTH; keep reading `address`
# 2. backfill copy the historical rows, in batches, idempotently
# 3. switch read `delivery_address`; still writing both
# 4. contract stop writing `address`; only then drop it
Four deploys instead of one, and every single one is reversible. The one-step rename is the change that looks fastest and is the only one you cannot roll back once the first write lands — the exact profile lesson 0037 calls irreversible.
† The un-safety of the alternative: a big-bang rewrite is the speed play that buys neither — the system is unstable for months and changes slower (every migration conflict lands in one pile). Stability and speed are both served by small reversible steps.| The change | The instrument | Remove it when… |
|---|---|---|
| a feature not ready for users | feature flag | The day it is on for 100% of traffic. |
| swapping a component with 40 callers | branch by abstraction | When the last caller uses the seam and the old implementation is deleted. |
| replacing a whole legacy system | strangler fig | When the last route stops going to the old system. |
| renaming or dropping a column | expand / contract | After the read path has stopped using the old column. |
Skill: which instrument, when?
You want to ship code that isn't visible to users yet. Choose:
Replacing a component used by forty call sites, safely, means:
A permanent flag is best treated as:
Practice on your own code
Find a change you're about to make that feels "big". Split it: what's the smallest invisible slice (flag), the smallest structural seam (branch by abstraction), or the smallest routed slice (strangler)? Write the slice list — then do the first slice.
Reveal: a sliced transition
Replacing a payments provider: (1) insert the PaymentGateway seam (already there from lesson 0009's ports — the seam was the whole point); (2) migrate callers to it behind a flag that still uses the old provider; (3) swap the adapter, flag on for 1% of traffic; (4) 100%, remove the flag. Four slices, each mergeable, each reversible.
Your win
You can slice any big change into small, reversible, mergeable steps using flags, seams, and strangler routing — and you know to remove the machinery when the transition ends.
Read and watch deeper
- "Strangler Fig Application", Fowler — the incremental-replacement pattern.
- "Technical Debt", Fowler — the loan framing for what flags and shortcuts accumulate.
- Working Effectively with Legacy Code, Feathers — ch. 1–2: changing software without breaking it, slice by slice.
- Monolith to Microservices, Newman — the strangler fig at system scale.
- Next: lesson 0036 — Isolation vs Integration.
- Reference: Trade-offs — stability vs speed of change; Patterns — strangler fig, branch by abstraction, expand / contract, feature flag.
Bring your big change to your agent-teacher and slice it together before writing code.