Reliability as a User Promise

LESSON

Reliability Engineering Foundations

001 25 min intermediate

Reliability as a User Promise

By the end of this lesson, you will be able to...

  • Explain reliability as a promise about user outcomes, not as internal perfection.

  • Classify service facts as user promises, internal signals, or engineering controls.

  • Rewrite a vague reliability goal into a small promise that later lessons can measure with SLIs and SLOs.

Idea in one sentence: Reliability starts by saying what experience the service promises to protect for users, then using internal signals and engineering work to keep that promise honest.

Core Insight

Imagine a checkout API.

At 10:00, the dashboard says:

checkout-api availability: 99.98%
database CPU: 42%
error logs: normal

The internal view looks healthy.

But customers are writing support tickets:

"I clicked Pay, the spinner kept running, and I do not know if I was charged."

From the user's point of view, the service did not keep its promise.

This is the first reliability lesson:

Reliability is not the absence of server problems.
Reliability is the service keeping the user-visible promise often enough, clearly enough, and safely enough.

The word "promise" matters.

A promise has a subject:

What experience are we protecting?

A promise has a boundary:

For which users, requests, regions, or workflows?

A promise has evidence:

How would we know the promise is holding or breaking?

And a promise has a cost:

What are we willing to slow down, simplify, automate, or reject to protect it?

Without that promise, reliability work turns into a bag of disconnected actions: add alerts, add retries, add dashboards, add replicas, add process. Some of those actions may help. Some may make the system louder or more expensive without making the user experience better.

This track starts here because the next lessons need a stable object to measure. SLIs measure the promise. SLOs set the acceptable level. Error budgets decide when the promise is being spent too quickly. Alerts, capacity, degradation, reviews, and release safety all orbit the same question:

Are we still keeping the promise we made to users?

The Naive Idea: "The Service Is Up"

The first model many teams use is simple:

If the service is up, it is reliable.
If the service is down, it is unreliable.

This model is tempting because it is easy to check.

Can the process run? Can the load balancer reach it? Does /health return 200? Can a dashboard draw a green line?

Those facts are useful. They are not enough.

For a checkout API, "up" can hide many user-visible failures:

The API returns 200, but it takes 18 seconds.
The API accepts payment, but does not create the order.
The API creates the order, but shows an error page.
The API works for cached users, but fails for new cards.
The API works in one region, but not another.
The API responds, but duplicate requests charge twice.

In all of these cases, some internal component may honestly say:

"I am up."

But the user hears:

"The service did not do the job I came here to do."

That is why reliability needs to begin with the experience, not the machinery.

Plain meaning:

A reliable service keeps the important user promise within agreed limits.

In this scenario:

Checkout reliability means users can place an order, receive a clear result, and avoid unsafe duplicate charges within a reasonable time.

Technical name:

That user-centered promise becomes the foundation for service reliability work.

A Promise Is Not a Metric

A common confusion is to treat a metric as the promise.

For example:

"Our promise is p95 latency under 300 ms."

That is closer, but it is still not the whole promise.

The metric is evidence. It is not the user need itself.

Ask one more question:

Why does this latency matter?

For checkout, the answer might be:

Users should get a payment result quickly enough that they can trust the transaction and avoid retrying in confusion.

Now the metric has a job.

The service promise might be:

For normal checkout traffic, a user who submits a valid payment should receive a clear success or failure result within 3 seconds, and the system should not charge the same checkout attempt more than once.

That promise is not perfect. Real promises need sharper boundaries later. But it is already better than "keep checkout up."

It tells us what to protect:

It also hints at what we need to measure:

This is the bridge to the next lesson. A promise becomes useful when we choose signals that approximate what users feel. Those signals are SLIs.

Worked Classification

Take this small set of facts from the checkout API:

Fact What kind of thing is it? Why
"Valid checkout requests should return a clear result within 3 seconds." User promise It describes the experience the service is meant to protect.
"p95 checkout latency is 280 ms." Internal signal It is evidence about timing, but it is not the whole promise.
"Payment provider timeout increased to 2 seconds." Engineering control It changes system behavior to influence the promise.
"CPU on checkout pods is 78%." Internal signal It may explain risk, but users do not directly experience pod CPU.
"Retry payment authorization once with an idempotency key." Engineering control It is a mechanism used to protect the promise.
"Customers are unsure whether payment succeeded." User impact It says the promise is breaking from the user's point of view.

The important distinction is not "business people care about promises and engineers care about metrics."

Engineers also care about promises.

The distinction is:

Promise:
  the user-visible outcome we are trying to protect

Signal:
  evidence about whether the promise is holding

Control:
  an engineering action or mechanism that changes the system

A healthy reliability practice connects all three.

promise -> signal -> decision -> control -> changed user experience

A weak reliability practice gets stuck on one of them.

Only promise:

"Checkout must always work."

This is emotionally clear but operationally useless. Nobody can tell what to measure or when to stop shipping risky changes.

Only signal:

"p99 latency, CPU, request count, and errors are on a dashboard."

This produces visibility, but not judgment. The team can stare at graphs without knowing what user promise is in danger.

Only control:

"We added retries, autoscaling, alerts, and a circuit breaker."

This creates activity, but not necessarily reliability. A retry can protect a user from a transient failure, or it can overload a dependency and make the promise fail faster.

The useful model joins them:

We promise clear checkout results.
We watch signals that approximate that experience.
When the signals show the promise is at risk, we take specific actions.
Then we check whether the user experience improved.

Where Promises Break

Reliability promises break in uncomfortable places.

They break at boundaries between components:

