Backstage Software Catalog: Getting Adoption Past the Demo
The Backstage demo always wows leadership. Then six months later the catalog has 400 stale entries and nobody trusts it. Here's what got ours to actually stick.
Key takeaways
- The Backstage demo always wows leadership.
- Then six months later the catalog has 400 stale entries and nobody trusts it.
- Here's what got ours to actually stick.
Backstage Software Catalog: Getting Adoption Past the Demo#
Our first Backstage rollout died quietly. We stood it up, ran a demo that made VPs nod, and asked every team to register their services by hand. Three weeks in we had 60 entries. Two of them were spelled correctly. By month three the catalog listed services that had been decommissioned, owners who'd left the company, and docs links that 404'd. Engineers stopped opening it, which meant they stopped maintaining it, which meant it got worse. The catalog was a graveyard.
The relaunch worked, and the difference came down to one rule: never ask a human to type what a machine can discover.
Stop asking people to register services#
Manual catalog-info.yaml registration is where adoption goes to die. Nobody remembers to add a new service, and nobody remembers to delete an old one. We switched to auto-discovery against our GitHub org so the catalog reflects reality without anyone thinking about it.
catalog:
providers:
github:
devopsnessOrg:
organization: 'devopsness'
catalog:
branch: 'main'
filters:
repository: '.*-service$'
filename: 'catalog-info.yaml'
schedule:
frequency: { minutes: 30 }
timeout: { minutes: 3 }
Repos that match *-service get scanned every 30 minutes. A deleted repo drops out of the catalog on the next sweep. That single change killed the stale-entry problem that had sunk the first attempt.
Make the entity file trivial to get right#
Even with discovery, each repo needs a catalog-info.yaml. If that file is hard to write, teams will half-fill it. We kept the required surface tiny and generated it inside our service scaffolder so new repos ship with it already correct:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payments-service
annotations:
github.com/project-slug: devopsness/payments-service
pagerduty.com/service-id: PD5X8Q2
backstage.io/techdocs-ref: dir:.
spec:
type: service
lifecycle: production
owner: team-payments
system: checkout
The owner field is the one that matters, and we made it non-negotiable. A component with no valid owner, or an owner that doesn't map to a real group in the org, fails a CI check. No ownerless services in the catalog, ever. That's how you keep the "who do I ask about this" question answerable, which is the whole reason people open the catalog in the first place.
Give people a reason to look#
Discovery keeps the catalog honest, but honesty isn't a reason to visit. We added a Soundcheck-style scorecard (you can build a lighter version with the backstage-plugin-tech-insights package) that grades every service on things people already argue about: has a README, has an on-call rotation wired up, ran a deploy in the last 30 days, has no critical CVEs open past SLA.
export const hasRecentDeploy: FactChecker = {
id: 'recent-deploy',
name: 'Deployed in last 30 days',
factIds: ['deploy.lastTimestamp'],
rule: (facts) => {
const days = (Date.now() - facts['deploy.lastTimestamp']) / 86400000;
return { result: days <= 30, value: days };
},
};
Once a director could pull up a table showing which teams had services failing the security check, the catalog stopped being optional. Engineers checked their own scorecard before their manager did. Adoption stopped being something we pushed and became something people self-served.
The number that predicted success#
The metric we watched wasn't entry count, it was weekly active catalog users divided by engineers. At launch it was under 10 percent, which is demo-only territory. It crossed 55 percent once TechDocs went live inside Backstage, because now the catalog was where the runbooks lived. People came for docs and stayed for everything else. If your docs are still in a separate wiki, the catalog will always be a second stop, and second stops get skipped.
What we'd skip next time#
We spent two weeks building a custom plugin to visualize service dependencies. It looked great in the demo and nobody used it. The dependency graph is only useful during an incident, and during an incident people go to the runbook, not a graph. Build the boring things first: discovery, ownership enforcement, docs, scorecards. Save the pretty graph for after people already trust the data.
The call we'd make#
Backstage lives or dies on trust in the data, and trust comes from automation, not diligence. Auto-discover everything, enforce ownership in CI, put your docs inside it, and add a scorecard leadership actually looks at. If you're relying on engineers to manually register and update services, cancel the project now and save yourself the graveyard. Ours went from 60 rotting entries to 380 accurate ones the day we stopped asking humans to do a robot's job.
Before you invest this much in the catalog, it's worth checking whether Backstage is even the right call for your team size in the first place — see internal developer platforms: is Backstage worth it?
Get the DevOps Troubleshooting Cheat Sheet
Subscribe and get our free one-page reference for the errors that eat an afternoon — CrashLoopBackOff, OOMKilled, Terraform state locks, and more — plus new guides as we publish them.
Terraform Import at Scale: Bringing Legacy Infra Under Code
We inherited 200-odd AWS resources built by hand over four years, with no state file anywhere. Here's how import blocks and a generation workflow got them under Terraform without a rebuild.
GitHub Actions Reusable Workflows: DRY Pipelines at Org Scale
We had the same 180-line build workflow copy-pasted into 60 repos. Fixing one bug meant 60 PRs. Here's the reusable-workflow setup that made it one.
More from Infrastructure
Explore more articles in this category
Cloudflare's Clef: When a Decision Model Beats an LLM Call
Most moderation, routing, and triage calls aren't generation problems. Clef proves it by answering in 38.8 milliseconds instead of seconds.
Perplexity Left DynamoDB for CobbleDB: When Should You?
Perplexity built its own key-value store because DynamoDB's read path and bill stopped fitting 50 KB search items. Here is the checklist for when leaving is justified.
The Terraform Lock File Is Code: Review It Before You Init
A DPRK-linked group is mailing DevOps candidates Terraform take-home repos whose lock file points at a fake registry. terraform init then runs the attacker's provider.
You might have missed
Evergreen posts worth revisiting.