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.

Golden path template feeding a service that is held in place by paved-road checks A versioned golden-path template scaffolds a service, which is then continuously evaluated by paved-road policy checks that either confirm conformance or open a remediation task. Golden-Path template v2.4.0 Scaffolded service repo Paved-Road policy checks Conformant badge: passing Remediation task opened
The template defines the road; checks keep the service on it, opening remediation when it drifts.

Prerequisites & Environment Baseline

A paved road is only enforceable when four pieces exist: a scaffolder to deliver the template, a policy engine to evaluate rules, CI credentials to read repos, and catalog ownership data the rules can assert against.

Prerequisites for golden paths and paved roads A scaffolder, a policy engine, CI credentials, and catalog ownership data make a paved road enforceable. scaffolder backend policy engine (OPA) CI OIDC creds catalog ownership Road enforceable template + checks
Ownership data is what lets a paved-road rule assert "this service has a resolvable owner" — the most common check.
  • Backstage scaffolder: @backstage/plugin-scaffolder-backend@^1.22.0 with 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 and catalog-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 Group entities resolvable in the catalog.
  • CLI toolchain: @backstage/cli@^0.27.0 and yamllint for local template validation.

Step-by-Step Configuration & Plugin Architecture

Configuration is four moves: define a minimal-parameter template, bind it to a versioned base skeleton, encode the standards as policy that versions alongside it, and restrict who can see each path. The diagram shows how the parts relate.

Golden-path configuration parts A minimal-parameter template, a versioned base skeleton, co-versioned policy rules, and RBAC visibility. Template few parameters Versioned base skeleton dir Policy rules co-versioned RBAC visibility who may use
Co-versioning the policy with the template means the road and its checks can never drift out of sync.

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 in a sequence that mirrors production: schema-validate the template, dry-run it to generate output, run the paved-road policy against that output, and confirm no placeholder survived the render.

Golden-path validation sequence Schema validation, a dry-run, a conftest policy check, and a placeholder scan validate a golden-path template. Schema validate 0 errors Dry-run generate output Policy check conftest PASS Placeholder scan clean render
Running the same policy in validation that the service will face in production is what makes the road genuinely "paved".
# 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

Lifecycle management is a tag-driven loop: a new revision is promoted only after it passes validation, a bad one is rolled back by moving the ref, and adoption metrics decide which paths to keep investing in.

Tag-driven golden-path lifecycle Validate a revision, promote the ref on success, roll back by moving the ref, and let adoption metrics guide investment. Validate revision sample service Promote ref ?ref=new tag Rollback ref → last passing Adoption metrics invest / retire
Because developers pin a ref, a bad revision never reaches them until it has passed validation on a sample service.
  • 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=debug on the backend to trace fetch:template and publish:* 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

The golden-path pitfalls divide into template-design mistakes (too many parameters, coupled skeleton) and enforcement mistakes (checks only at creation, in-place edits). The diagram groups the four so the fix lands in the right place.

Golden-path pitfalls by class Too many parameters and a coupled skeleton are design mistakes; creation-only checks and in-place edits are enforcement mistakes. Template design too many params skeleton coupled → default + separate dir Enforcement check at creation only edit in place → recheck + tag refs
Re-running checks on every PR and pinning refs turns a one-time gate into a road that stays paved.
  • 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:template so 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.