Skip to main content

Modernization starts with better maps, not bigger promises

I usually care more about what a system does than what it's called. The part I would watch in any modernization effort is how teams map dependencies before they talk about replacement. What surprised me, after reading the Azure AI services docs and Martin Fowler's latest fragments, is how often everyone assumes you already know where the bodies are buried.

Modernization is not a technology problem. It is a knowledge problem. The code is just the fossil record of decisions made years ago by people who have since moved on.

The Real Problem

The real problem is that organizational knowledge is locked in legacy systems. Engineers want to build new things, but the business fears breaking what already works. The human cost is brutal context switching: excitement about new tech in the morning, anxiety about unknown dependencies that might take down production on a Friday evening.

Martin Fowler's recent writing on harnessing engineering for coding agents nails the core issue: we treat codebases as things to be rewritten instead of systems to be understood. The Azure AI Foundry docs are full of powerful capabilities, but they don't tell you which of your 200 microservices actually talks to the billing database that no one remembers. They don't show you the cron job that runs every Sunday at 2am and rebuilds the reporting cache in a way that violates every normalization rule.

"Legacy system" is just industry slang for "code that works but no one wants to touch." The fear is rational. I have seen a team break a five-year-old integration that nobody could explain, and the resulting fire drill cost more than the planned modernization would have. The original developer had left, the documentation was a PowerPoint from 2012, and the only person who understood it was a support engineer who had reverse-engineered the behavior by reading logs for three years.

Where Teams Usually Get It Wrong

Most teams start with a demo. They see a new Azure service, a shiny GitHub release, or a conference talk. The decision is made before anyone has traced a single request through the existing system. I read the Azure OpenAI "What's New" page and thought: this is useful, but only if you know which part of your monolith actually handles customer queries and whether it's even a good candidate for a language model.

The mistake is treating modernization as a procurement exercise. You don't buy your way out of complexity. You map your way through it. Most architecture diagrams look like abstract art—impressive but you wouldn't want to debug them. They show boxes and arrows, but they don't show the data flows, the timeout configurations, the retry policies, or the fact that Service A calls Service B which calls Service C which eventually writes to a table that Service A also reads from directly. That hidden cycle is where your midnight pages come from.

I have watched teams select a new database before understanding their query patterns. They choose a message queue before mapping their event flows. They commit to a service mesh before cataloging their actual service-to-service communication. This is resume-driven development disguised as modernization. The technology becomes a solution in search of a problem, and the problem is always more complicated than the demo suggested.

A Better Working Shape

A better shape starts with code intelligence. Not the marketing kind, but the practical work of answering three questions:

  1. Which services call this deprecated API?
  2. What data flows through this queue?
  3. Who knows how this cron job affects the reporting pipeline?

GitHub's code navigation and language server protocols are examples of better maps. They don't replace your system; they illuminate it. You can click through call hierarchies, find references, and see where a particular method is used. But static analysis only gets you so far. You also need dynamic traces.

Microsoft's Azure modernization guides actually get this right in places—they suggest assessing before migrating. The gap is in execution, not intent. The assessment phase is where you build your map. You instrument your existing system, capture real traffic, and build a dependency graph from the data. You use grep -r to find hardcoded connection strings, git blame to find the last person who touched a critical module, and distributed tracing to see how requests flow across service boundaries.

What I do not trust yet is any tool that promises to auto-migrate your dependencies. Dependencies are social, not just technical. The person who wrote that integration left three years ago. The documentation is a PowerPoint from 2012. The real behavior is encoded in logs, in monitoring dashboards, and in the war stories that get told during incident reviews. A tool cannot capture that.

I usually start by mapping the critical path. Find the request that starts at the API gateway and ends at the database. Trace every service, every queue, every cache lookup. Then expand outward to the secondary paths: the batch jobs, the reporting queries, the admin tools. The map is never complete, but it is useful. It tells you where the leverage is.

What to Watch in Practice

The part I would watch is sequencing. You cannot modernize everything at once. You can modernize one dependency at a time. Start with the edges. Find the service that talks to the mainframe but nothing else does. Wrap it. Test the wrapper. Then replace it. This is where the leverage lives: surgical, high-leverage interventions based on actual maps.

I usually look for the database that has three different schemas from three different eras. That is usually where things get messy. Migrate one schema at a time. Keep the old and new running in parallel until the traffic map shows you can safely cut over. Use feature flags to route traffic gradually. Monitor error rates, latency, and business metrics. The map tells you what to measure.

The practical question for me is always: what breaks if we turn this off? If you don't know, you aren't ready to modernize it. I have seen teams skip this step and learn about a dependency when a customer called to report that their order history was missing. The map would have shown that the order history service was the only consumer of a deprecated logging table.

Testing during modernization is different. You cannot rely on unit tests alone. You need contract tests to ensure the new service behaves like the old one. You need consumer-driven contracts to ensure downstream services still get what they expect. You need canary analysis to compare the behavior of old and new in production with real traffic. The map tells you which contracts matter.

Heuristics for Day-to-Day Work

Here is what this changes about day-to-day engineering work:

  • Before any tech decision, draw a map. Even a rough one. Use grep, use logs, use distributed tracing. The map is the plan. I usually start with a Mermaid diagram and fill it in as I trace requests. It does not need to be perfect; it needs to be better than a blank page.

  • Prioritize by pain, not by hype. The service that pages you at 3am is the service to modernize first, not the one that would look best on a resume. I track pain using incident reports, support tickets, and on-call rotations. The pain score tells you where the map is most valuable.

  • Preserve knowledge, not just code. When you find someone who knows how the payment gateway integration works, record them. Literally. That knowledge is more valuable than the code. I have a folder of screen recordings where senior engineers walk through critical paths. It is not a replacement for documentation, but it is better than nothing.

  • Ship less, but better. This connects directly to my earlier post on shipping less but better. Modernization is the ultimate exercise in shipping less—fewer services, fewer dependencies, fewer things that can break. Each modernization step should reduce the total surface area of your system. If it does not, you have added complexity instead of removing it.

  • Build the map incrementally. You do not need to map the entire system before you start. Map the part you are touching now. Expand the map as you expand your work. This makes the task less daunting and gives you immediate value. The map for the billing service helps you modernize the billing service. That is enough for now.

The useful part of this approach is that it reduces fear. Fear of breaking dependencies that no one remembers. The annoying part is that it is slow. Mapping takes time. But it is faster than rolling back a failed migration at midnight. I have measured this: a week of mapping saves a month of debugging.

Closing Heuristics

Modernization starts with better maps, not bigger promises. The leverage comes from precision, not from rewriting everything. I would rather have a team that can trace a request from API gateway to database and back than a team that knows all the features of the latest Azure service.

The next time someone suggests a modernization project, ask them to show you the map. If they can't, they aren't ready. If they show you a diagram with boxes and arrows but no data flows, ask for more. If they tell you the tool will handle it, ask what breaks when they turn it off.

The map is the artifact that turns a vague desire for modernization into a concrete plan. It is the difference between a demo and a deployment. It is the only thing that can save you from a Friday evening incident that could have been avoided with a better understanding of the system you were trying to improve.

Resources Worth Reading

Related Reading