---
id: 66
title: "Agonize over the interface, not the choice behind it"
kind: note
status: current
date: 2026-08-24
ai_assisted: true
authors:
  - "Theo Zourzouvillys"
tags: [architecture, design, process, decisions, llm]
references:
  - id: parnas
    title: "D.L. Parnas — On the Criteria To Be Used in Decomposing Systems into Modules (CACM 15(12), 1972)"
    url: https://dl.acm.org/doi/10.1145/361598.361623
    abstract: "The paper that introduced information hiding. A system should be decomposed by the design decisions each module conceals from the rest, not by the steps in its processing — so that each of those decisions can later change without the rest of the system finding out."
  - id: bezos2015
    title: "Jeff Bezos — 2015 Letter to Shareholders"
    url: https://s2.q4cdn.com/299287126/files/doc_financials/annual/2015-Letter-to-Shareholders.PDF
    abstract: "Introduces Type 1 and Type 2 decisions: one-way doors, consequential and nearly irreversible, deserving slow deliberation; and two-way doors, changeable and reversible, which 'can and should be made quickly by high judgment individuals or small groups'. A footnote names the opposite error — organisations that habitually use the light-weight process on Type 1 decisions go extinct before they get large."
summary: "Re-implementing a decision now costs hours. Changing an interface costs everyone standing on it. Spend deliberation at the boundary; hold the choice behind it loosely — on one condition: you know a decision was made, and where it lives. Unnoticed ones are the expensive kind."
supersedes: null
superseded_by: null
aliases: []
crossrefs:
  ZFN-23: "The sibling note, from the other end. There the claim is that an implementation behind a stable contract is safe to throw away; here the claim is what that does to the decision you make before writing anything."
  ZFN-1: "The mechanism that makes a decision locatable. Cheap reversal only works if someone can find the choice and the reasoning behind it a year later."
  ZFN-22: "Containment is the precondition. A choice with one place to live is cheap to revisit; the same choice spread across the codebase is not a decision any more."
  ZFN-47: "Why the boundary stayed expensive. On the far side of a contract are teams with their own roadmaps, and no amount of generation speed rewrites their code for them."
  ZFN-62: "The other reason boundaries stay expensive: data already written under a schema cannot be regenerated, so a shape change is three deploys and a backfill rather than an afternoon."
---

## TL;DR

**Deliberation is a budget, and it should be spent on boundaries.** What a choice will cost you is
no longer how hard it was to build. It is how many things you don't control are standing on it.

- The cost of *re-implementing* a decision has collapsed. The cost of *changing an interface* has
  not: on the far side of one are other teams, clients you have already shipped, and rows already
  written in the old shape. That asymmetry is the whole note.
- So agonize at the boundary — the schema, the envelope, the error taxonomy, the identifiers, the
  data model. Behind the seam — the library, the algorithm, the internal structure, the storage
  engine — decide fast and hold it loosely.
- **The condition is that the decision has to be locatable.** You need to be able to name it, point
  at where it lives, and replace it without a hunt. A decision nobody noticed making is not cheap to
  reverse at any price, because by then it isn't one decision — it's a hundred places.
- The failure mode of the cheap era isn't choosing wrong. It's a hundred unnamed choices, made at
  generation speed, none of them written down, hardening into an architecture nobody chose.
- Cheap to change is not free to churn. Record *why*, so the next person reopens the question on new
  evidence rather than on taste.

## Context

