When diffs land faster than your gates can reject bad ones, the repo feels limitless.
That used to be mostly human typing speed. Now it is often assistant-proposed patches, merged on vibe when they "look right". Same geometry, faster collapse if admission lags.
It is not. You are trading away room to change the thing later.
Freedom in software is not how much you can ship. It is how much you can still change safely six months out.
That is not a one-time "we fixed how we ship". It is evolution in the evolutionary architecture sense (Fowler's foreword to Ford/Parsons/Kua): the product moves in steps, and the admission regime moves with it - new gates, retired checks, shifted seams - as explicit change, not folklore baked into tribal merge habits.
Control has to scale with proposal rate - not with how fast someone can read patches. So the blunt move: replace review with rejection, opinion with admissibility. You are not rubber-stamping plausibility; you refuse invalid transitions.
The model
proposal
-> machine constraints (reject / accept)
-> repo state (only admissible states land)
Correctness is enforced before merge, not argued after.
If staying correct still means "someone read the whole diff", you lose at high volume. Proposal outruns reading; verification has to be mechanical.
The correction loop (this is the engine)
proposal
-> tool says no (structured, machine-readable errors)
-> patch again
-> repeat until merge carries proof
The environment teaches through errors - not through a thread of explanations. Good failure messages converge; vague ones thrash.
The failure pattern is predictable
The problem is rarely "we shipped a bug". It is "we let implicit behavior through".
Then the same chain:
not rejected
-> observable
-> depended on (Hyrum)
-> contracted
-> expensive to fix
Plasticity drops: features keep landing, safe edits shrink.
High proposal volume makes the middle steps cheap: easy to merge plausible patches, easy to skip intent and invariants, hard for admission to keep up.
AI-assist raises proposal rate. Vibe-coding lowers the cost of acceptance - merge on feel, argue after. Together they widen the gap between output volume and proof.
You get more "looks right", less "we can defend this change".
Summary
The failure mode is unbounded admissibility.
If invalid states are representable and mergeable, something will eventually occupy them - especially under pressure. The same dynamic is spelled out as Hyrum's Law and in more depth in Software Engineering at Google, chapter 1.
Scalable control is not better prose in the PR. It is correctness you can enforce in CI.
Law
Proposal scales.
Only constraints scale correctness.
The stack (admissibility layers, not a checklist)
Each layer shrinks what can exist without proof.
1) State space - illegal states unrepresentable
If a state is representable, it will be produced.
Shrink the surface:
- opaque / branded types at boundaries
- ADTs + exhaustiveness so new cases do not compile until handled
- total-ish functions where "partial" hides bugs
That is state topology, not typing for aesthetics.
Merge fails if: new state / case escapes the model.
2) Topology - graph with enforced edges
You do not architecture-review line by line. You kill invalid edges.
- layer rules, forbidden imports
- private / internal modules so reachability is smaller
- one real way to cross a boundary (protocol, schema, bus)
- automate the graph in CI (ArchUnit for Java, dependency-cruiser for TS)
Merge fails if: dependency rule or boundary breaks.
3) Contracts - what may exist
Contracts are not discovered in the patch. They are declared first; implementation proves conformance.
- interface / trait as shape
- OpenAPI / Protobuf / JSON Schema as truth
- generated validators and clients off one spec
Merge fails if: code drifts from the declared contract.
4) Semantic policy - beyond "it parses"
Parser green is cheap. Policy is the rest: complexity, nesting, fan-out, banned APIs.
Non-syntax observables count too: latency floors, ordering quirks, anything users can see in production. Those expectations harden like a documented field - even when the contract never promised them (same shape as implicit-performance expectations in Hyrum's Law - see Summary).
Merge fails if: budgets or deny-lists trip.
5) Fitness - health and cost, not only logic
Building Evolutionary Architectures calls this kind of thing a fitness function: dependency growth, cycles, perf, memory, "how hard is revert". That is the bill for next quarter, not today's green build.
Merge fails if: fitness budgets regress.
6) Decision economy - intent is not optional
Big anonymous diffs are debt. Small changes + link to ticket / ADR / explicit intent ("why now, what breaks if wrong, when removable").
If there is no machine-linkable why for the what, treat the change as invalid.
Merge fails if: trace missing.
7) Feedback semantics
Verdict + reason codes + stable repro steps for the tool chain. Invest in error quality like you invest in features - bad messages waste everyone's time.
Another shift (architectural, not only procedural)
Gates reject bad merges. Good. You still lose if volatility leaks outward and becomes the contract.
Principle
Volatile parts must not be contracts. They live behind contracts - recursively:
- churny implementation behind a stable module surface
- churny module behind a stable service contract
- vendor quirks behind an internal adapter
- accidental shapes behind a semantic API
Stability is not "clean core, messy edges". Every boundary that other teams or binaries see has to stabilize meaning - not mirror today's DB row, library type, or temporary workflow. The old split is still the reference: Parnas on module criteria; for "shallow interface, deep module", Ousterhout.
When volume is high, the dangerous move is the same every time: an internal detail ships as a public shape (DTO as API, enum as protocol, error text as behavior). Assistants are especially good at surfacing whatever shape is easiest in the moment - which is exactly what you do not want on a public edge. Then the usual chain: observable -> depended -> expensive.
Good architecture does not publish change. It absorbs it where the system can still afford to rewrite.
The balance (do not over-rotate)
"Hide everything behind interfaces" is how you buy a second job: adapters, mapping layers, ceremony, integration tax.
The target is not maximal isolation. The target is governed coupling:
- volatile internals
- stable semantics
- economical integration
Contracts must be cheap enough to compose. A boundary is good if it absorbs local churn without making cross-boundary change disproportionately expensive.
Prefer a thin waist: small surface, explicit invariants, few operations, clear error semantics - not a universal wrapper over the universe.
DRY caveat
Duplication inside a volatile zone is often cheaper than a shared abstraction that freezes churn too early.
Law-shaped rule:
Don't share what is still changing.
Hide it until the semantics stabilize.
Sanity checks on any seam
- Does it hide volatility, or just export the internal model?
- Can you swap implementation without changing the contract? (Seams and tests around them are still the practical toolkit in Feathers.)
- What does a cross-cutting change cost across two or three of these seams?
- Did the adapter layer become the slowest and smartest part of the system?
Not everything should be isolated. Isolate where volatility would otherwise escape and become public truth.
The purpose of a boundary is not separation for its own sake. It is preserving cheap change for everyone outside.
Rollout (without freezing the team)
- Pick 5-10 non-negotiables: invariants + boundary rules.
- Encode as CI with explicit failure modes.
- Require intent links for non-trivial patches.
- Add fitness budgets (deps, complexity, perf, reversibility).
- Trend the meta: rejections by rule, repeat offenders, cost to delete recent work, deps touched per change.
What to optimize for
Not "more lines merged". More reversible change.
Execution got cheap; mistakes did not. They got easier to commit and harder to remove.
So the question is not "how fast do we ship"? It is "do we keep the right to change this tomorrow"?
"Read every line" is not a strategy at volume.
What scales: invariants and contracts that reject invalid states before they become history.
Top comments (0)