Choose familiar components when their limits are visible, because every extra boundary turns a local change into shared operational work.
The decision behind the 2am page
The graph rose, the pager fired, and the incident call begins with a map of components rather than a clear failure. A gateway calls a service, which waits on another service, which writes an event that a worker may process later; everyone owns a piece, but nobody can state what the request is waiting for. That is the decision on the table: keep a short, familiar path, or add a boundary that promises a property worth operating.
Boring architecture means legible architecture. It is not a ban on new technology or a vote for a monolith forever. It is a refusal to accept a component merely because it is available: every box needs a constraint it protects, an owner, and a failure mode the team can explain when the path is under load.
A short path and its limits
A single deployable process, or a small set of modules that change together, buys a shared model of the request. The state is close to the code path, local calls do not introduce timeouts or partial network failure, and one team can trace a write from input to durable state without reconciling several dashboards. The CTO pays for this choice when a larger deployable unit slows coordination; the Staff engineer pays when unrelated changes share a release and a blast radius.
That cost is real. A short path is not automatically safe at higher demand, and a module boundary cannot solve a workload that needs independent failure containment. The point is narrower: do not pay the operating cost of distribution before the system needs the property distribution provides.
The boundaries that earn their keep
A new service, queue, cache, or datastore can be the right call. Each earns its place differently, which is why the comparison belongs at the centre of the decision rather than after it.
| Choice | Property it buys | Operational cost | Who carries the cost | Evidence needed before adding it |
|---|---|---|---|---|
| Keep work in one deployable unit | Short request path and one place to inspect state | A wider release and shared scaling limits | The delivery team during releases | The unit can still be deployed, owned, and scaled together |
| Add a service boundary | Independent deployment or fault isolation | Contracts, network failure, timeouts, and a durable ownership split | On-call engineers and teams maintaining the contract | A failure or release boundary that a module cannot provide |
| Put work behind a queue | Absorb bursts and separate producer from consumer timing | Delivery semantics, replay, ordering, poison messages, and delayed feedback | The team that diagnoses stuck or duplicated work | Producer and consumer genuinely need different rates or availability |
| Add a cache or second datastore | A read path with lower latency or a different access pattern | Staleness, invalidation, replication, and another source of truth to observe | The team answering why a user saw old data | An access pattern whose consistency cost is understood |
The table does not say that the first row wins by default. It says the alternatives have different prices, and the price is paid in operations, not in the diagram review. A queue can keep a producer available while a consumer is down, but it moves the hard question into state: what happens to late, repeated, or poisoned work? A cache may relieve a read path, but it also makes “what is current?” a product question.
The service boundary and its irreversible costs
A service boundary is often the most expensive choice to reverse. Once it owns data, exposes a contract, and is deployed independently, it creates a second product inside the product: versioning, compatibility, access control, and a team-to-team agreement. That may be exactly what isolates a volatile workload or lets two groups deliver safely at different cadences. It is not a free way to organise a repository.
Services that still release together, share a database, and call one another synchronously have accepted network failure without real isolation. The boundary may still be worth keeping, but the team should name the property it protects rather than treating the number of repositories as evidence. If no such property survives review, bringing the work back together reduces the number of states an incident commander must reason about.
The novelty threshold and its exceptions
Familiar technology is valuable because the team already knows its failure signals, upgrade path, and operational vocabulary. That familiarity compresses the time between an alert and a useful hypothesis. It does not make a tool universally correct. A regulatory isolation requirement, a workload with a materially different access pattern, or a known failure mode that conflicts with the product’s risk can justify novelty.
When novelty is proposed, raise the burden of proof instead of lowering it. Write down the property it buys, the property it gives up, the owner of its failure mode, and the signal that tells you it is failing. A new component is a decision only when the operating model is as concrete as the implementation plan.
A reversible sequence for the next architecture review
Start by tracing one important request to durable state. Mark each network hop, data copy, retry, timeout, and asynchronous handoff. Then place each component in the table: which property does it buy, whose work does it create, and what would get worse if it disappeared?
Use capacity planning as a calendar, not a fire drill to separate a demand problem from a component problem, and treat a cloud bill that scales faster than traffic as another prompt to inspect the request path rather than an automatic reason to add machinery. For the wider diagnostic, connect this review to the scaling practice: the useful output is not a prettier diagram, but a path whose bottleneck and recovery model are clear.
Remove or defer the component with the weakest answer first. That is reversible: you can put it back if the protected property proves necessary. Splitting a data-owning service or introducing a permanent replication path is not, so make those moves only after the constraint is explicit.
FAQ
Does boring architecture mean we should keep a monolith?
No. A monolith is a deployment choice, not a virtue. Keep work together while it can share an ownership and scaling model; split it when a real isolation, release, or workload constraint makes the cost worthwhile.
When does a queue earn its operational cost?
When producer and consumer need different rates or availability, and the team can define delivery semantics, idempotency, replay, and ownership of failed work. A queue without those decisions hides pressure rather than resolving it.
Is a cache the simplest way to fix a slow read path?
Not necessarily. First identify whether the latency comes from the access pattern, an avoidable round-trip, or a query that should change. A cache is justified when the team also accepts the user-visible meaning of stale data and the invalidation path.
How should we evaluate a new technology proposal?
Ask which constraint the familiar option cannot meet, what failure mode the new tool introduces, and who will operate it. The proposal is ready when those answers are concrete enough to guide an incident response.
Before approving the next box, trace the request that made it seem necessary and make the constraint visible; if the team cannot name the property it protects, keep the path boring for one more release.


