PlatformORG-SPECIFICSCALE-SPECIFIC

Golden Paths

A recommended, supported route to production that removes toil without removing engineering judgement — and stays a path rather than becoming a cage.

The question, the obvious approach, and why it breaks

Every lesson starts where the work starts: an operational problem, a first attempt that is entirely reasonable, and the way production disagrees with it.

The production question

How do you make one supported way of building a service without forbidding every other way?

The problem

Teams need to make dozens of delivery decisions that have a right answer almost every time, and spending judgement on all of them leaves none for the decisions that are actually specific to their problem.

What teams do first

Pick the best stack, the best pipeline and the best deployment strategy, and standardise on them. One way to do everything is simpler for everyone.

How it breaks

The standard is chosen against today's services. The first service that does not fit — a batch job, a stateful store, a latency-critical path, an inherited system — has to either distort itself or go around, and going around is unsupported.

How it breaks in production
  • The standard is chosen against today's services. The first service that does not fit — a batch job, a stateful store, a latency-critical path, an inherited system — has to either distort itself or go around, and going around is unsupported.
  • Standardisation applied as prohibition converts every genuine exception into a negotiation with whoever owns the standard, which is a queue (Guardrails, Not Gates).
  • Teams that cannot get an exception ship one anyway and describe it as temporary. Now the organisation has a standard *and* undocumented divergence, which is worse than having neither.
  • A path with no exits stops receiving the information that would improve it: nobody leaves, so nobody reports what was missing.
CodeBuildTestArtifactReleaseDeployRunObserveOperateIncidentRecoverLearnImprove

What is actually happening

Underneath the tooling, which is the part that survives a change of tool.

  • A golden path is a *default*, and the useful property of a default is that taking it requires no decision while leaving it requires only a reason — not permission.
  • What the path removes is toil: the repetitive, automatable work of assembling a pipeline, wiring observability, choosing resource requests, writing the same deployment manifest again (Toil).
  • What the path must not remove is judgement: whether this workload should be a service at all, what its consistency requirements are, what its failure behaviour should be, what its blast radius is. Those are the decisions the team is uniquely positioned to make.
  • The difference between a path and a cage is what happens at the exit. On a path, leaving costs you the support the path provided — that is a real, self-enforcing cost. In a cage, leaving costs you an approval, which converts a technical trade-off into a political one.
  • Paths are plural. Most organisations need two or three — a request-serving service, an asynchronous worker, a scheduled job — and pretending one path covers all of them is how the exceptions start.

A path has an exit; a cage does not

This is the whole lesson, and it is a single structural difference. Both shapes have a recommended way of building a service. They differ in what happens when a team has a genuine reason to do something else.

The self-enforcing version is better not because it is more permissive but because the cost of leaving is *real and immediate to the team leaving* — they take on the patching, the pipeline, the alert wiring. That cost does not need an approver to apply it, and it is proportional to how far off-path they went.

What happens when a team needs something the path does not do
Cage
Team has a real constraint
  -> path does not support it
    -> exception request to platform team
      -> queued behind other requests
        -> approved in three weeks, or refused
          -> team distorts the design to fit,
             or ships an undocumented workaround
               -> platform team never learns what was missing
Path
Team has a real constraint
  -> path does not support it
    -> team leaves the path, no approval
      -> takes on: own pipeline, own base image
         patching, own alert wiring, own runbook
        -> the exit is recorded automatically
          -> platform team sees three teams left
             for the same reason and paves it

The cage moves a technical trade-off into an approval queue, where it is decided by whoever has capacity rather than by whoever understands the workload. The path leaves the trade-off with the team and attaches its true cost to it — which both keeps most teams on the path for good reasons and tells the platform team exactly where the path is too narrow.

Toil off, judgement on

GENERALThe categories transfer; the specific rows do not. In an organisation with a strong central SRE function, alert thresholds may sit with the path — and then the path owns the consequence of a bad threshold, which is the trade being made.

The qualifier that keeps a path healthy: it should reduce toil, not eliminate engineering judgement. Those are different categories of work, and a path that confuses them either fails to help or removes the team's ability to be responsible for its own service.

The test for any decision the path makes on a team's behalf: if this default is wrong for a service, will the team be the one paged for it? If yes, the team must be able to change it without leaving the path.

DecisionCategoryWho decidesWhy
Pipeline stages and their orderToilThe pathSame answer for nearly every service; no service-specific information improves it
Base image and patch cadenceToilThe pathFleet-wide property; per-team choices produce fleet-wide inconsistency
How rollout and rollback workToilThe pathMechanically identical across services; getting it wrong is expensive and unoriginal
Metrics, logs and trace wiringToilThe pathShould exist before anyone thinks to ask for it (Instrumentation: From Code to Signal)
Resource requests and limitsMixedPath default, team tunesDefaults get a service running; only the team knows its actual profile (Requests and Limits)
Deployment strategy for this changeJudgementThe teamDepends on blast radius and reversibility of the specific change (Blast Radius: If This Is Wrong, How Much Does It Affect?)
Consistency and durability requirementsJudgementThe teamA property of the problem, not of the delivery mechanism
What to alert on, and at what thresholdJudgementThe team, on path-provided signalsThe path can supply the signal; only the owner knows what constitutes user impact (Alert on Symptoms, Not on Causes)
Whether this should be a service at allJudgementThe teamThe path makes services cheap, which is exactly why it must not be the reason one exists

Reading the exits

Every departure from the path carries a reason, and the reasons sort into categories that call for different responses. Treating all of them as non-compliance loses the information; treating all of them as missing features leads to a path that tries to cover everything and covers nothing well.

