Composite SLOs and dependency availability math
What it is
The arithmetic for computing a service's achievable availability from its dependencies', and the design decisions that follow from it.
Two combining rules, and everything else is an application of them:
SERIAL (all required) A_total = A_1 × A_2 × ... × A_n
Availability MULTIPLIES DOWN.
PARALLEL (any suffices) A_total = 1 - (1-A_1)(1-A_2)...(1-A_n)
UNavailability multiplies down.
Three dependencies at 99.9% each:
Serial: 0.999^3 = 99.70% (worse than any one)
Parallel: 1 - 0.001^3 = 99.9999% (far better than any one)
Commonly confused with a modelling nicety. The serial rule is why a service composed of several 99.9 percent dependencies cannot itself be 99.9 percent, and that is a hard ceiling you discover either by arithmetic or by missing your SLO.
Also commonly confused: the rules assume independence, and real failures are correlated. Two replicas in the same rack, two regions on the same provider, or two services calling the same database are not independent, and the parallel formula over-states the benefit substantially. Naming that assumption is the difference between using the arithmetic and believing it.
The problem it solves
Availability targets are usually set by aspiration and then discovered to be impossible.
"The API should be 99.99% available."
It calls:
auth service 99.95%
user database 99.99%
product catalogue 99.9%
recommendations 99.5%
payments provider 99.9%
All required? 0.9995 × 0.9999 × 0.999 × 0.995 × 0.999
= 99.33%
*** The ceiling is 99.33%. The target is 99.99%. ***
*** It is not 66x away, it is unreachable by construction. ***
The arithmetic ends the conversation and, more usefully, points at the fix: the recommendations service at 99.5 percent costs more availability than everything else combined, and it is the least important dependency in the list.
Mechanics
Working the serial chain
Contribution of each dependency to total UNavailability:
dependency A U = 1-A share of total U
----------------------------------------------------------
recommendations 99.5% 0.00500 74.7%
auth 99.95% 0.00050 7.5%
catalogue 99.9% 0.00100 14.9%
payments 99.9% 0.00100 14.9% (see note)
user database 99.99% 0.00010 1.5%
----------------------------------------------------------
total U ≈ sum of U (for small U) = 0.00760 -> A = 99.24%
The approximation worth knowing: for small unavailabilities, total unavailability is approximately the sum, because the cross terms are second-order. That makes the arithmetic doable in your head:
0.9995 × 0.9999 × 0.999 × 0.995 × 0.999
≈ 1 - (0.0005 + 0.0001 + 0.001 + 0.005 + 0.001)
≈ 1 - 0.0076 = 99.24%
Exact: 99.243%. The approximation is accurate to three
decimal places, and it is far faster.
And it makes the design conclusion obvious: unavailability adds, so the worst dependency dominates. Improving the 99.99 percent database to 99.999 buys 0.00009; removing the 99.5 percent dependency from the critical path buys 0.005, which is fifty times more.
Removing a dependency from the critical path
The highest-leverage move, and it is usually cheap.
BEFORE: recommendations required
A = 99.24%
AFTER: recommendations optional, with a cached fallback
Its failure no longer fails the request; the page renders
with popular items instead.
A = 0.9995 × 0.9999 × 0.999 × 0.999 = 99.74%
*** +0.5 percentage points, which at 99.24% is going from
5.5 hours of downtime a month to 1.9. ***
Cost: a fallback path and a cache. Days, not quarters.
The general principle: every dependency on the critical path multiplies its unavailability into yours, so the question for each one is "what happens if this is down" and the answer should not be "the request fails" unless it genuinely must.
The classification to apply to every dependency:
REQUIRED the request is meaningless without it
(auth for a private resource, the database
for the primary data)
DEGRADABLE the request works less well without it
(recommendations, related items, personalisation)
ASYNCHRONOUS the request does not need it at all
(analytics, audit logging, notifications)
Most dependencies teams treat as required are degradable, and reclassifying them is where the availability is.
Parallel redundancy, and the independence trap
Two payment providers, either sufficient, each 99.9%:
A = 1 - (0.001)^2 = 99.9999%
That number is almost certainly wrong.
The correlation correction:
If a fraction c of failures are COMMON to both (a shared
network path, a shared upstream, a correlated traffic spike):
A_effective ≈ 1 - [ c·U + (1-c)·U² ]
With U = 0.001 and c = 0.10 (10% of failures are common):
= 1 - [0.10 × 0.001 + 0.90 × 0.000001]
= 1 - 0.0001009
= 99.99%
*** Not 99.9999%. Two orders of magnitude worse, from a 10%
correlation. ***
That sensitivity is the point. Redundancy's benefit is dominated by the correlated fraction, not by the individual availabilities, which means the engineering that matters is reducing correlation, not adding a third replica.
Sources of correlation people miss:
same availability zone -> power, network, cooling
same cloud provider -> control plane, IAM, DNS
same deployment pipeline -> a bad deploy hits both
same configuration source -> a bad config hits both
same certificate authority -> expiry hits both
same upstream dependency -> the shared thing fails
correlated demand -> the spike hits both
A deploy pipeline shared between two "redundant" regions is the one that catches people, because it looks like redundancy on the architecture diagram and a bad rollout takes both regions simultaneously.
Composite SLOs: the budget-sharing question
When several services contribute to one user journey, how is the error budget divided?
User journey: "search and add to cart"
frontend -> search API -> ranking -> cart API -> database
Journey SLO: 99.9% (43 minutes per 30 days)
OPTION A: EQUAL SPLIT
Each of 5 services gets 1/5 of the budget: 8.6 minutes.
Each needs 99.98% individually.
Simple, and it ignores that services have different
difficulty and different traffic shares.
OPTION B: PROPORTIONAL TO DIFFICULTY
The database is easier to keep up than the ranking service,
which depends on a model server and a feature store.
Allocate: db 3 min, cart 5 min, search 10 min, ranking 20 min,
frontend 5 min.
More realistic, and it needs negotiation.
OPTION C: MEASURE THE JOURNEY DIRECTLY
Instrument the end-to-end journey and set ONE SLO on it.
Individual services have their own SLOs for their own
operational purposes, and the journey SLO is what the
business commits to.
*** This is the right answer, and it requires
journey-level instrumentation that most systems lack. ***
Why option C wins: the sum of component SLOs is not the journey SLO, because failures are correlated (one dependency taking down three services), because not every request touches every service, and because a component's SLO measures its own requests rather than the journey's.
Concretely: the ranking service is 99.9% available, and 40%
of searches do not call it because they hit the cache. Its
unavailability contributes 0.4 × 0.001 to the journey, not
0.001.
Weighting by the fraction of journeys that actually touch a dependency is the correction most composite calculations omit.
Where the arithmetic misleads
1. IT ASSUMES BINARY UP/DOWN.
Real services degrade: slow, partially failing, failing
for one tenant. A service at "99.9% available" may be
serving 99.9% of requests successfully while being
unusable for 5% of users.
2. IT ASSUMES INDEPENDENCE.
Addressed above, and it is the largest error.
3. IT IGNORES RETRIES.
A dependency at 99.9% per attempt, with one retry and
independent failures, is 99.9999% per logical call. Retries
are cheap redundancy and they change the arithmetic
substantially, which is why they belong in the model.
4. IT IGNORES TIMEOUTS AND FALLBACKS.
A dependency that is "down" but whose failure is detected
in 50 ms and served from cache has not caused an outage.
The availability that matters is the availability of the
USER-VISIBLE OUTCOME, not of the dependency.
Point 4 is the reframe: you are not composing dependency availabilities, you are composing the availability of outcomes, and a fast failure with a good fallback contributes almost nothing to unavailability.
A worked example: raising a ceiling
STARTING POINT
API SLO target: 99.9%. Measured: 99.31%. Consistently missing.
STEP 1: compute the ceiling.
auth 99.95, db 99.99, catalogue 99.9, recs 99.5, payments 99.9
All on the critical path.
U_total ≈ 0.0005 + 0.0001 + 0.001 + 0.005 + 0.001 = 0.0076
A_ceiling = 99.24%
*** The SLO was never achievable. We were not failing to
execute; we had committed to arithmetic that does not
work. ***
STEP 2: rank by contribution to unavailability.
recs 0.005 66% <- and it is the least important
catalogue 0.001 13%
payments 0.001 13%
auth 0.0005 7%
db 0.0001 1%
STEP 3: reclassify.
recs REQUIRED -> DEGRADABLE. Cached popular items on
failure. 3 days of work.
payments REQUIRED at checkout, NOT required for browsing.
Split the SLO: the browse journey does not
include it.
catalogue REQUIRED, but 60% of requests are served from
cache, so its effective contribution is
0.4 × 0.001 = 0.0004.
STEP 4: recompute.
Browse journey:
U ≈ 0.0005 (auth) + 0.0001 (db) + 0.0004 (catalogue, weighted)
= 0.0010
A = 99.90% <- now exactly at the target, with no margin
Checkout journey:
U ≈ 0.0010 + 0.001 (payments) = 0.0020
A = 99.80%
-> Set a SEPARATE, lower SLO for checkout. It is a
different journey with a different dependency set and
pretending it is the same journey was part of the
original error.
STEP 5: buy margin where it is cheapest.
auth is 0.0005, half the remaining browse budget, and it is
a single-region deployment. Making it multi-AZ takes it to
~99.99% for a week of work.
New browse U ≈ 0.0001 + 0.0001 + 0.0004 = 0.0006 -> 99.94%.
Now there is 0.04 points of margin against the 99.9% SLO.
Two findings worth stating from that. The original SLO was arithmetically impossible, so the team's failure to meet it was a planning error rather than an execution one, and that reframing matters for how the conversation goes. And splitting one SLO into per-journey SLOs was as valuable as any reliability work, because browse and checkout have genuinely different dependency sets and a single number for both was wrong for each.
Production evidence
Google Cloud's and Azure's composite SLA documentation both state that the combined SLA of services used together is the product of the individual SLAs, and provide worked examples. It is the serial rule as vendor guidance.
Google's SRE Workbook, chapter 2, covers SLOs for services with dependencies and makes the point that a service cannot be more available than the product of its critical dependencies.
AWS's well-architected reliability pillar documents the correlated-failure problem explicitly in its guidance on multi-AZ and multi-region design, including that a shared deployment pipeline undermines regional independence.
The 2021 Facebook BGP outage and the 2021 AWS us-east-1 control-plane events are the canonical illustrations of correlation: redundant infrastructure that failed together because it shared a control plane, and in Facebook's case because the recovery tooling depended on the network that was down.
Netflix's and Amazon's published work on graceful degradation documents the degradable-dependency classification in production, with Netflix's fallback hierarchy being the reference example.
The debate
The case for computing composite SLOs formally: it prevents committing to impossible targets, it ranks improvement work by actual contribution rather than by intuition, and it turns "should we make X more reliable" into arithmetic.
The case against: the model assumes binary up/down and independent failures, both of which are false, so the number is confidently wrong. Real availability is determined by correlated failures and partial degradation, neither of which the formula captures, and a precise wrong number is worse than an admitted uncertainty.
The case for measuring the journey instead: skip the composition entirely and instrument the end-to-end user journey. That is what the business cares about, it captures correlation and degradation automatically, and it does not require a model.
My position: use the arithmetic to find the ceiling and to rank the work, measure the journey to set the SLO.
The arithmetic's value is diagnostic rather than predictive. Computing that the ceiling is 99.24 percent against a 99.9 percent target tells you the commitment was impossible, which is a different conversation from "we keep missing our SLO", and it ranks the fixes: the recommendations dependency was 66 percent of unavailability and the least important service in the list. That ranking is worth having even though the absolute number is unreliable.
The approximation I would always use is that for small unavailabilities, total unavailability is the sum, because it makes the arithmetic doable in a meeting and it is accurate to three decimal places at these values. And it makes the design conclusion immediate: unavailability adds, so the worst dependency dominates and improving the best one is nearly worthless.
The correction I would insist on is the independence assumption. Two 99.9 percent providers in parallel is 99.9999 percent only if failures are independent, and at a 10 percent correlated fraction it is 99.99 percent, two orders of magnitude worse. So the engineering that matters is reducing correlation, not adding replicas, and the correlation people miss most often is a shared deployment pipeline, which looks like redundancy on the diagram and fails both regions on one bad rollout.
The move I would make before any of the reliability work is reclassifying dependencies as required, degradable or asynchronous. Most dependencies teams treat as required are degradable, and moving one off the critical path bought half a percentage point in days where a quarter of reliability engineering on the others would have bought a fraction of that.
And I would split by journey. Browse and checkout have different dependency sets and one SLO for both is wrong for each, which is a modelling error that looks like an execution problem.
Follow-up Q&A
"How do you compute a service's achievable availability?" Serial dependencies multiply, so all-required availability is the product. Parallel redundancy multiplies the unavailabilities. And for the small numbers involved, total unavailability is approximately the sum of the individual unavailabilities, which makes it doable in your head: five dependencies at 0.0005, 0.0001, 0.001, 0.005 and 0.001 sum to 0.0076, so the ceiling is 99.24 percent. That is accurate to three decimals against the exact product.
"What does that tell you to do?" Rank by contribution to unavailability, because it adds, so the worst dependency dominates. In that example the recommendations service at 99.5 percent was 66 percent of total unavailability and was also the least important service in the list. Improving the database from 99.99 to 99.999 would have bought 0.00009; making recommendations optional bought 0.005, fifty times more, for a few days of work.
"So the fix is a fallback?" Usually, and I would frame it as reclassification. Every dependency is required, degradable or asynchronous, and most that teams treat as required are degradable. The question for each is "what happens if this is down", and the answer should not be "the request fails" unless it genuinely must. Moving one dependency off the critical path is routinely worth more than a quarter of reliability engineering on the ones that stay.
"Two providers at 99.9 percent each. What's the combined availability?" 99.9999 percent if the failures are independent, and they are not. With a 10 percent correlated fraction, a shared network path or upstream or deploy pipeline, it is about 99.99 percent, two orders of magnitude worse. That sensitivity is the point: redundancy's benefit is dominated by the correlated fraction rather than by the individual availabilities, so the engineering that matters is reducing correlation, not adding a third replica.
"What correlations do people miss?" Shared availability zone, so power and cooling. Shared cloud provider, so control plane, IAM and DNS. Shared certificate authority, so an expiry hits both. Shared upstream. Correlated demand, so the spike hits both. And the one that catches people most: a shared deployment pipeline, which looks like redundancy on the architecture diagram and takes both regions out on one bad rollout.
"How do you set an SLO for a multi-service user journey?" Measure the journey directly rather than composing component SLOs, and give components their own SLOs for their own operational purposes. Composition is wrong for three reasons: failures are correlated, not every request touches every service, and a component's SLO measures its own requests rather than the journey's. Concretely, if 40 percent of searches skip the ranking service because they hit cache, its contribution is 0.4 times its unavailability, and that weighting is the correction most composite calculations omit.
"Where does the arithmetic mislead you?" Four places. It assumes binary up or down, and real services degrade, so a service at "99.9 percent available" may be serving most requests while being unusable for five percent of users. It assumes independence, which is the largest error. It ignores retries, which are cheap redundancy that changes the numbers substantially. And it ignores timeouts and fallbacks, which is the reframe: you are composing the availability of outcomes, and a dependency that fails in 50 milliseconds into a good fallback contributes almost nothing.
"Your SLO is 99.9 and you keep missing it at 99.31. What do you do first?" Compute the ceiling, because the likely finding is that the SLO was never achievable, which is a completely different conversation from "we keep failing to execute". In the case I worked the ceiling was 99.24 against a 99.9 target, so the team had committed to arithmetic that does not work. Then rank by contribution, reclassify dependencies, and split by journey, because browse and checkout had different dependency sets and one SLO for both was wrong for each.
Common misconceptions
"Adding a replica doubles reliability." Only if failures are independent. A 10 percent correlated fraction removes most of the benefit.
"The composite SLO is the sum of component SLOs." Failures correlate, not every request touches every component, and component SLOs measure their own traffic. Measure the journey.
"Improve the least reliable dependency." Correct, but the stronger move is usually to remove it from the critical path entirely.
"99.9 percent availability means it works 99.9 percent of the time for everyone." It can mean it works perfectly for 95 percent of users and not at all for 5 percent.
"Multi-region means independent." Not with a shared control plane, a shared deploy pipeline or a shared configuration source.
Interview delivery note
Give the two rules and the approximation that makes them usable in a meeting: "Serial dependencies multiply, parallel redundancy multiplies the unavailabilities. And for small numbers, total unavailability is approximately the sum, which is accurate to three decimals and means you can do it in your head."
Then use it to reach a conclusion rather than stating it abstractly: "So five dependencies at 99.95, 99.99, 99.9, 99.5 and 99.9 sum to 0.0076 unavailability, so the ceiling is 99.24 percent. If the SLO is 99.9, it was never achievable, which is a different conversation from 'we keep missing it'."
Then the ranking and the move it produces: "And because unavailability adds, the worst dependency dominates. Recommendations at 99.5 was two thirds of total unavailability and the least important service in the list. Making it degradable with a cached fallback bought half a percentage point in three days; improving the database from four nines to five would have bought 0.00009."
Volunteer the independence correction, because it is the largest error in the model: "and I'd be careful with parallel redundancy. Two providers at 99.9 is 99.9999 only if failures are independent. At a ten percent correlated fraction it's 99.99, two orders of magnitude worse. So the work that matters is reducing correlation, and the one people miss is a shared deploy pipeline, which looks like redundancy on the diagram and takes both regions out on one bad rollout."
Close on the modelling point: "and I'd split by journey. Browse and checkout had different dependency sets, and one SLO for both was wrong for each. That was a modelling error that looked like an execution problem."
Further reading
- Beyer et al., The Site Reliability Workbook, chapter 2, on SLOs with dependencies.
- Google Cloud's composite SLA documentation, for the multiplication rule as vendor guidance.
- The AWS Well-Architected reliability pillar, on correlated failure and independence.
- Netflix's engineering write-ups on graceful degradation and fallback hierarchies.
- The public post-incident reports for the 2021 Facebook BGP outage, as the canonical correlated-failure case.