Every architecture is a bet on a set of assumptions, and every one of them eventually loses. The judgement that matters is not whether to redesign — you will — but when, and how much. Get it early and you burn a year rebuilding something that was merely unfashionable. Get it late and the redesign happens under a feature freeze, driven by an outage, with the people who understood the old system already gone. This chapter is about the leading indicators that separate a system that has run its course from one that is simply old, and about reading them before the incident review reads them for you.
Every system with users hurts. Engineers complain about the legacy module, the build is slow, on-call is noisy, the data model has a column nobody understands. None of that, on its own, says the architecture is exhausted — most of it is entropy, the steady accumulation Lehman described half a century ago, and it is roughly proportional to how much the system is being changed. The mistake senior engineers make in both directions is reasoning from the level of pain. Newly arrived leaders see a high level and call for a rewrite; long-tenured teams have habituated to the level and miss that it is accelerating.
The useful question is about slope, normalised by whatever drives the work: cost per change per engineer, incidents per thousand deploys, infrastructure cost per unit of business output. Three shapes matter. Entropy is linear in the driver and is held in place by a fixed hygiene budget. An event — a reorg, a hiring wave, a large migration — produces a step and then decays back as people relearn the system. Structural pain has a slope that itself grows with the driver: each new team, tenant or feature makes every subsequent one more expensive. Only the third is an architecture signal, and at any single point in time it can look identical to the other two.
Illustrative shapes. At the dashed line all three systems report roughly the same cost per change, and a quarterly review looking at levels cannot tell them apart. Normalise by the driver and look at how the slope changes quarter over quarter: flat or falling slope is normal pain; rising slope is the architecture.
This reframes the conversation you will have with your leadership. "The system is painful" is an opinion and loses to the roadmap. "Cost per cross-team change grew 40% last quarter while headcount grew 10%, and the ratio has widened for three quarters" is a trend with a mechanism behind it, and it can be falsified. The remaining sections cover four measures that carry that trend reliably, and the traps in each.
Architect's note · the novelty discount
If you only instrument one thing, instrument lead time for changes that cross a boundary. The DORA lead-time metric, as most teams implement it, is a median over all merged changes. That median is dominated by local changes — a copy fix, a bug in one handler — and those stay fast long after the architecture has stopped fitting. The cross-cutting changes, the ones that require two or more teams or deployables to move together, are usually a single-digit percentage of volume. They carry almost all of the strategic work: a new pricing model, a new market, a regulatory requirement. When they slow down, the roadmap slows down, and the aggregate dashboard shows nothing.
The mechanism is queueing, and it is worse than intuition suggests. Suppose a change needs work from k teams in parallel, and each team's wait before picking it up is roughly exponential with mean μ. The change completes when the slowest team finishes, and the expected maximum of k such waits is μ times the k-th harmonic number: 1.8μ for three teams, 2.7μ for eight. That is the optimistic case. In practice the dependencies are partly sequential — the schema has to land before the API, the API before the client — and sequential hand-offs sum rather than max. Add sprint-planned backlogs, where a request that arrives mid-sprint waits on average half a sprint just to be scheduled, and a change touching four teams in sequence picks up roughly two sprints of pure waiting before anyone writes code.
Because local changes dominate the count, the median is structurally blind to coupling. Segment lead time by how many deployables, repos or owning teams a change touched, and watch the widest bucket. When its slope is steeper than the growth in headcount, the boundaries are wrong.
The instrumentation is cheap. For each merged change set — group commits by ticket or by a release tag — record how many deployables, repositories and owning teams it touched, using the CODEOWNERS map you already have. Bucket by breadth (one owner, two, three or more) and plot each bucket's median and p90 per quarter. Pair it with change coupling from version-control history — which files and modules repeatedly change together, the technique Adam Tornhill's work popularised — because change coupling tells you where the boundary is wrong, while breadth-bucketed lead time tells you how much it costs.
Two refinements separate a real signal from noise. First, look at the share of changes in the wide bucket, not only their duration. A rising share means the product is asking for things the decomposition did not anticipate — the boundaries were drawn for last year's roadmap. A stable share with rising duration means the teams have stopped being able to coordinate, which may be an organisational problem (5.4) rather than a code problem. Second, watch for suppression: when cross-cutting work gets expensive enough, product managers stop asking for it. The wide bucket shrinks, the dashboard looks better, and the business has quietly routed around the architecture by not building the thing. The tell is a growing list of "we would like to but it touches billing" items in roadmap discussions.
1.83μ
E[max], 3 parallel teams
2.72μ
E[max], 8 parallel teams
k·μ
Sequential hand-offs
≈5%
Share of changes that matter most
Put numbers on the sequential case, because it is the one that surprises people. Take a pricing change that needs, in order, a schema change from the data team, an API change from the pricing team, a checkout change and a mobile release — four hops, two-week sprints (illustrative). Each hop waits on average half a sprint to be scheduled, five working days, before anyone starts: twenty working days of queueing, four calendar weeks, on a change whose hands-on work might total six engineer-days. Flow efficiency is under a quarter, and no individual team is slow. Add one rejected hand-off — the API shape does not suit the client and goes back one hop — and the queueing roughly doubles again. This is why the wide bucket can triple while every team's own dashboard is green: the delay lives in the edges of the graph, which nobody owns.
Pitfall · the metric becomes the target
Aggregate change failure rate is a noisy architecture signal: it moves with test investment, with team experience, with release cadence. The slice that carries the architectural information is the distance of a failure — how often the defect that caused an incident lived in a different module, service or team than the change that triggered it. In a system whose boundaries still hold, most failures are local: you broke your own thing. As coupling grows, failures start travelling: a change to a shared library breaks a consumer nobody listed, a reused configuration flag wakes up dormant behaviour elsewhere, a schema tweak for one feature changes a query plan for another. Tag each post-incident review with "triggering component" and "faulting component"; the fraction where they differ is the number to trend.
Knight Capital in August 2012 is the canonical extreme. Deploying new routing code, the firm reused a flag that had previously activated a long-retired function, and the deployment missed one of its eight servers. On that server the old code woke up and traded, and the firm lost around $440 million in under an hour. The proximate causes were deployment hygiene and dead code, but the structural fact is that a flag's meaning was shared across code paths with no boundary to stop a change in one from activating the other. Few systems fail that spectacularly; many fail that way in miniature every month.
The more common trap is what organisations do about rising CFR. Each incident produces a reasonable, local control: a freeze window around peak season, a change advisory board, a release train that batches deploys fortnightly, a 48-hour soak in staging. Each control lowers CFR. Each also increases batch size, and larger batches mean more changes interacting per release, harder bisection and longer recovery. The arithmetic is unforgiving: if each change independently carries a 2% chance of causing an incident (illustrative), a release of 10 changes has an 18% chance of containing at least one, and a release of 50 has 64% — and finding the culprit now takes log₂(N) build-and-verify cycles instead of a revert. CFR stays flat or improves on the quarterly review while lead time quietly multiplies. The architecture's coupling has not gone anywhere; it has been converted from failures into waiting. The cross-industry data is harsher still: the Accelerate research (Forsgren, Humble and Kim, 2018) found approval by an external body such as a CAB negatively correlated with lead time, deployment frequency and time to restore, and not correlated with change failure rate at all. Whatever CFR improvement your gates appear to buy, assume it is smaller than the organisation believes, and the lead-time bill is real.
Read the two metrics as a pair. A falling CFR bought with a rising lead time is not an improvement; it is the same coupling, now paid for in batching. Every control on this chart makes batches bigger, and bigger batches make each release riskier — which is why the next incident produces the next control.
The judgement call: when you see CFR and lead time move in opposite directions, and the lead-time increase coincides with process additions rather than with growth, the process is compensating for a structural problem. Count controls. If the number of gates a change passes through has grown faster than the team, the design is being defended by people instead of by boundaries — and people are the most expensive and least reliable boundary you can buy.
Pitfall · the regulated-industry exception
Chapter 2 covered Conway's law as a design force. Here it is a measurement. Every pair of teams whose code changes together needs a working communication channel — a shared roadmap, a standing sync, overlapping on-call. Pairs that co-change heavily without a channel produce integration bugs and surprise; pairs that meet regularly without co-changing produce overhead. Research on socio-technical congruence (Cataldo, Herbsleb and colleagues) formalised this as the ratio of required coordination that actually exists, and found that lower congruence correlated with slower resolution of work. You do not need the full method: the co-change matrix from version control and a list of real channels is enough to compute a useful approximation.
Checkout
Pricing
Catalog
Fulfilment
Platform
Checkout
31%
needed · talks
4%
—
18%
needed · silent
12%
needed · talks
Pricing
31%
needed · talks
22%
needed · silent
11%
needed · silent
6%
meets · no need
Catalog
4%
—
22%
needed · silent
2%
—
3%
meets · no need
Fulfilment
18%
needed · silent
11%
needed · silent
2%
—
14%
needed · talks
Platform
12%
needed · talks
6%
meets · no need
3%
meets · no need
14%
needed · talks
Reading it
Cell = % of the pair's merged change sets that touched code owned by both teams (last 2 quarters).
Needed = ≥10%. Channel = a standing sync, shared on-call, or shared roadmap.
Congruence = 3 of 6 needed pairs covered = 50%.
The pink cells are where your next cross-team incident is being written.
Illustrative composite. Both inputs are cheap: co-change comes from version control plus a CODEOWNERS map; channels from the org chart and calendar. Track the pink count over time — a rising count with a stable org chart means the code has drifted away from the team boundaries, not the other way round.
Coordination cost surfaces in the organisation well before it surfaces in the code, and it has recognisable tells. Roles appear whose job is to be a human interface between teams: integration leads, release managers, a "platform" team that in practice operates as a merge queue for everyone else. Slack channels multiply per initiative rather than per team. Design reviews need more attendees each quarter. The best senior engineers spend more of their week in alignment and less in the code, and the calendar is the cheapest instrument you have: sample the fraction of your top ten engineers' time in cross-team synchronous meetings each quarter. When it passes roughly a third (an illustrative line, not a law) and is still rising, the boundaries are costing you your most expensive people.
The matrix has a second use that most organisations never exploit: run it against the proposed org chart before a reorg. Re-map CODEOWNERS to the new teams and recompute on the last two quarters of history. A split that divides a team along a line its code crosses constantly turns internal co-change into cross-team co-change overnight — congruence drops, and the wide lead-time bucket from 5.2 jumps the following quarter, with no line of code changed. A large share of "the architecture got worse after the reorg" is exactly this, and it is visible in a spreadsheet a month before anyone announces anything.
Segment's widely read 2018 post on moving away from microservices is a useful public example read through this lens. They had split delivery to each downstream destination into its own service and repository; over time shared libraries diverged across more than a hundred services, and a fix to common code meant coordinating and redeploying all of them. The operational and coordination cost per destination was rising with the number of destinations — structural pain, not entropy — and they consolidated. The lesson is not "monoliths good"; it is that the coordination signal was visible and measurable, and it pointed to the specific seam that was wrong.
Architect's note · diffuse vs concentrated coordination
A sound architecture exhibits economies of scale: fixed costs — the database cluster, the platform team, the observability stack — amortise over more units, and cost per order, per job or per tenant falls, then flattens. When cost per unit bottoms out and starts rising with volume, some term in the design is growing faster than the work it serves. This is the most finance-legible signal you have, and the one least often computed, because cloud bills are organised by service and payroll by team, and nobody divides either by the business unit.
The super-linear terms are usually structural and recognisable. Fan-out: a request that touches every shard or tenant partition grows its cost with the partition count, and its tail latency with it — Dean and Barroso's "The Tail at Scale" showed that with a hundred back ends each slow 1% of the time, about 63% of fan-out requests hit at least one slow response. Working-set growth: a cache sized for yesterday's keyspace degrades gracefully in hit rate and then not gracefully in database load. Isolation by copy: single-tenant stacks for enterprise customers are rational at twenty tenants and ruinous at two thousand. Data gravity: cross-region or cross-AZ transfer per unit rises as more features read data that lives elsewhere. And O(n) operations on O(n) data: the nightly job, the full reindex, the backfill that used to take an hour and now takes the night.
Illustrative shapes. A healthy design amortises fixed cost and flattens. When the curve turns up, some term in the design is super-linear in volume. Include the human rows (pink-tinted): an architecture that needs an engineer-week per new integration has a unit cost too, it is just booked to payroll instead of the cloud bill.
Two practices make this signal usable. First, compute cost per unit fully loaded, including engineering time: pages per million requests, toil hours per new customer onboarded, engineer-weeks per new integration or market. Human cost is often the first to turn upward, because people absorb super-linearity before infrastructure does. Second, compute the marginal unit, not only the average. Illustratively: one quarter volume grows 25% and cost 10%, so the marginal unit costs 0.4× the average; the next quarter volume again grows 25% but cost grows 20%. Average cost per unit still fell 4% and the finance review is happy — but the marginal unit now costs 0.8× the average, and when it crosses 1.0× the average starts rising. Trending that ratio buys you two or three quarters of warning over trending the average. Amazon's Prime Video team's 2023 write-up — consolidating a distributed monitoring pipeline into a single process and cutting that service's infrastructure cost by over 90% — is a case where per-unit cost, not reliability or velocity, was the signal that the decomposition no longer fitted the workload (Chapter 4 covers the serverless side of that story).
63%
Fan-out requests hitting a p99 (N=100)
Marginal
Not average — it turns first
People
Absorb super-linearity first
Per unit
Divide the bill by the business
Each of the four signals has lookalikes, and a redesign aimed at a lookalike is the most expensive mistake in this chapter. Slow CI or flaky tests inflate every lead-time bucket equally; structural decay inflates the wide bucket disproportionately. Knowledge loss — the two people who understood the billing engine left — produces a step in CFR concentrated in one area, which decays as new owners learn it, or does not, in which case you have a documentation and ownership problem. A hiring wave raises CFR and lead time across the board for two or three quarters. Under-investment — a year of zero hygiene budget to hit a launch — looks like compounding decay but reverses quickly once the budget returns. And a change in product strategy can make perfectly sound boundaries suddenly wrong, which is the one lookalike that genuinely is an architecture signal.
The diagnostic below orders the questions by cost. Most bad redesign proposals fail the first gate — the slope, once normalised, is flat — and most of the rest fail the second: the pain was tooling or process and a bounded fix removes it. Only after both does it become worth asking whether the pain is concentrated in a seam or diffuse, and whether the assumptions the system was built on still hold.
Q1 Normalised by its driver, is the pain's slope rising?
Divide by engineers, units of work or feature count; compare 3+ quarters.
No → normal pain: fund hygiene at a fixed share, no redesign. Still run Q4 — flat slope plus an expired assumption is the latent-mismatch case.
Q2 Does it survive a bounded fix?
4–6 weeks: CI time, flaky tests, ownership gaps, one hot-spot refactor.
No, it went away → it was tooling or process. Keep the fix funded.
Q3 Is it concentrated in one or two seams?
e.g. >60% of cross-team changes and incidents route through ≤2 modules or one shared data model (threshold illustrative).
Yes → redesign that seam only: strangle it behind an interface, leave the rest.
Q4 Has a load-bearing assumption expired?
Tenancy, region count, write pattern, traffic shape, regulatory scope, who the caller is.
Yes → re-architect around the new assumption, incrementally.
Answered "yes, yes, no, no"
diffuse, compounding decay with no changed assumption
Usually ownership, not architecture. Redraw team boundaries before you redraw service boundaries.
The order matters because the gates get more expensive. Q1 is a spreadsheet, Q2 is a month, Q3 is a code-and-incident analysis, Q4 is a conversation with the business. Most redesign proposals skip straight to the conclusion of Q4 without having run Q1.
The last question deserves the most attention, because it is where the architecture's expiry date is actually set. A design does not wear out; its load-bearing assumptions do. Write them down explicitly — one tenant per database, one region, a single writer per entity, batch freshness is acceptable, the catalogue fits in memory, callers are humans at human speed, personal data can cross borders — and review the list against the business plan every half. That last pair are live examples: agentic clients are invalidating human-paced traffic assumptions in rate limiting, session design and cost models, and data-residency rules have ended the single-region assumption for many companies that had no other reason to leave it. An expired assumption is a signal before any metric moves.
load-bearing assumptions still hold
an assumption has expired
slope rising
Scaling seam
The design is right for the business; one part of it has hit its order-of-magnitude limit.
Redesign that seam. The rest stays.
Run its course
The system encodes a world that no longer exists, and every quarter costs more than the last.
Redesign now; stop adding features to the old seam.
slope flat
Normal pain
Complaints, legacy, ugly corners. Costs track the driver linearly.
Fixed hygiene budget. Refuse the rewrite.
Latent mismatch
The business moved but volume has not caught up yet. Nobody feels it.
Cheapest moment to migrate — and the hardest to fund. Start the strangler now.
The common path runs right, then up: normal pain → latent mismatch (the business changes) → run its course (volume arrives). Straight up — normal pain to scaling seam — is growth without a changed assumption. The gold quadrant is where good architects earn their salary: the evidence is an assumption, not a metric, so the argument has to be made before the dashboards agree with it.
The 2×2 is also a map of who to convince. In the scaling seam quadrant, engineering can usually fund the fix itself — Chapter 8 covers designing that seam for the next order of magnitude. In run its course, the dashboards already agree and the argument is about scope and sequencing. In latent mismatch, you are asking the business to pay for a problem that has not arrived yet, on the strength of an assumption change they themselves made. That is the hardest sell and the cheapest migration, and it is worth making the case with the numbers from the quadrant it will move into.
Even with good signals, organisations act late, and the reason is structural rather than a failure of nerve. The value of redesigning compounds with the slope; the cost of redesigning grows with the size of the system, the data volume and the number of integrations; and the capacity to do it shrinks, because the drag you are trying to remove is consuming the same engineers you would need. There is a window where value exceeds cost and cost is still within spare capacity. It opens earlier than it feels, and it closes well before the incident that finally makes the case obvious.
Illustrative model: redesign cost grows with the system (+4 eng-months per quarter), free capacity shrinks as drag consumes it (−3), and the value of acting compounds with the slope (×1.3 per quarter). The window opens when value passes cost and closes when cost exceeds the capacity you can spare without stopping the business. Organisations that wait for an incident to decide usually arrive after it has closed — and then do the rewrite anyway, under a freeze.
The antidote is to decide the triggers while calm. In a quarterly architecture review, track the four measures — breadth-bucketed lead time, failure distance, congruence, marginal cost per unit — plus the assumption register, and agree tripwires in advance: for example, "if wide-bucket lead time grows faster than headcount for three consecutive quarters, and more than half of it routes through the order model, we fund a seam redesign next half." The exact thresholds matter less than the pre-commitment. It defeats sunk-cost reasoning ("we just finished migrating to this"), novelty bias ("this is ugly, let's rebuild") and the political cost of being the person who says the system everyone built is done — because the organisation agreed on the rule before anyone's work was on the line.
When a tripwire fires, the default response is the smallest redesign that changes the slope, not the largest one that changes the diagram. Strangle the one seam that carries most of the wide-bucket changes; move the regulated surface behind a boundary; re-partition the one table whose fan-out drives the cost curve. Measure the slope again two quarters later. Full re-architecture is the answer only in the pink quadrant, and even there it is delivered as a sequence of seam replacements with the old system serving traffic throughout — Brooks's second-system effect is at its most dangerous precisely when the team is most convinced the old system has nothing to teach.
4
Measures, plus an assumption register
3 quarters
Of slope before believing it
Pre-agreed
Tripwires, not debates
Smallest
Redesign that changes the slope
Architect's note · reporting upward
A tripwire tells you the slope is real. It does not tell you which response to fund, and the choice between a seam redesign and a full rewrite is where most of the money in this chapter is lost. The comparison has to be done in the only currency both options share — engineer-months — and it has to include the months the redesign itself consumes while the old system keeps compounding. The chart below works it through for an illustrative composite: a 60-engineer product organisation whose drag (time lost to coordination, rework, incident follow-up and wide-bucket queueing) sits at 15% of capacity and has been growing about three points a quarter.
Assumptions: 60 engineers (180 eng-months a quarter); drag starts at 15% of capacity and grows 3 points a quarter. The seam redesign stops the growth and returns drag to ~11%; the rewrite returns it to ~8% but runs dual systems while it is built, and drag keeps compounding on the old one until cut-over. The schedule, not the end state, decides the rewrite's case: a two-quarter slip turns a marginal win into a loss inside the horizon.
Three things fall out of the arithmetic that the usual debate misses. First, the seam option wins not because it is cheap but because it is early: six engineers for two quarters stops the compounding in Q3, and by Q12 it has consumed less than half the engineer-months of doing nothing. The rewrite delivers a better end state — drag near 8% rather than 11% — but spends four quarters paying for both systems while drag on the old one keeps rising, and breaks even only around Q11. Second, the rewrite's case is almost entirely a schedule bet. Stretch its build from four quarters to six and it is still 150 engineer-months behind doing nothing at the three-year mark; stretch it to eight and it never pays back inside any horizon a leadership team will commit to. Ask anyone who has shipped a large rewrite whether four quarters became six, and price that answer in before the decision, not after. Third, the gap between the seam and the rewrite end states — three points of capacity, about five engineer-months a quarter here — is the most the rewrite can ever earn back. A plan that spends 240 engineer-months to win five a quarter needs 48 quarters to recover the spend on end-state alone; it only looks viable because it also stops the compounding, which the seam does too.
The inputs are all estimable from data you already have: drag from the breadth-bucketed lead time and the calendar sample in 5.4, its growth from three quarters of slope, the redesign cost from a sized plan. When someone presents a rewrite without this table, the missing column is almost always the dual-running period.
Called too early (level, novelty)
M0
Rewrite approved on “legacy pain”; no slope data
M3
New system meets the spec; old one encoded rules nobody wrote down
M6
Parity treadmill: every feature lands twice; dual-write reconciliation
M12
Easy 70% migrated; the long tail is the hardest tenants and flows
M18
Sponsor moves on; priority drops; cut-over date slides
M24+
Two systems in production, both load-bearing, one frozen
Cost per change is now higher than before the ‘fix’.
Called too late (habituation)
M0
Wide-bucket slope rising; CFR held flat by new gates
M3
Roadmap suppression: “it touches billing” items pile up
M6
Top engineers >⅓ in syncs; the first of them leave
M12
Major incident; freeze; the redesign becomes an order
M18
Rewrite under freeze by people without the old theory
M24+
Competitor ships the market you could not reach
The window closed at around M9. Nobody noticed it close.
Illustrative composites; month markers are typical shapes, not measurements. The two failures are asymmetric: an early call wastes capacity but preserves the option of stopping; a late call spends the option itself, because the people and the spare capacity needed to redesign are gone by the time the case is obvious.
The errors are not symmetric, and that should shape your priors. A redesign called too early typically fails through lost theory: the old system encoded hundreds of decisions — a rounding rule for one region, a retry that exists because a partner's API lies about idempotency, an ordering dependency in month-end close — and the new system rediscovers them one incident at a time. It also fails through the parity treadmill: while both systems run, every feature ships twice or the migration stalls, and a migration that is 70% done for a year is more expensive than one that is 0% or 100% done, because both code paths are load-bearing. But an early call can be stopped; you lose capacity, not the option. A late call spends the option itself. By the time the dashboards agree, the engineers with the theory have left, the spare capacity is consumed by drag, and the redesign is done under a freeze by people who never ran the old system. Given the choice of which error to risk, risk the early one — and then bound it by always choosing the seam before the rewrite.
Signal that moves first
Slope evidence to act
Illustrative tripwire
Default response
Pre-PMF, <15 eng
Scale-up, 30–150 eng
Enterprise, 300+ eng
Regulated surface
Thresholds are illustrative starting points to be calibrated against your own history. The pattern is the point: the smaller the organisation, the more the decision rests on assumptions, and the larger it is, the more on coordination — because at scale most of the cost of a wrong boundary is paid in people.
Context changes the thresholds more than the method. In a pre-product-market-fit company the metrics are too short and too noisy to trend, the team is small enough that coordination cost is a conversation, and the dominant signal is an expired assumption — the product pivoted from self-serve to enterprise and the single-tenant, single-region design is suddenly wrong. Redesign there is cheap and should be quick. In a scale-up the four measures are at their most informative and the congruence matrix is the most actionable, because team lines are still being drawn. In a large enterprise every signal has three lookalikes and every reorg perturbs the data, so demand longer evidence — and expect the redesign to be a platform or domain carve-out delivered over years. In regulated domains, compliance scope dominates everything: the share of deploys that fall inside audit scope is the tripwire, and shrinking it is usually the highest-return architecture work available.
Where the conventional advice is wrong
Questions to ask in a design review
Where next