The Cartographer's Quiet Afternoon: Mapping Dependencies with a Simple Dot File
In the quiet hum of a small service, there exists an intricate landscape. Data flows from this tiny API to that background worker; a cache warms from a database, which in turn is fed by a periodic import script. We often hold a rough sketch of these routes in our heads—a mental map that seems sufficient until the fog of an incident rolls in. Suddenly, that background worker is not so trivial; its failure means a clogged queue, which means an unresponsive API, which means a frantic search through logs to find the original fissure. We realize our map is incomplete, drawn from memory on the back of a napkin.
We can do better than the napkin. We don't need a sophisticated, auto-discovering, real-time monitoring suite to start. Often, that's overkill for our small kingdom of processes. What we need is the equivalent of a cartographer's first draft: a clear, deliberate, and human-readable chart of what depends on what. The simplest way I've found to do this is to maintain a .dot file at the root of a project.
The Humble DOT Language
Graphviz's DOT language is a plain-text format for describing graphs. It feels almost like writing a thoughtful list. You declare your components—your services, databases, queues—and then you draw the lines of dependency between them. A basic example for a web application might look like this:
digraph Dependencies {
user_browser -> nginx [label=\"HTTP\"];
nginx -> api_service [label=\"port 8000\"];
api_service -> postgresql [label=\"queries\"];
api_service -> redis [label=\"sessions\"];
cron_job -> api_service [label=\"trigger report\"];
api_service -> email_service [label=\"SMTP\"];
}
This is not code. It's documentation. It lives as service-map.dot right next to your docker-compose.yml or your README. Anyone, including your future self on a sleep-deprived Tuesday night, can read it and understand the lay of the land. The real magic comes when you run it through the dot command-line tool (easily installed via Graphviz) to generate an image: dot -Tpng service-map.dot -o service-map.png. In seconds, you have a clean, professional diagram.
The practice is more important than the tool, however. The act of writing this file forces a moment of clarity. You must name your services. You must consciously decide on the direction of the arrow: does the API call the queue, or does the queue push to the API? This simple act of definition is a form of operational thinking. It surfaces assumptions. You might realize that your ‘simple’ service quietly relies on three different external APIs, a fact easily forgotten when everything is healthy.
This map becomes a living document. When you add a new caching layer, you add a node to the .dot file. When you decommission an old service, you remove it. The generated image can be included in your internal wiki, but the source file remains the source of truth, version-controlled alongside your code. It’s a ritual of documentation that scales with your system, not a burdensome chore that gets outdated the moment it's printed. It’s the quiet work of a cartographer, ensuring that no matter how complex the territory becomes, there is always a reliable map for the journey ahead.
Notes & further reading
A few pages I came back to while writing this:
- Baton Rouge, LA
- The Lineman's Knot: How a Telegraph Repair Shaped Our Thinking on Redundancy
- Lafayette, LA
- The Clockmaker's Regret: A Lesson in Logging's Blind Spots
- New Orleans, LA
- The Slow River of the Request Queue
- Shreveport, LA
- Boston, MA
- Springfield, MA
- Worcester, MA
- Baltimore, MD
- Detroit, MI
- Grand Rapids, MI