Why teams leave, and what each departure means
TriggerSymptomCauseResponse
The path has no shape for this workloadA batch job forced into a request-serving template, with a health check that means nothingOne path is being stretched across genuinely different shapesAdd a second path for the shape; do not widen the first one (Service Templates)
The path is slower than doing it manuallyTeams scaffold with the platform then abandon itPath optimised for coverage rather than for feedback timeTreat it as a defect and measure it (Developer Experience as an Operational Metric)
A default cannot be overriddenA latency-critical service leaves entirely to change one settingPath confused an opinion with a constraintMake the setting a parameter with a default, not a fixed value
An inherited system predates the pathThe oldest and most critical service is off-path and untrackedMigration never fundedTrack it explicitly as off-path; deliver fleet-wide fixes to it by another route rather than pretending it is covered
A genuine regulatory constraintOne service needs separated duties the path does not modelLegitimate; the path is not wrongDocument it as a permanent supported exception with a named owner
Preference, not constraintA team leaves because it prefers different toolingAlso legitimate, and it costs them the supportLet them, record it, and check in a year whether the cost changed their mind

How to do it properly

Most important first.

  • Pave the path teams already walk. Watch what people actually build, then make the common case a default rather than designing the case you wish they had.
  • Publish what the path covers and, explicitly, what it does not. An honest boundary is what makes leaving it a considered choice rather than a discovery.
  • Make leaving a documented transfer of responsibility: the team keeps its own pipeline, patching, alert wiring and runbook. No approval, a clear bill.
  • Keep judgement on the path. The path should not decide consistency requirements, error budgets or architecture — it should make the mechanical consequences of those decisions cheap to implement.
  • Treat every exit as product feedback. Three teams leaving for the same reason is a missing feature, not three non-compliant teams.
  • Version paths and migrate consumers deliberately, the way you would migrate an API's clients.

How much can this affect

Every production change has a blast radius. Stated as a scale so it is comparable between changes rather than adjectival — and paired with what actually contains it, because a wide scope with a real containment mechanism is a different situation from a wide scope with none.

Blast radius if this is wrongEveryone
One testEveryone
What contains it

A change to the default path affects every service created afterwards and every service that adopts the new version. Contained by versioning the path and letting existing consumers upgrade on their own schedule rather than being moved.

What can go wrong

Failure modes, including of the mitigation
  • The path becomes mandatory, and the exception process becomes the platform team's largest workload.
  • The path is so opinionated that teams on it cannot make ordinary engineering decisions — resource sizing, retry behaviour, deployment strategy — without leaving.
  • The path is optional but unmaintained, so it is neither the fastest route nor a supported one, and adoption decays into nostalgia.
  • Off-path services inherit none of the fleet-wide fixes, and nobody tracks that they are off-path at all.
  • One path is stretched to cover shapes it was not designed for, and it becomes the worst option for everybody.
Misreads this invites
  • "A golden path means one way to do things." It means one *default* way, which is a different claim: defaults are about where the burden of decision falls, not about what is permitted.
  • "Removing decisions is the goal." Removing *repeated mechanical* decisions is the goal. A path that also removes the design decisions leaves teams accountable for outcomes they no longer control.
  • "If teams keep leaving the path, they need to be brought into line." Teams leaving is the highest-quality feedback the platform gets. Suppressing it does not fix the path; it hides the reason.
  • "Off-path means unsupported means unimportant." The off-path services are frequently the oldest and most business-critical ones.

Operating it

Evidence is the signal, not the intention. Rollback is sometimes 'you cannot, and that is the point'.

How you know it worked
  • Most new services take the path without anyone asking them to.
  • You can list the services that are off-path, and for each one, the reason.
  • A team that left the path did so without filing anything, and knows exactly which responsibilities it took on.
  • Recent path improvements can be traced to specific exits.
How you get back
  • A team can return to the path. If rejoining requires a rewrite, the path is not a default — it is a platform lock-in with a friendlier name.
  • A path version that turns out to be worse can be un-defaulted for new services immediately, while existing consumers migrate on their own schedule.
What to automate, and what stays human
  • Automate everything the path does mechanically: scaffolding, pipeline, wiring, defaults, propagation of improvements.
  • Do not automate the enforcement of path membership. An automated block on leaving is the definition of a cage, and it produces divergence you cannot see rather than divergence you can.
  • Do automate *detection* of off-path services — that is not enforcement, it is knowing your fleet.
What this costs
  • Defaults chosen for the common case are wrong for the uncommon one, and the uncommon one is often the most important service you have.
  • Supporting exits costs the platform team real work — an off-path service still needs the fleet-wide security fix, delivered a harder way.
  • Multiple paths multiply maintenance; each one is a product with its own consumers and its own upgrade obligation.

Where this applies

This domain is unusually tool- and organisation-dependent. These labels say what each claim is specific to, and what a different platform, provider or organisation does instead.

  • ORG-SPECIFICWhether an exit requires approval is a policy choice, and in regulated environments some exits genuinely cannot be self-served — a change to how audit evidence is produced, for example. Where that is true, say so explicitly and keep the list of genuinely-gated exits short, rather than gating everything by default.
  • SCALE-SPECIFICWith one or two teams the path is whatever the last service did, and formalising it adds ceremony to a decision two people can make in a conversation. It becomes worth naming at the point where someone joins who was not in that conversation.

Where the depth lives

This domain teaches delivery and operation, and hands the mechanism off to the domain that owns it.

Architecturemicroservices
Domains that do not exist yet
  • Testing & Reliability Engineering — how much of the confidence a path provides comes from the checks it runs rather than from the defaults it sets.