checkout-api -> payment-provider
checkout-api -> inventory-service
checkout-api -> email-service

They break during partial failure:

The database is reachable, but slow.
The payment provider accepts a request, but the response is lost.
One region is healthy, another is saturated.

They break when the team optimizes the wrong thing:

Reduce alert count, but miss user impact.
Increase retries, but amplify load.
Ship faster, but spend the error budget.
Add redundancy, but keep the same bad deployment process.

They also break when the promise is too broad.

Compare these:

Weak:
  Checkout should be reliable.

Better:
  For normal card payments in the primary region, valid checkout requests should produce a clear success or failure result within 3 seconds, and one checkout attempt should not create duplicate charges.

The better version is less heroic. That is a feature.

It gives the team something inspectable. It leaves room for later promises:

What about international cards?
What about degraded mode when inventory is slow?
What about checkout during a payment-provider incident?
What about delayed email confirmation?

Reliability engineering often starts by making the promise smaller and clearer, not larger and more dramatic.

A quick review test helps:

Can a support engineer recognize the user harm?
Can an on-call engineer find evidence for it?
Can a product owner explain why the boundary is acceptable?
Can a backend engineer name one control that would protect it?

If the answer is no, the promise is probably still too vague.

For checkout, "be reliable" fails this test. "Return a clear checkout result within 3 seconds for valid card payments, and prevent duplicate charges for one attempt" passes it more often. Support can recognize confused or double-charged users. On-call can look for latency, ambiguity, and duplicate authorization signals. Product can debate the 3-second boundary. Engineering can discuss idempotency, timeouts, retries, fallback states, and release controls.

That does not make the promise final.

It makes the promise usable.

Trade-offs and Limits

A user promise improves focus.

It helps the team reject convenient but misleading goals:

"The health check is green, so users are fine."

It helps the team decide which signals matter:

"CPU is useful context, but checkout result clarity is closer to user impact."

It helps the team compare engineering actions:

"Will this retry policy protect the checkout promise, or just hide dependency failures until they become worse?"

But a promise also creates trade-offs.

First, a clearer promise can expose uncomfortable product choices. If the promise says checkout should complete within 3 seconds, the team must decide what to do when fraud checks take 8 seconds. Maybe the right answer is to show a pending state. Maybe it is to reject some risky transactions. Maybe it is to change the user flow.

Second, a promise can make engineering slower in the short term. The team may pause a release, add compatibility work, simplify a dependency path, or automate a manual recovery step. That cost is real.

Third, a promise can be too narrow. If the team only protects "successful checkout responses," it may ignore ambiguous failures where the user is charged but sees an error. The promise must include the outcomes that actually hurt trust.

Fourth, a promise can become stale. A service used by ten internal users can make a different promise than a service used by millions of paying customers. Reliability work needs review because the product, traffic, dependencies, and user expectations change.

The trade-off is:

Vague reliability goals are easy to agree with and hard to operate.
Precise reliability promises are harder to choose, but easier to measure, debate, and improve.

Common Confusions

Confusion: "Reliability means never failing."

No production service can promise that honestly. Reliability work decides which failures matter most, how often they are acceptable, how quickly they are detected, and what the service does when they happen.

Confusion: "Users only care about availability."

Users care about completing their task. Availability is one part. Latency, correctness, durability, clarity, and safety can matter just as much. A checkout service that is available but double-charges users is not reliable in the way users need.

Confusion: "Internal metrics are not user-centered, so they do not matter."

Internal metrics matter because they explain and predict user impact. The mistake is treating them as the final goal. CPU, queue depth, dependency errors, and saturation can be excellent signals when they are connected to the promise.

Confusion: "The reliability promise belongs to the SRE team."

The promise belongs to the service. Backend engineers, product owners, platform teams, support, and on-call responders all affect it. SRE practices can help make the promise explicit, measurable, and operational.

Check Your Understanding

Check: A team says, "Our reliability goal is Kubernetes pod uptime above 99.99%." What is missing?

Think first, then reveal.

Answer: Pod uptime is an internal signal. The statement does not say what user outcome is being protected. A better promise would name a user workflow, the expected result, the boundary, and the acceptable behavior when something goes wrong.

Check: The checkout API returns 200 quickly, but some users later discover they were charged twice. Did the service keep the reliability promise?

Think first, then reveal.

Answer: Not if the promise includes safe checkout completion. Fast responses are not enough. Correctness and user trust are part of the experience, so duplicate charges are a reliability failure for this workflow.

Practice

Rewrite this vague reliability goal:

The image upload service should be reliable.

Use this situation:

Users upload profile images.
Most files are under 5 MB.
The service stores the original image and creates thumbnails.
Sometimes thumbnail generation is slow.
Users mostly need to know whether the upload was accepted.

Write three things:

  1. A user promise.
  2. Two signals that approximate the promise.
  3. One engineering control that could protect the promise.

Model answer:

Promise:
  For valid profile images under 5 MB, the user should receive a clear upload
  accepted or rejected result within 4 seconds. If thumbnails are delayed, the
  profile should show a safe placeholder instead of making the upload look lost.

Signals:
  - percentage of valid uploads with a clear result within 4 seconds
  - rate of accepted uploads that still have no original image after 1 minute

Control:
  Store the original image before thumbnail generation, return "accepted" after
  durable storage succeeds, and process thumbnails asynchronously with visible
  placeholder behavior.

Your answer can differ. The important test is whether the promise names a user-visible outcome, the signals are evidence about that outcome, and the control changes system behavior in a way that protects the promise.

Resources

Key Takeaways

NEXT SLIs: Measuring What Users Feel