Software Design Reference Master Designer · Evolution Trade-Offs

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:

  1. 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.
  2. 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.
  3. 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.
Field notes · instrument, and when the machinery comes out
The changeThe instrumentRemove it when…
a feature not ready for usersfeature flagThe day it is on for 100% of traffic.
swapping a component with 40 callersbranch by abstractionWhen the last caller uses the seam and the old implementation is deleted.
replacing a whole legacy systemstrangler figWhen the last route stops going to the old system.
renaming or dropping a columnexpand / contractAfter 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

Bring your big change to your agent-teacher and slice it together before writing code.