Foundational Chapter 2
Architecture Is Constraint Management
"You cannot have all the properties you want. Architecture is the discipline of choosing which ones you will not have."
— Rick Collette
Abstract
You cannot have everything.
Fast and durable. Flexible and simple. General and quick. Pick. Every system worth trusting is defined less by what it can do than by what it gave up in order to do a few things well.
The first law gave truth a single home. This one asks what shapes a system around that home — and around every other hard limit a real system must honor.
The popular image of a software architect is someone who adds: more layers, more abstraction, more capability, more flexibility. The working reality is the opposite. Architecture is mostly subtraction and commitment — deciding which constraints are real, which trade-offs are acceptable, and which doors to close on purpose so that the system has a shape at all. A design that tries to keep every option open keeps no promises; a design that commits to the right constraints becomes predictable, fast, and safe in exactly the ways that matter.
This chapter argues that architecture is constraint management: not the pursuit of an unconstrained ideal, but the deliberate selection and enforcement of the constraints that make a system trustworthy. It grounds the argument in two systems that treat constraints as a primary design material — CapBan, whose entire engine is organized around what it refuses to know, and CapDB, whose existence is a single, disciplined constraint held for years.
1. The idea
Every system is defined less by what it can do than by what it has decided it will not do.
A database that guarantees serializable transactions has decided not to be the fastest possible store. A cache that guarantees microsecond reads has decided not to be durable. A system that guarantees it can be reasoned about has decided not to support every clever thing a contributor might want to add. These are not failures of ambition. They are the constraints that give each system its character, and removing them does not produce a better system — it produces a formless one that promises everything and can be trusted with nothing.
This is why "architecture is constraint management" is more than a slogan. The architect's real work is to look at the space of everything a system could be, identify the constraints that actually matter — the ones imposed by the domain, by truth ownership, by the operational reality, by the team — and then commit to them hard enough that the rest of the design follows. The constraints are not obstacles to route around. They are the load-bearing decisions. Everything else is detail.
There is a useful inversion hiding here. Junior designs treat constraints as enemies: things to minimize, defer, or abstract away until the system can do anything. Mature designs treat constraints as tools: the thing you reach for first, because a well-chosen constraint does more work than any amount of flexibility. The chapters of this book are full of constraints presented as gifts — truth has one home, the AI may not act, the gate fails closed — and each one buys a property that no amount of generality could.
2. The forces
Three forces make constraint management the central architectural act, and a fourth makes it hard.
You cannot optimize for everything. Latency and durability pull apart; flexibility and reasonability pull apart; generality and performance pull apart. Every desirable property a system might have competes with some other desirable property for the same finite budget of complexity, time, and human attention. The architect does not get to want all of them. The job is to decide which properties are non-negotiable for this system and to spend the budget there, accepting that the others will be weaker.
Commitment creates capability. A constraint, once truly committed to, becomes something the rest of the system can rely on. If writes are guaranteed serialized, every reader can stop worrying about a class of race. If a module is guaranteed to be disableable, operations can stop fearing it. Capability in a system comes not from keeping options open but from closing them in ways that let everything downstream assume the closure. Uncommitted flexibility is a liability that every component must defend against; committed constraint is an asset every component can lean on.
Constraints must be enforced, not merely intended. A constraint that lives only in a design document or a code-review norm is not a constraint; it is a wish, and wishes erode. The constraints that hold are the ones the architecture makes hard or impossible to violate — through a type system, a compile-time gate, a trait boundary, an authorizer, a single enforced seam. The strongest architectures push their constraints down to where violating them is not a matter of discipline but a matter of "the code does not compile" or "the operation is structurally impossible."
The hard part is choosing the right ones. All of the above assumes you have identified the correct constraints, and that is the genuinely difficult, judgment -laden work. Over-constrain and the system is rigid, unable to adapt to needs you failed to anticipate. Under-constrain and it is formless, slow, and untrustworthy. Constrain the wrong axis and you have paid the price of rigidity without buying the property you needed. There is no formula for this; it is the architect's core skill, informed by the domain, the truth model, and an honest reading of what the system must guarantee versus what it can leave open.
3. The law
Architecture is the deliberate selection and enforcement of the constraints that make a system trustworthy. Design is choosing which properties to guarantee and, therefore, which to give up — and then making the guarantees structurally hard to violate.
Two corollaries follow:
A constraint is only real if it is enforced. Prefer constraints the architecture can make impossible to break — at compile time, at a single seam, by construction — over constraints maintained by vigilance.
Generality is a cost, not a default virtue. Every degree of freedom a system keeps open must be paid for by every component that has to handle it. Keep the degrees of freedom that earn their cost; close the rest.
CapBan and CapDB demonstrate the two halves of this law. CapBan shows constraint as enabling structure — an engine made powerful by what it refuses to know. CapDB shows constraint as sustained commitment — a system whose entire identity is one hard boundary held over time.
4. Implementation: CapBan, the engine defined by what it refuses to know
CapBan is an intrusion-prevention system, but its architecture is a case study in constraint as a design material. At its center is a single type:
CapBanEngine<S, E> where S: CapBanStore, E: CapBanEnforcer
The engine is generic over two traits — a Store for state and an Enforcer for applying decisions — and that genericity is a deliberate, enforced constraint, not a convenience. The engine is forbidden from knowing whether its storage is an in-memory map or a persistent embedded database, and whether its enforcement is a no-op, a Linux firewall, or a Kubernetes network policy. It can only speak to the narrow vocabulary the traits define.
This is constraint as enabling structure. By refusing to let the engine know about concrete storage or enforcement, CapBan makes several properties true by construction:
detection logic be exercised with no firewall and no disk. The constraint that the engine cannot reach past the traits is exactly what makes this possible.
enforcer means implementing a trait, not modifying the engine. The constraint protects the core from the churn at the edges.
firewall rules — lives behind the Enforcer seam, where it can be made idempotent and validated in one place, rather than smeared through the engine.
- The core is testable in isolation. A
NoopEnforcerand aMemoryStorelet the - New backends cost nothing to the core. Adding PostgreSQL storage or a Kubernetes
- The security boundary is localized. The dangerous work — actually manipulating
Notice the inversion from Section 1 in action. A less disciplined design would let the engine "just call nftables directly" for convenience and add abstraction "later." That flexibility would cost every future change: every test would need a firewall, every new backend would mean surgery on the core, every security review would have to read the whole engine. CapBan pays a small up-front cost — you must define the traits and route through them — to buy a large, permanent capability. The constraint is the feature.
CapBan reinforces the same philosophy at the language level. It is written in Rust and leans on the type system to make illegal states unrepresentable — a Decision cannot be constructed without the fields that make it valid; an Event cannot be malformed at the boundary. These are constraints the compiler enforces, which is the strongest form there is: not "we agree not to do this" but "this does not compile." And it applies the same idea to its detection logic by refusing a general regex engine in the core — parsers are small, focused, testable modules, and the open-ended regex power is pushed to the boundary where it is loaded as data and contained. CapBan is fast and trustworthy not because it kept its options open but because it closed them on purpose, at the places where openness would have cost the most.
5. Implementation: CapDB, a system that is one constraint
If CapBan shows constraint as structure, CapDB shows constraint as commitment over time — and the discipline of refusing to relax it.
CapDB is a hard fork of SQLite that adds connection pooling, TLS network access, and physical replication. The defining architectural decision is stated in its own design notes as a principle: engine stability first. CapDB does not replace SQLite's B-tree, pager, or query planner. It layers new capability around a proven core that it commits to leaving alone. That commitment is the whole architecture, and everything else is arranged to honor it.
The constraint is expensive. Holding it means CapDB must track upstream SQLite releases and merge their patches — ongoing work it could avoid by forking freely and diverging. It chooses the cost deliberately, because the alternative — rewriting the parts of a database most likely to harbor subtle, data-corrupting bugs — would forfeit the one property that makes a storage engine worth using: that its core is battle-tested and correct. CapDB constrains itself so that its users do not have to wonder whether the pager is sound. This is the law's first corollary lived out: the constraint is real because the project structures its work — feature gates, fork-only extensions, upstream tracking — to make violating it visible and avoidable.
CapDB also manages constraints at build time, the same way CapBan manages them at the type level. Its capabilities are gated behind explicit feature flags — pool, network, store, replication, full-text search — with implication rules that encode the dependencies between them: network implies pool, replication implies store, store implies pool. Those implication rules are constraint management made literal. They prevent the construction of nonsensical configurations (networking without a pool to serve it) not by documentation but by the build system itself. You cannot compile an incoherent CapDB; the constraints are enforced where they cannot be ignored.
And where CapDB adds capability, it does so by adding constraints, not removing them. Its connection pool deliberately refuses shared-cache mode, giving each connection a private page cache — a constraint that trades a memory optimization for the elimination of an entire class of cross-connection locking bugs. Its replicas are constrained to be read-only, rejecting writes outright, with generation fencing to prevent a promoted replica's predecessor from ever writing again. Each added feature is, on inspection, a new boundary drawn — and each boundary is what makes the feature safe. CapDB grows by constraining, which is the opposite of how growth usually goes, and exactly why it stays trustworthy as it grows.
6. The failure modes
The unconstrained system. The design that refuses to commit — everything is configurable, every layer is abstract, every decision is deferred "for flexibility." It can do anything and guarantees nothing. Such systems are slow (every path must handle every case), fragile (no component can rely on any other's behavior), and impossible to reason about (the actual behavior depends on a configuration no one fully holds in their head). Flexibility hoarded past the point where it earns its cost is not prudence; it is the absence of architecture.
The unenforced constraint. A constraint that exists only as intent — a comment, a wiki page, a "we always do it this way" — and is therefore violated the first time someone is in a hurry. The fix is to push the constraint down to where it is structurally enforced. If "truth has one home" matters, an authorizer should deny the write that would give it a second home, the way CapDB's pool authorizer denies ATTACH to keep connections from opening arbitrary files. A constraint maintained by vigilance is a constraint already decaying.
The wrong constraint. Rigor applied to the wrong axis. The team locks down something that did not need locking — an internal API frozen prematurely, a data format ossified before it stabilized — while the axis that actually mattered stays loose. The cost of rigidity is paid; the property that rigidity was supposed to buy is never delivered. This is the hardest failure to see, because it looks like discipline. The guard is to keep asking what property does this constraint buy, and do we actually need it — and to relax constraints that turn out to buy nothing.
Constraint creep without coherence. Constraints added one at a time, each locally sensible, until they collectively contradict — a system that is simultaneously required to be stateless and to remember things, to be synchronous and to never block. CapDB's implication rules are the antidote in miniature: constraints should be managed as a coherent set whose interactions are understood, not accreted independently until the set is unsatisfiable.
7. The tradeoffs
The honest cost of constraint management is adaptability. A system built on firm, enforced constraints is, by design, harder to bend in directions its constraints did not anticipate. CapDB's "leave the engine alone" makes it deliberately unable to do the radical core surgery that a from-scratch engine could attempt; CapBan's trait boundaries mean a change that genuinely needs the engine to know about its storage is awkward by construction. These are real limits, and they are the price of the guarantees the constraints buy.
The discipline, then, is not "constrain maximally" but "constrain correctly" — and to leave open, deliberately, the axes where future change is likely and the cost of rigidity would be high. CapBan keeps storage and enforcement open precisely because it expects new backends; it locks the engine's logic because it does not expect, and does not want, that to vary per deployment. CapDB freezes the engine because that is the axis where stability is worth everything, and gates everything else behind flags it can turn on and off. The skill is in the matching: firm where guarantees matter, open where change is expected, and an honest account of which is which.
There is also a cost in up-front thought. Choosing the right constraints requires understanding the domain, the truth model, and the operational reality before the design sets — work that an "we'll keep it flexible and decide later" approach appears to let you skip. It does not, really; it only defers the cost to every future change and charges interest. But the up-front cost is real, and pretending constraint management is free is its own failure.
8. The future
As systems grow more distributed, more automated, and more AI-driven, the surface area of things that could be done expands faster than any team's ability to reason about them. That is precisely the condition under which constraint management becomes more important, not less.
An AI agent that can call arbitrary tools is the unconstrained system taken to its limit — maximally flexible, minimally trustworthy. The systems that will be safe to automate are the ones that constrain hard: explicit action-rights matrices instead of open capability, fail-closed gates instead of permissive defaults, descriptor hashes that constrain a tool to be exactly what was approved. Chapter 1's AISDR was, read through this chapter's lens, a study in constraining an AI down to where it can be trusted. As models grow more capable, the constraints around them — not the capabilities within them — will be what determines whether they can be deployed at all.
Distribution pushes the same way. More services and regions mean more degrees of freedom, more ways for state to diverge, more configurations that could be assembled. CapDB's implication rules and read-only replicas are small previews of a general truth: distributed systems stay coherent only when their constraints are enforced structurally — by the build, by the protocol, by the type system — rather than by the hope that every operator assembles them correctly.
The tools will change. The forces will not. There will always be more properties a system could have than it can have at once, always a finite budget of complexity to spend, and always the temptation to keep every option open rather than commit. The architect's job, in every era, is the same: see the constraints clearly, choose the ones that matter, enforce them where they cannot be violated, and accept — honestly — the adaptability given up in exchange for a system that keeps its promises.
The next law turns to a constraint so often deferred that deferring it has become an industry-wide failure mode: the constraint that a system must be safe to operate at all. Security Is a Property, Not a Feature.