The decision advice most of us absorbed was priced for a world where building the thing was the
expensive part. [Bezos's 2015 shareholder letter](ref:bezos2015) is the crispest version of it: Type
1 decisions are one-way doors, consequential and nearly irreversible, to be made slowly and with
great deliberation; Type 2 decisions are two-way doors, and should be made quickly. The framework is
good and I still use it. What has moved is where the line falls.

[Parnas](ref:parnas) supplied the other half fifty years ago: decompose a system by the **decisions
each module hides**, not by the steps in its processing. A boundary exists precisely so the choice
behind it can change without the rest of the system finding out. That argument has not changed. What
changed is the payoff.

Re-implementing behind a stable contract used to be weeks, and weeks is what turned a
merely-adequate choice into a permanent one. It's now hours. Swap the queue client, replace the
hand-rolled state machine, move the parser, take a second run at a module with a cleaner structure —
given a real interface and tests, that's an afternoon.
[ZFN-23](/zfn/23-iterate-and-rewrite-implementations/) makes the case that rewriting behind a
contract is safe; this note is about what that does to the decision you make *before* writing
anything. If reversal is an afternoon, deliberating for a week to avoid it is an expensive way to be
slightly less wrong.

> [!aside]
>
> I've found myself more and more doing the thing that has always been expensive: trying a path out
> just for the sake of it. And I'm constantly impressed. As of now — August 2026 — every time I get
> that "but the implementation is a lot of work" feeling, I just feed it into Claude Code or Codex,
> just to see what it comes up with. Sometimes I throw it away. But a lot of the time, it turns out
> it just created something in the background that would have taken me weeks of work.

Nothing similar happened to interfaces, because an interface isn't code. It's a promise to someone
who isn't in the room. Changing one means coordinating with teams who have their own roadmaps
([ZFN-47](/zfn/47-govern-the-contract-between-teams/)), clients you shipped and cannot recall, and
data already on disk in the old shape ([ZFN-62](/zfn/62-expand-migrate-contract/)). An LLM will
rewrite every call site in your repository in minutes and can do nothing at all about the ones in
someone else's. Generation speed applies to code you own; a boundary is exactly the line where that
stops being true.

So the thing that used to ration change was **effort**, and it doesn't any more. What replaced it is
**awareness**. You can be loosely bound to a decision — genuinely willing to redo it next month — as
long as you know you made one and know where it lives. That qualifier carries the entire argument.

## Recommendation

**Spend deliberation in proportion to who is standing on the choice, and make sure every choice you
make is one you can find again.**

- **Sort by what's on the far side, not by how important it feels.** A storage engine feels like a
  momentous decision and is usually a contained one. A field name in a published payload feels
  trivial and is close to forever. The useful question isn't "how significant is this?" — it's "who
  would I have to call?"
- **Behind a seam, pick the boring option in an afternoon.** You're not choosing the final answer,
  you're choosing the current one. Take the thing you can reason about today; the cost of being
  wrong is a rewrite you can afford.
- **Write down that a decision happened, even a cheap one.**
  ([ZFN-1](/zfn/1-engineering-decision-records/)) A few lines. What you're preserving isn't the
  answer, it's the *fact that there was a question* — plus enough context that someone can reopen it
  without re-deriving the problem. That record is the difference between a decision and a habit.
- **Give every decision one place to live.**
  ([ZFN-22](/zfn/22-extract-complexity-at-the-seam/)) One adapter, one module, one config value. If
  the honest answer to "where would I change this?" is a grep across the repository, it stopped
  being a decision a while ago.
- **Don't gold-plate the interface either.** "The boundary is expensive" is not a licence for
  speculative generality, which is its own permanent tax. Make it *specific and honest* — name what
  you actually mean ([ZFN-51](/zfn/51-design-the-request-envelope-first/),
  [ZFN-58](/zfn/58-errors-are-part-of-the-contract/)) — and version it so it can move. The goal is
  knowing which side of the line you're on, not flexibility everywhere.
- **Reopen on evidence.** Cheap reversal makes it tempting to re-litigate on taste, or on whatever
  someone read about last week. The recorded *why* is the test: if nothing in it has changed,
  neither has the decision.

**Caveat.** Some choices are structural even when they look internal. The data model is the classic
one ([ZFN-20](/zfn/20-deliberate-complexity-is-often-simpler/)): it leaks into every consumer
whether you meant it to or not, and no seam rescues you from getting it wrong. If a choice will
reshape everything downstream, treat it as a boundary regardless of which directory it lives in.

## Consequences

**Easier:**

- Most decisions get faster, honestly rather than recklessly — and the interior is where most
  decisions are.
- Being wrong is survivable exactly where you're most likely to be wrong: inside, where you had the
  least information when you started.
- Running two implementations against one interface becomes a normal way to settle an argument
  rather than a luxury.

**Harder:**

- **Generation outruns notice.** Working at LLM speed produces decisions faster than anyone records
  them, and the result is a coherent-looking system full of choices nobody remembers making — the
  expensive kind. [ZFN-28](/zfn/28-llm-brute-force-prs-understanding/) is the same failure seen from
  the understanding side.
- Telling the two sides of the line apart is now the skill, and it's a judgement call with no
  checklist. Bezos's own footnote names the opposite error: organisations that habitually run the
  light-weight process on genuine one-way doors go extinct before they get large. Cheap change
  doesn't abolish one-way doors, it moves some of them and leaves the rest exactly where they were.
- Held loosely is one flinch from churn. A team that rewrites its internals every quarter because it
  now can has spent the entire saving.

**New obligations:**

- Every boundary needs someone who can state what is promised across it, and a version story for
  when the promise changes.
- Decision records stop being ceremony reserved for the big calls. They become the thing that keeps
  the cheap ones cheap.

## References

- [Parnas (1972)](ref:parnas) — the original case that a module boundary exists to hide a decision so
  it can change. The cost side of this note, written fifty years before the benefit side moved.
- [Bezos, 2015 letter to shareholders](ref:bezos2015) — one-way and two-way doors. The frame this
  note revises: cheap reimplementation moves doors between the two lanes, it doesn't remove the lanes.
- [ZFN-23](/zfn/23-iterate-and-rewrite-implementations/) — the sibling note. Rewriting behind a
  contract is safe; this one is about the decision you make before you write.
- [ZFN-1](/zfn/1-engineering-decision-records/) — how a decision stays locatable once it's cheap.
- [ZFN-22](/zfn/22-extract-complexity-at-the-seam/) — containment: one place to change it.
- [ZFN-47](/zfn/47-govern-the-contract-between-teams/) /
  [ZFN-62](/zfn/62-expand-migrate-contract/) — why the boundary stayed expensive: other teams, and
  data already written.
- [ZFN-20](/zfn/20-deliberate-complexity-is-often-simpler/) — the data model is a boundary even when
  it looks like an internal detail.

## Changelog

- **2026-08-24**: First published as a Field Note.
