Your Platform Serves 95% of Requirements and 60% of Services
A platform that handles any individual requirement 95% of the time fully serves about three services in five, because coverage multiplies rather than averaging. That gap is why exception requests never stop arriving, and why an escape hatch is structural rather than a concession.
Here is a conversation that happens on every platform team eventually.
Someone points out that the platform works. The golden path produces a running service. The contract covers the common cases. Teams that use it are productive, and the ones who have adopted it say good things. And yet the team is fielding exception requests constantly, several teams are running things outside the platform, and adoption has stalled somewhere around sixty percent.
The usual explanations are that the remaining teams are difficult, or that the platform needs better documentation, or that adoption needs a mandate from someone senior.
There is a simpler explanation, and it is arithmetic.
The multiplication nobody does#
Suppose your platform handles any individual requirement correctly with probability p. Not catastrophically, just well enough that a team does not need something you do not offer. And suppose a typical service has n requirements.
A service is fully served only when every one of its requirements is handled. So the probability is not p. It is p multiplied by itself n times.
P(service fully served) = p^n
p = 0.95, n = 10 -> 0.60
p = 0.95, n = 20 -> 0.36
p = 0.99, n = 10 -> 0.90
p = 0.99, n = 20 -> 0.82
Ninety five percent per requirement, which sounds excellent, produces a platform that completely serves three services in five.
Two services in five need something you do not have. Not because those teams are awkward, and not because your platform is bad, but because requirements compound and intuition averages.
Why it feels like the platform works#
This is the part that makes the situation confusing from the inside.
Every individual interaction with the platform is fine. Pick any requirement at random and the platform handles it. Pick any team and most of what they need is covered. The engineers building the platform are looking at a system that does what it is supposed to do, because at the level of a single requirement it does.
The failure only appears when you aggregate across a whole service, and nobody experiences a whole service at once except the team that owns it.
Which is why the platform team and the service teams reach different conclusions from the same facts. The platform team sees ninety five percent. The service team sees the one thing out of ten that does not work, because that one thing is what is blocking them today.
What the ten requirements actually are#
The number is not arbitrary. Count what a real service needs from a platform:
build and produce an image
deploy, and roll back
configuration and secrets
routing and ingress
scaling behaviour
resource sizing
health checks
observability: metrics, logs, traces
network policy and egress
persistent state, if any
scheduled or background work
inter service authentication
That is twelve before anything unusual. A service with a cron job, an unusual health check, a sidecar required by a vendor agent, or a database with a specific extension has more.
So n between ten and twenty is the realistic range for the calculation, and n grows as your organisation acquires the kind of service that has requirements.
Two levers, and one is much better#
If p^n is the number to move, there are exactly two ways to move it.
Raise n's tolerance, which means reducing what a service needs from you. Mostly not available, because the requirements come from the services rather than from your design.
Raise p. This is the one that works, and the arithmetic says something specific about it.
0.95 -> 0.99 full coverage goes from 60% to 90%
the same four percentage points that moved one
requirement from good to excellent moved the fleet
from two thirds served to nine tenths
That is the case for depth over breadth. A platform team choosing between supporting a new class of workload and making an existing capability genuinely complete should usually pick the second, because compounding rewards it disproportionately. Breadth adds to n. Depth raises p.
It is also the argument against the roadmap most platform teams write, which is a list of new capabilities.
The escape hatch is structural, not a concession#
Here is the consequence that matters most.
Even at p = 0.99, one service in ten needs something the platform does not do. There is no realistic value of p where the remainder is zero, because p^n only reaches one when p does.
So the question was never whether some services will need an exception. It is whether those services get an exception inside the platform, where you can see it, or outside it, where you cannot.
A platform with no escape hatch does not have fewer unmet requirements. It has exactly the same unmet requirements, plus:
teams running things outside the platform entirely
patches smuggled through fields meant for something else
no record of what was needed
no way to rank what to build next
The last one is the expensive part. An instrumented escape hatch turns every gap into a datapoint attributed to a team and a service, with a reason attached. Forty patches with stated reasons usually collapse into six distinct requirements, of which two are worth promoting into the contract.
That is a roadmap derived from evidence, and it is available for the cost of a required text field.
Make the hatch measurable#
The mechanism matters less than the instrumentation. A strategic merge patch works well: it keeps the service inside the contract, it is specific enough to aggregate, and it fails loudly when the platform changes underneath it.
apiVersion: platform.example.com/v1
kind: Service
metadata:
name: checkout
spec:
owner: team-payments
runtime: java21
port: 8080
escapeHatch:
reason: >
Sidecar required for the legacy audit agent until the central
audit pipeline supports gRPC ingestion.
ticket: PLAT-4471
expires: "2027-03-01" # forces a review, not a removal
patch:
spec:
template:
spec:
containers:
- name: audit-agent
image: registry.example.com/audit-agent:2.4.1
The reason field is the one people want to make optional and the one that makes the data worth having. Without it you have a list of patches. With it you have a ranked list of things your contract is missing.
Then the aggregate query is the roadmap:
SELECT patch_target,
count(DISTINCT team) AS teams,
count() AS services,
groupArray(DISTINCT reason) AS reasons
FROM platform_escape_hatches
WHERE active
GROUP BY patch_target
HAVING teams >= 3
ORDER BY teams DESC;
Three or more teams patching the same thing is not three exceptions. It is a missing field, and the arithmetic above says it is worth more than it looks, because closing one gap raises p for every service that has that requirement.
The thresholds worth knowing#
The rate at which the hatch is used tells you which problem you have, and they need different responses.
one team patches one thing
normal. Help them, do not build anything
three or more teams patch the same thing
the contract is missing a field. Add it
a third of all services patch something
the abstraction is in the wrong place, and no
individual field will fix it
a team patches most of its generated object
they are not being served. Tell them to leave
That last one is worth saying plainly. A service whose every meaningful field is overridden gets nothing from the platform, and keeping it nominally inside the contract serves your adoption metric rather than that team. Recommending they eject is better platform work than pretending they are covered.
Which also means eject needs to exist as a supported operation. Counterintuitively, a documented way out makes adoption easier rather than harder: a team evaluating your platform is making a bet on constraints they cannot fully predict, and a cheap exit lowers the cost of being wrong. Teams that can leave are more willing to arrive.
What to measure#
Three numbers, and none of them is a survey.
The share of services with no exception at all. This is your real p^n, measured rather than assumed. If it sits near sixty percent, your per requirement coverage is around 0.95 whatever you believed it was.
count(platform_service_info{escape_hatch="false", exception="false"})
/ count(platform_service_info)
The escape hatch rate. Above roughly a third, stop adding fields and reconsider where the abstraction sits.
Services leaving. The strongest signal available, and unambiguous in a way no satisfaction score is.
A rising satisfaction score alongside falling adoption is not a contradiction, incidentally. Teams that left have no reason to answer your survey, so the score climbs as the unhappy population exits.
The part that is genuinely reassuring#
The arithmetic explains something that otherwise feels like failure.
A platform team fielding constant exception requests is not necessarily doing a bad job. At ninety five percent per requirement coverage, a steady stream of exceptions is the expected output of a well built platform, and treating each one as evidence of inadequacy leads to the wrong response, which is usually a mandate or a documentation project.
The right response is to instrument the exceptions, raise p where the same gap appears repeatedly, and accept that the remainder is permanent and should live somewhere you can measure it.
Sixty percent is not a failure. It is 0.95^10, and now you know which lever moves it.