# The Map Behind My Decisions: A Code Knowledge Base for Microservices

Imagine you land in a new city and need a pharmacy. You can stop strangers on the street and ask each one. Or you can open a map.

Working across a dozen microservices used to feel like the first option. I'd grep, open a file, follow a call, lose the thread and grep again. The time wasn't the real problem. The noise was. The more I read, the easier it became to miss the one line that mattered.

So I built myself a map. It started as a way to move faster, and it turned into a knowledge base of my own code. Now I use it to plan features, make architecture calls and review changes with the whole system in mind, not just the file in front of me. My AI tools read it too, but that came later.

## The Questions Before Any Change

Before I touch anything, I ask the same four things:

- Where else is this method used?
- Who reads this table?
- Do we already have a helper for this?
- Did we already decide how to do this?

Experienced engineers carry these answers in their heads. The trouble is that heads can't be searched, they get tired and people move on. I wanted the answers written down, in a place I can query in seconds.

## Layer 1: The Map (What Exists)

The first piece is a **code graph**. Every class, method and file is a node, and every call, import or inheritance is a line between two nodes.

I kept the choices boring on purpose:

- **No LLM involved.** It's built from the syntax tree, locally. That makes it deterministic and free, and a rebuild takes a few seconds per service.
- **One graph per service, plus a merged one.** My biggest backend has around four thousand nodes and twelve thousand edges. That's nothing for a machine.
- **Rebuilt only when needed.** A scheduled job checks the branch, the commit and the working tree, and skips services that haven't changed.
- **Kept outside the repositories**, so the repos stay clean.

The question I ask most is *"what is affected if I change this?"* The answer is a short list of files and line numbers. I see the blast radius of a change before I read a single line of source.

## An Accidental Discovery

Once every service had a graph, merging them was almost free. And the merged view showed me things I could never see from inside one repository.

I listed every concept that shared a name across two or more services and got over two hundred candidates. Authentication was the biggest group by far. One token-validation class turned out to be identical across five backends. Not similar. Identical.

Is that bad? Maybe it's exactly what a shared library would look like if we had built one. Either way, I would never have spotted it reading code service by service.

Same name doesn't mean same code, so I read that list as questions. Which ones deserve a real conversation, and what to do about them, is up to me.

## Layer 2: The House Rules (How We Do Things)

A map shows you what exists. It can't tell you how your team does things.

For that I keep a few short markdown files per service: architecture, conventions, known tech debt and a glossary. Every claim in them has to point to a file. I refresh them when they go stale, because outdated docs do more harm than no docs.

They answer questions like "how do we name statuses here?" or "how do we paginate?" without a trip through old pull requests.

## Layer 3: The Memory of Why (Decisions)

This is the layer I underestimated the most.

I keep a plain list of **decision records**. Each one is short: the context, what we chose, what we rejected and why. They start as drafts, and none of them counts as a decision until I've gone through the evidence.

People forget why things are the way they are, and so does any tool you feed your notes to. With these records, "didn't we already talk about this?" becomes a lookup instead of a meeting.

## How I Actually Use It

The order matters more than the tools:

1. **Decisions:** was this already settled?
2. **Conventions:** how does this service expect it to be done?
3. **Graph:** where does it live and what does it touch?
4. **Code:** only now, and only the files the first three pointed at.

I go through this when I plan a ticket, design a change, review a pull request or prepare a release. My AI assistant reads the same layers, so it starts from the same facts I do and doesn't wander through the repository blindly. But it only reads. The map, the rules in it and the calls I make from it are mine.

## Beyond a Single Change: Thinking Long Term

What surprised me is that the map pays off most *before* any code exists.

On a small change it saves a little time. On a big decision, like "should this become a shared library?" or "can we change this contract without breaking anyone?", it changes the question itself. I'm no longer reasoning about one service from memory. I can see which services share the concept, which ones depend on the table and what tech debt I already wrote down for that area.

Long-term thinking mostly comes down to field of view. A decision that looks cheap inside one repository can be expensive across five. The map shows me that early, while changing my mind still costs nothing.

## What I Still Do by Hand

Screenshots never show this part, so here it is:

- **Deciding what becomes a decision.** A draft stays a draft until I've checked the evidence.
- **Checking how fresh the map is.** Before I trust a graph, I compare its build with the latest commit.
- **Reading the final code.** The map tells me where to look. Whether the change is right is a separate question, and it's mine to answer.
- **Verifying suggestions.** When a tool tells me something, I confirm it in the source first.

When the map can't answer a question, I fix the map and keep thinking.

## A Hypothetical, to Make It Concrete

Say you're about to rename a status from `Rejected` to `Failed` in one service. A plain search finds eight matches and you feel fine about it. The graph also shows two other services reading the same shared table, and one of them filters on the old value in a reporting query. The decision records tell you someone picked the original name on purpose, and why.

Same change. You just go into it knowing a lot more.

## Where It Falls Short

- **A map is not the territory.** It's only as good as its last build.
- **Static analysis has blind spots.** Dependency injection, reflection and config-driven behavior don't show up as clean lines.
- **There's noise.** Framework types like loggers and tasks connect to everything, so they have to be filtered out.
- **It decides nothing.** It only makes the right question cheaper to ask.

## Key Takeaways

- **Write down what lives in your head.** A graph, a few rules and a list of decisions are enough to start.
- **Three layers, three questions.** The graph tells you *what*, the conventions tell you *how*, the decisions tell you *why*.
- **Automate the refresh.** A map that needs manual upkeep will rot.
- **Look across services.** The graph that helps with one change also exposes duplication in the whole system.
- **Use it before you write code.** Design and architecture decisions are where a wider view is cheapest.
- **Keep the judgment.** Tools, AI included, read the map. Engineers make the decisions.

Clean code is not about beauty — it's about predictability. The same goes for the context behind your work: the better it's organized, the better the decisions you can make from it.

---

**📣 Your turn!**

Do you keep any kind of map of your system, even a rough one? If you could build only one of these three layers tomorrow, which would it be? Tell me in the comments.

👉 Follow my articles on the official blog at [byRodovalho.com](http://byRodovalho.com) and let's keep the conversation going on [LinkedIn](https://www.linkedin.com/in/phelipe-rodovalho).

🔗 Previous article: [How AI Saved Me From a Cross-Service Code Review Blind Spot](https://byrodovalho.com/how-ai-saved-me-from-a-cross-service-code-review-blind-spot)
