Versioning and Deprecating Golden Paths
A golden path is a living product, not a one-time gift: the framework it scaffolds moves on, security requirements change, and last year’s template quietly becomes a liability. Without a versioning and deprecation discipline, you end up with a graveyard of half-current templates and services born onto paths you no longer support. This how-to versions golden-path templates, deprecates old ones cleanly, and guides teams to the current version. It extends Golden Paths & Paved Roads within Developer Experience & Self-Service Platforms.
Prerequisites
- Backstage 1.20+ with golden paths implemented as scaffolder templates.
- A version convention for templates — semantic versions or dated releases.
- A signal for template lifecycle state, so the portal can show current, deprecated, or retired.
- Adoption tracking, so you know who is on an old path before you deprecate it — see Tracking Golden Path Adoption.
Exact Configuration
-
Version the template and mark its lifecycle in metadata.
# template.yaml apiVersion: scaffolder.backstage.io/v1beta3 kind: Template metadata: name: nodejs-service-v2 title: Node.js Service (v2) tags: [recommended] annotations: backstage.io/template-version: '2.1.0' spec: lifecycle: production -
Deprecate the old version so it is discouraged but still visible to those already on it.
# template.yaml (old) metadata: name: nodejs-service-v1 title: 'Node.js Service (v1 — deprecated)' tags: [deprecated] spec: lifecycle: deprecated -
Point teams to the current version with a migration note in the deprecated template’s output.
# old template.yaml output: text: - title: This path is deprecated content: | Use **Node.js Service (v2)** for new services. Migration guide: /docs/golden-paths/nodejs-v1-to-v2
Validation
# Requires curl, jq
# 1. Exactly one version per path is recommended
curl -s "${PORTAL_URL}/api/catalog/entities?filter=kind=template" \
| jq '[.[] | select(.metadata.tags | index("recommended")) | .metadata.name]'
# Expected: one current template per path family
# 2. Deprecated templates are labeled, not deleted
curl -s "${PORTAL_URL}/api/catalog/entities?filter=kind=template" \
| jq '[.[] | select(.spec.lifecycle=="deprecated") | .metadata.name]'
# Expected: old versions present with deprecated lifecycle
# 3. No new services are created from a deprecated path
psql -c "SELECT template, COUNT(*) FROM scaffolder_events
WHERE at > now() - interval '30 days' AND template LIKE '%-v1' GROUP BY template;"
# Expected: zero, or a shrinking count
Edge Cases & Troubleshooting
| Symptom | Root Cause | Resolution |
|---|---|---|
| Two versions both look current | No single recommended tag | Tag exactly one version recommended |
| Teams keep using the old path | Deprecation not visible | Retitle, tag deprecated, add a migration note |
| Retired path breaks existing services | Removed the running scaffold’s support | Retire the template, keep supporting scaffolded services |
| No migration guidance | Deprecation without a path forward | Ship a migration guide before deprecating |
| Version sprawl | Every change makes a new template | Reserve new versions for breaking changes |
Frequently Asked Questions
When does a change warrant a new version instead of an edit?
Reserve a new version for a breaking change — a different framework major, an incompatible structure, a new required input. Non-breaking improvements should just update the current template in place, so teams get the fix without a migration. Minting a new version for every tweak is how you end up with the sprawl that makes the whole catalog feel untrustworthy.
How long should a deprecated path stay available?
Until adoption of it reaches zero and existing services have migrated or been accounted for. Deprecation is a signal, not a deadline you enforce blindly; pull the template only when the data shows nobody is starting new work on it. Rushing removal to hit a date strands the teams who had not migrated yet and erodes trust in the next deprecation you announce.