Backstage and the developer-portal category solve a real problem. The reason platform teams quietly abandon them is something different, and it points at the shape of what actually works.
Every few weeks I run into the same observation from someone in platform engineering: their team looked at Backstage, evaluated it seriously, maybe even ran a proof of concept, and walked away. The reason is rarely that it doesn’t do what it advertises. The reason is that the work of keeping it running turned out to be bigger than the value it returned.
I’ve now heard this pattern in r/devops threads, in conversations with engineers who built their own internal alternatives, and most recently from a platform engineer who summarised his evaluation in a single sentence: the cost of maintaining it was bigger than what we got back.
That sentence has been bouncing around my head, because it names something I haven’t seen named clearly. I’m calling it the catalog maintenance trap: the gap between what a service catalog promises and what it costs to keep it true.
What catalogs actually require
Backstage, Port, OpsLevel, and the rest of the developer-portal category are built around a simple model. Each service is described by a YAML file (in Backstage’s case, catalog-info.yaml) that lists the service, its owner, its dependencies, links to runbooks, on-call rotations, and so on. The portal aggregates these files and gives the whole organisation a single pane of glass.
The model is clean. The trouble is the verb tense. The catalog describes the world as of the last time someone updated it. Every dependency added, removed, or renamed since then is invisible until a human notices and edits the YAML. Multiply that across a hundred repos and a few dozen engineers, and the catalog drifts faster than anyone wants to admit.
This isn’t a Backstage flaw. It’s a property of any system where the source of truth is a manually-maintained manifest. The same trap exists in any “declare your dependencies in this file” approach.
Why teams stop maintaining it
The maintenance gradient is brutal. On day one, the catalog is shiny and motivating, and the platform team writes the first batch of YAML by hand. Over the next few weeks, two or three engineers add their services. Then onboarding starts to slow down. Then someone changes a dependency without updating the catalog. Then the dashboard shows an outdated graph. Then a new hire asks “is this accurate?” and the honest answer is “kind of, in places.”
At that point the catalog has stopped being an authoritative graph and started being documentation that was supposed to be authoritative. Which is worse than not having it, because people make decisions on the assumption that it’s accurate.
The platform team has three options at this stage:
- Mandate catalog updates as part of every PR review. An organisational tax that nobody enforces consistently.
- Build automation to keep the catalog current. Which is the actual problem, only now there’s also a YAML schema in the loop.
- Quietly let it rot and rely on tribal knowledge again.
Option three is what most teams converge on. Not because the platform engineers are lazy, but because the cost of options one and two exceeds the value of having a catalog that’s accurate to within a week.
The two escape routes
When teams give up on the catalog, they tend to take one of two paths.
Path one: monorepo. Consolidate everything into one repository and let a build tool like Nx or Turborepo maintain the dependency graph implicitly. This works, but it isn’t a tooling decision. It’s an architecture decision that takes years to execute, often isn’t feasible across business units, and doesn’t help with the infrastructure dependency surface (Terraform, Docker, Helm, CI templates) the way it helps with application code.
Path two: do nothing. Accept that the dependency graph lives in the heads of the senior engineers. Ask around when you need to make a breaking change. Hope nobody on the relevant team is on holiday. This is what most organisations actually do, and it’s also what creates the conditions for the three-hour Slack outage when a base image changes and six teams break in sequence.
Neither escape route is satisfying. The catalog promised to solve this problem, and the alternatives are either an architectural migration or institutional amnesia.
The thing the catalog model gets wrong
The catalog model assumes humans should be the source of truth about dependencies. They shouldn’t be. The actual source of truth already exists, written down in machine-readable form, in the repositories themselves: Terraform source = "..." blocks, Dockerfile FROM statements, go.mod require directives, .gitlab-ci.yml include references, Helm Chart.yaml dependencies. The dependency graph is already declared. It’s just declared across thousands of files in dozens of formats.
The work isn’t getting humans to write a second declaration in YAML. The work is parsing the declarations that already exist and stitching them into one queryable graph.
This is what auto-discovery means in practice. No catalog. No annotations. No PR template reminding people to update the manifest. The graph is read from the source files, and the source files are the ones engineers are already editing because they have to in order to ship code.
This is the difference between documentation that engineers are asked to maintain and a graph that maintains itself.
What this actually changes
Once discovery is automatic, the value proposition shifts. You stop selling “a place to write down what you have” and start selling “a query interface for the truth your repos already encode.” The questions change too:
- Not “is the catalog up to date?” but “what’s the blast radius of changing this module?”
- Not “did everyone fill in their YAML?” but “which repos still pin to the old major version?”
- Not “who owns this service?” but “if I deprecate this artifact, which teams need to be in the room?”
These are the questions platform teams actually ask. The catalog model could only answer them if the catalog was perfect, which it never was. Auto-discovery answers them by skipping the catalog step entirely.
Where Backstage still fits
I want to be careful not to overclaim. Backstage genuinely solves problems that auto-discovery doesn’t: service ownership across hundreds of services, documentation aggregation, templated scaffolding for new services, a unified frontend for on-call and runbooks. Those are real jobs, and a service catalog is a reasonable tool for them.
The mistake is using the catalog as the dependency graph. The catalog is good at things humans want to declare on purpose (this service is owned by team X, on-call is via PagerDuty, the runbook is in Confluence). It is bad at things that change underneath every commit (the actual graph of what consumes what).
For dependency visibility specifically, the catalog model isn’t the right shape. Something that reads the source and rebuilds the graph on every scan is.
What we built
I’m working on Riftmap because this is the gap I kept hitting at client engagements as a consultant, and it’s the gap the engineers I talk to keep describing. Riftmap connects to a GitLab or GitHub org with a read-only token, parses the dependency declarations that already exist across Terraform, Docker, Helm, CI pipelines, Go, npm, Python, Ansible, Kubernetes, and Kustomize, and presents the graph with blast radius analysis. There is no catalog YAML to maintain. The graph rebuilds itself when repos change.
If your team has evaluated Backstage and walked away because the maintenance cost was bigger than the value, or if you’re staring at that same trade-off right now, I’d be curious to hear about it. The pattern I described in this post is built from a small number of conversations and the consistent signal in r/devops and r/terraform threads. The more I hear from teams in this position, the better the tool gets.
You can try Riftmap free at app.riftmap.dev, or reach me directly at [email protected].
If you’ve hit the catalog maintenance trap and want to compare notes, I’d genuinely like to hear how it went. Honest accounts of what worked and what didn’t are the most valuable input I get.