ArchyntArchynt.api
Back to blogArchitecture

The dependency edge that lies to you

Every diagram anyone has ever drawn of a running system was wrong the moment someone shipped without updating it. Here's how we catch it.

July 1, 2026 · 7 min read

Ask a platform team for their architecture diagram and you'll get one of two things: a Confluence page with a "last edited 14 months ago" stamp, or a shrug and a Slack thread where three people argue about whether the payments service still calls inventory directly or went through the event bus in the Q3 migration nobody finished documenting. Both answers are correct, in the sense that both are what actually happens on most teams past a certain size.

The diagram isn't wrong because someone was lazy. It's wrong because a diagram is a claim about the system frozen at the moment someone drew it, and the system kept shipping. Every dependency you didn't remove, every new call someone added under deadline pressure and meant to clean up later, every fallback path that only fires in production — none of that shows up in a box-and-arrow diagram unless someone goes back and redraws it. Nobody redraws it. That gap between the system as documented and the system as it runs is what we mean by architectural drift, and it's not a hypothetical: it's the default state of any system old enough to have had more than one engineer touch it.

Two sources of truth, and neither one is enough alone

Static analysis reads what you wrote: parse the source, resolve the imports, follow the HTTP clients and the Kafka listeners, and you get a graph of what the code says should be calling what. It's precise and it's cheap to compute, but it only knows about paths that exist in the source — a feature flag that's been at 100% for eight months and never had its old branch deleted still looks "used" to a purely static reading, and a call that only happens through reflection or a dynamically built URL might not show up at all.

Runtime observation is the mirror opposite. Watch actual traffic — which port talked to which container, how often, how fast — and you get exactly what's happening right now, no interpretation required. But runtime data has no memory of intent. It can't tell you whether a call was designed in or is a leftover nobody's game to remove, and if the traffic sample misses a nightly batch job that runs once a day, that dependency is invisible to it.

Neither one is lying, exactly. They're both telling the truth about a different question.

What tagging every edge actually buys you

The useful move isn't picking one source over the other — it's keeping both and refusing to merge them into a single unlabeled fact. Every dependency edge in Archynt's graph carries where it came from: static (found in source, never observed live), runtime (observed in traffic, not written anywhere), or both. That third state, confirmed_by: both, is the only one you can act on with real confidence — it means the code and the wire agree.

The other two are where the interesting findings live. A static-only edge with no runtime traffic in weeks is a dead-code candidate — a dependency you can probably delete, and now you have evidence instead of a guess. A runtime-only edge is the opposite and, in our experience, the more urgent one: a live dependency nobody wrote down, which means nobody owns it, nobody alerts on it, and the next person who touches the calling service has no idea it's load-bearing until they break it.

Why this is a risk conversation, not a documentation one

It's tempting to file drift under "keep the docs current," which is exactly why most teams never fix it — documentation debt loses every prioritization meeting to anything with a ticket and a deadline. The more honest framing is that an unowned runtime dependency is unmonitored attack surface and an unplanned single point of failure at the same time. Nobody's on call for a connection they don't know exists. That's the pitch we lead with now, because it's the one that survives contact with a roadmap review.

Want to see this on your own system?

A repository URL is enough for the static graph. The runtime agent takes one docker-compose file.