Abstract
Today we write more code in less time than ever before. AI systems act like permanently available junior programmers: They produce thousands of lines of code in minutes – but who takes responsibility for the software architecture?
Table of Contents
- Abstract
- 1. We Write More Code Than Ever Before
- 2. Two Ways to Use AI in Programming
- 3. If Only This Existed …
- 4. UML Is the Solution, but Not How We Know It
- 5. Dependencies as a Common Language
- 6. Interactive Dependency Visualization as a Thinking Tool
- 7. From Understanding to Action: Refactoring With Less Risk
- Conclusion
The real problem is therefore often not “bad AI code,” but the growing difficulty for people to keep an overview and make sound architectural decisions.
This article discusses an idea for quickly building a mental model of existing code, spotting architectural problems early, and making targeted changes – without having to maintain static documentation in parallel.
1. We Write More Code Than Ever Before
AI accelerates software development. What we are experiencing here is known as the Jevons paradox or rebound effect. When compilers for languages like C were developed, software development suddenly became much faster. But instead of just going home earlier, we developed more complex mental models, which is why we needed a new language like C++. This transition happens again and again – from HTML to React, SQL to ORM, and Java to Kotlin.
With AI, the exact same thing is happening right now. Writing code becomes easier and the mental models become more complex. As a result, costs don’t get lower; they shift – from writing to understanding.
In classic projects, the balance was often more even: code emerged at a pace at which people could build a mental model along the way. With AI‑generated code, this balance tips. The code is there – the mental model has to follow.
You notice this especially in teams: The time that used to flow “on the side” into orientation (pair programming, code reviews, occasional discussions about structure) suddenly becomes its own work‑package category. You have to understand the architecture before you can change it – and that order cannot be optimized away.
2. Two Ways to Use AI in Programming
The use of AI is not uniform. In practice, you see two poles between which most ways of working move.
2.1 Goal-Oriented “Vibe Coding”
Here, developers simply describe the desired outcome. The AI delivers the complete implementation. The code is not read at all. If something doesn’t work, the AI has to fix it.
This can work astonishingly well. The benchmark today is no longer whether the AI can program the game Snake in a single prompt – but a complete Fortnite clone.
That is unquestionably impressive, but this model reaches its limits when it comes to further developing that software. Without instructions about software architecture, today’s AIs tend to write a lot of code into single files. This leads to:
- very large classes and files
- unclear layers (UI ↔ domain ↔ infrastructure blurs)
- flat, hard‑to‑maintain structures
- dependencies that reach “everywhere”
At some point the AI also starts to wobble when it comes to changing the code.
This will, of course, keep improving with newer models, and maybe we will eventually reach the point where we trust an AI as much as we trust a compiler today.
2.2 Architecture-Driven With AI Support
On the other end are teams that consciously use AI as an accelerator, but treat architecture as a human task:
- small, targeted prompts
- clear boundaries (modules, packages, responsibilities)
- explicit requirements for maintainability and structure
- consistent realignment of dependencies as the system grows
Here, AI mainly helps with implementation within guardrails. The guardrails themselves must remain visible and verifiable. And right here the central question appears:
But how do we reliably keep the structure in view, regardless of whether we designed it ourselves or an AI did?
3. If Only This Existed …
More than once I wanted to rebuild my code – but in order not to break anything, I first had to (again) understand how the methods call each other. I arranged the windows so I could take a screenshot and draw the call arrows by hand. Every time I wished that this would happen automatically. That’s why I built a small prototype to test whether this is possible – and how well it works.
4. UML Is the Solution, but Not How We Know It
Drawing method calls and dependencies between classes strongly resembles UML. And that is no coincidence: connections between classes, their relationships, and the package structure are exactly what we need to understand existing code faster. In practice, however, UML fails in many teams not because of the content, but because of the dynamics:
- Diagrams are snapshots.
- They must be actively kept in sync with the code.
- Even with automatic generation, maintenance work remains: What is relevant? What is noise? What belongs in the diagram?
UML is therefore often more documentation than navigation. To really help with understanding AI‑generated code, we need something that …
- is always in sync
- can answer questions interactively
This comparison makes it clear:
- A static UML diagram is like a paper map. It can be correct. Or it can be outdated. And there is only one scale.
- Interactive visualization is like Google Maps. It changes when the world changes. I can zoom to the place I actually care about. Different views help me get exactly the information I need right now.
The decisive shift in perspective is therefore not “replace UML,” but thinking of “diagrams as a living system”:
- derived directly from existing code
- with interaction that supports exploration
5. Dependencies as a Common Language
So what does that look like in practice? UML class diagrams usually show only classes, methods, inheritance. But to really understand code, we need more. Methods call other methods – parameters can be of another class’s type.
That’s why I use dependency as a common language that works on every level. What other part do I need so that this part works?
The dependency graph that emerges really functions like a mental map. Structures that are close to each other likely share a common meaning. And if they don’t, then the code is not clean at that point.
This leads me to a few observations I’ve already made while testing my prototype:
Code is no longer a collection of files – but a two‑dimensional structure. I read a class name and thought, “Oh right, that’s the one down in the lower right.” And I also knew which other classes were nearby. That gave me context for that class faster.
Another moment was when I started to refactor code because the diagram “didn’t look right” at that spot. The visualization felt “noisy.” So I cleaned it up. Not to satisfy an architectural guideline – but out of aesthetics.
That made it clear to me that such software is not the next architect’s tool, but an everyday tool for programmers who simply want to get their tasks done without building huge diagrams in their heads and consciously forcing themselves to maintain clean architecture.
This tool creates mental relief when you want to extend code without risking existing functionality. And it nudges you toward clean architecture – without wagging a finger.
6. Interactive Dependency Visualization as a Thinking Tool
But what about the two points of currency and exploration?
Currency can be ensured by generating the diagram directly from the source code – or, for a JVM language, from class files, in order to capture Java, Kotlin, and Groovy projects in one go.
For exploration, the challenge is to generate a readable diagram automatically. That’s not trivial, but it has a decisive advantage. A clean program also has a simple and calm dependency graph. So if the layout algorithm is overwhelmed in cleanly rendering a spot, that is a pretty good sign that the spot has potential to be cleaned up.
But we don’t just want diagrams that look good; we want a different way to explore code:
- Zoom: From package overview to methods to code, without switching tools.
- Focus: Open only relevant areas to reduce cognitive load.
- Filter: Make certain relationship types visible to answer specific questions.
This makes patterns visible that often appear late in code – and then become expensive.
Practically speaking, this means you can answer questions in minutes that otherwise require half an hour of “jumping through files”:
- Where is the natural entry point into this module?
- Which classes are “hubs” where everything converges?
- Which dependency explains why a change suddenly breaks tests everywhere?
- Where does domain logic end and where does infrastructure begin?
- Which parts can I understand in isolation without reading the whole system?
6.1 Patterns That Stand Out “Immediately”
Some examples that stand out very quickly in dependency pictures:
- God classes / God modules: A node with too many edges. A building block that “knows everything.”
- Cyclic dependencies: Loops that feel like knots in a cable – often hard to untangle once features build on them.
- Talking to strangers: Access to classes that are not connected to this class by a variable or a parameter. An important part of “Clean Code.”
- Layer violations: Directions reverse. Dependencies run “from inside to outside,” although the design intended the opposite.
In the editor, you often don’t see these problems because the view is local: file by file, class by class. A visualization acts like a bird’s‑eye view.
6.2 Visual Feedback as a Gentle Guardrail
An interesting effect is that architectural decisions feel less like “rules” and more like “aesthetics”:
If a diagram becomes noisier after a change, you automatically ask: Is this new connection really necessary? Could an interface help? Is the concept cleanly cut?
Conversely, a calmer picture creates a kind of confirmation: because it signals less unnecessary coupling.
That doesn’t replace architectural principles – but it still leads to cleaner code.
7. From Understanding to Action: Refactoring With Less Risk
And when you do have to make changes, they become easier.
- You often recognize good places to cut packages or classes when they have grown too large. The functional grouping often shows up again in the dependency graph.
- You get a natural order for changes across the entire codebase. For example, I could start a port from Java to Kotlin from the “edges” – always starting with the packages that no longer had any dependencies on Java.
Whether changes are then implemented manually or with AI support is secondary. What matters is that a shared understanding exists first. AI can then work very well within a clear plan: “This dependency has to go,” “this part gets extracted,” “this direction is allowed.”
That makes AI less of an autonomous code generator and more of a tool that cleanly follows an already understood architecture.
Conclusion
The amount of code we work with every day keeps growing – regardless of whether it is written by humans or AI. Architectural understanding thus becomes even more important. The bottleneck is increasingly in the mental model: What is connected to what, and what change has which consequences?
Interactive dependency visualization provides a practical answer to this: a map that emerges from real code, enables exploration, and makes architecture experienceable as a living feedback system.
My experience with my prototype has been positive so far, and I’m curious to see where this develops.

This article is part of the JAVAPRO magazine issue:
From AI as a Feature tu AI as Infrastructure
Move beyond AI experimentation and into AI engineering.
Explore the architectures, platforms, and operational practices required to build trustworthy AI systems at scale. From governance and observability to modern Java infrastructure, this edition examines the foundations of production-ready AI.
Discover the edition →