Golden Paths & Paved Roads
A golden path is the supported, opinionated route from a developer’s intent to a production-ready service, and a paved road is the set of automated checks that keep services on that route as they evolve.
This sub-section of Developer Experience & Self-Service Platforms covers how to encode that route as a maintainable artifact: a versioned template that scaffolds the right defaults, and a policy layer that continuously verifies a service has not drifted off the supported road. Done well, the golden path becomes the lowest-friction option available, so developers choose compliance because it is genuinely the easiest way to ship.
Prerequisites & Environment Baseline
- Backstage scaffolder:
@backstage/plugin-scaffolder-backend@^1.22.0with at least one registered template location. Golden paths are delivered as scaffolder templates; the Scaffolder Template Design sub-section is the authoritative reference for action sequencing. - A policy engine: Open Policy Agent (
open-policy-agent/opa@^0.63.0) or Conftest (open-policy-agent/conftest@^0.49.0) for evaluating paved-road rules in CI. Either can run against generated manifests andcatalog-info.yaml. - CI runners with provider credentials: OIDC-federated identity for
${GITHUB_TOKEN}so checks can read repository contents without a long-lived secret. - Catalog ownership data: Every paved-road check that asserts “this service has an owner” needs
Groupentities resolvable in the catalog. - CLI toolchain:
@backstage/cli@^0.27.0andyamllintfor local template validation.
Step-by-Step Configuration & Plugin Architecture
1. Define the golden-path template skeleton
Declare a Template whose parameters capture only the decisions a developer must make; everything else is an opinionated default supplied by the platform team.
# templates/microservice.yaml
# Requires scaffolder.backstage.io/v1beta3 (Backstage >= 1.22.0)
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: golden-path-microservice
title: Golden Path — HTTP Microservice
description: Supported route for a new Go HTTP service with CI, IaC, and ownership
tags: [golden-path, microservice, paved-road]
spec:
owner: group:platform-engineering
type: service
parameters:
- title: Service Identity
required: [name, owner]
properties:
name:
title: Service name
type: string
pattern: "^[a-z][a-z0-9-]{2,38}$"
ui:autofocus: true
owner:
title: Owning team
type: string
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: Group
2. Bind the template to a versioned base module
Keep the opinionated content — Dockerfile, CI workflow, IaC — in a versioned directory so a single bump propagates to all future services without rewriting the template.
steps:
- id: fetch-base
name: Render golden-path base
action: fetch:template
input:
url: ./skeleton
values:
name: ${{ parameters.name }}
owner: ${{ parameters.owner }}
registry: ${CONTAINER_REGISTRY}
The detailed authoring of this skeleton is covered in Creating a Golden Path Template for Microservices.
3. Encode paved-road rules as policy
Express each standard as an OPA rule that evaluates a generated artifact. Store these rules alongside the template so the road and its checks version together.
# policy/paved_road.rego
# Requires OPA >= 0.63.0
package pavedroad
deny[msg] {
not input.spec.owner
msg := "service must declare a spec.owner"
}
deny[msg] {
input.spec.lifecycle == "production"
not input.metadata.annotations["backstage.io/techdocs-ref"]
msg := "production services must publish TechDocs"
}
The full enforcement pipeline — running these in CI and gating merges — is documented in Enforcing Paved-Road Policies with Software Checks.
4. Restrict who can use each path
Scope template visibility through the permission framework so a golden path is offered only to teams that should adopt it, aligning with your Role-Based Access Control Setup.
# app-config.yaml
# Requires @backstage/plugin-permission-backend >= 0.5.0
permission:
enabled: true
Validation & Health Checks
# Validate the template and dry-run it, then check the output against policy
# Requires @backstage/cli >= 0.27.0, conftest >= 0.49.0
set -euo pipefail
npx @backstage/cli catalog validate --path ./templates/microservice.yaml
# expected: "Validated 1 entity ... 0 errors"
conftest test ./dry-run-output/catalog-info.yaml --policy ./policy
# expected: "PASS - ./dry-run-output/catalog-info.yaml - pavedroad"
Confirm the rendered output contains no unresolved placeholders:
grep -R '\${{' ./dry-run-output && echo "UNRESOLVED PLACEHOLDERS" || echo "clean render"
# expected: "clean render"
Maintenance & Lifecycle Management
- Upgrade path: Bump the
?ref=on the template location only after the new revision passes the validation pipeline against a sample service. - Rollback: Revert the catalog location ref to the last passing tag and restart the backend to clear cached template definitions.
- Debug commands: Set
LOG_LEVEL=debugon the backend to tracefetch:templateandpublish:*actions when a scaffold misbehaves. - Metrics: Track per-template adoption and dry-run failure rate; feed these into Developer Experience Metrics to decide which paths to invest in or retire.
Common Pitfalls & Mitigation Strategies
- Too many parameters. Root cause: pushing platform decisions onto developers. Fix: default everything that is not a genuine product decision; a golden path should ask three questions, not thirty.
- Policy that only runs at creation. Root cause: treating the paved road as a one-time gate. Fix: re-run paved-road checks on every pull request and on a schedule so drift is caught continuously.
- Skeleton coupled to the template. Root cause: opinionated files inlined in the YAML. Fix: keep the skeleton in a versioned directory referenced by
fetch:templateso it evolves independently. - Silent breaking changes. Root cause: editing a published template in place. Fix: tag every template revision and reference it explicitly via
?ref=.
Frequently Asked Questions
How is a golden path different from just having good documentation?
Documentation describes the right way; a golden path executes it. The value is that the supported route is also the path of least resistance — a developer runs the template and receives a working service rather than reading instructions and assembling one by hand. Paved-road checks then keep that service compliant over time, which documentation alone can never enforce.
Should every team be forced onto the golden path?
No. Golden paths work through gravity, not mandate. Make the supported route dramatically easier than the alternatives and most teams adopt it voluntarily; reserve hard enforcement (blocking merges) for the small set of non-negotiable controls such as ownership and security gates. Teams with genuinely unusual requirements should be able to deviate with a documented exception rather than be blocked.
How many golden paths should we maintain?
Start with one per dominant service archetype — typically an HTTP service, a worker/consumer, and a frontend. Each path is a maintained product, so resist proliferation; consolidate variants into parameters of an existing template before creating a new one.
Related
- Developer Experience & Self-Service Platforms — the parent guide
- Creating a Golden Path Template for Microservices — authoring the template skeleton
- Enforcing Paved-Road Policies with Software Checks — gating with policy in CI
- Scaffolder Template Design — the underlying template engine