DEV Community

Cover image for Untangled
krazybean
krazybean

Posted on Originally published at juancastro.me AI-assisted

Untangled

Your IDE will tell you if a file has a syntax error. Your linter will tell you if the style is off. Deptrac will tell you if you broke an architectural rule you wrote yourself.

What happens when you've spent eight months building an application and suddenly realize how expansive it's become?

Or when you join an organization with an established monolithic codebase, but have little understanding of how its architecture fits together?

Your existing tools are essential, but they don't necessarily show you the bigger picture: how the different parts of your application depend on one another.

Which dependencies have accumulated? How many components rely on that helper you wrote months ago? And most importantly, what other parts of the application might be affected if you change something?

Why Untangled exists?

I was talking with a coworker and trying to explain how Snakefood visualized the structure of a codebase and the complexity identified at a glance.

This is the kind of visualization I remembered from Snakefood. What interested me wasn't necessarily identifying a problem, but being able to look at the dependencies across an entire application instead of examining files individually.

Snakefood, originally created by Martin Blais, provided the inspiration for Untangled. I wanted to preserve that fundamental approach while extending it with modern language support, additional analysis, and more accessible reports.

But when I searched for graphical examples, I found almost nothing. Something that I thought was so profound was barely in my search results despite whatever keywords I used.

This is when the idea came out, Hell, let's rebuild it then. But why stop there, lets add more to it, more languages and more capabilities!

Not a score, not a grade — a map of the relationships in your codebase, backed by measurements that explain what you're looking at.

Architecture grows one decision at a time

Being able to identify complexity at a high level gives you something valuable: a better understanding of where your code is tightly connected, which areas might benefit from refactoring, and where separating responsibilities could make future changes easier.

A big app accumulates that shape quietly, one reasonable decision at a time.

Think about it. You want to implement some new functionality, so you pull in another library from your favorite package manager.
Maybe you made a helper folder and it started to become your junk drawer of code.

Spend enough time moving between codebases, or even working in the same one for months, and it becomes surprisingly easy to lose track of how those dependencies accumulated.

A real-world example — Monica

I wanted to start by examining some established open-source software, so I chose Monica, a personal CRM built with Laravel.

I ran Untangled against Monica at two different levels. Grouping by namespace produced 33 components, with 13 involved in one large interconnected dependency cycle. That cycle connected areas such as actions, Fortify, settings, and controllers.

But I also wanted to see what was happening at the individual file level.

Across 923 analyzed files, Untangled identified 3,131 dependency relationships and four cyclic groups involving 76 files. The largest of those groups contained 69 files.


Is this unusually large? I'm not sure. I haven't tested enough comparable applications to establish a meaningful baseline, and I don't want to draw conclusions the numbers can't support.

What I can say is that those relationships are now visible.

The dependency map provides a visual representation of how the codebase is connected. In this report, it displays the 300 most connected files to keep the visualization readable, while the full results remain available elsewhere in the report.


The cycle analysis goes a little further. It identifies groups of files whose dependencies eventually lead back to one another. That doesn't automatically mean something is broken, but it may reveal areas where changing, testing, or separating responsibilities could require additional consideration.

For me, one of the more useful sections is Where to look first.

For example, Monica's ServiceInterface.php is depended upon by 248 other parts of the codebase, while routes/web.php depends on 144 parts. These numbers represent different kinds of relationships, and neither is necessarily problematic.

For an individual developer, knowing which components are heavily depended upon can help identify the potential impact of a change. Something as simple as modifying a shared interface may deserve more attention than changing an isolated utility.

For an architect, the same information provides a different perspective. Large dependency cycles, heavily connected components, and architectural boundaries become things you can investigate rather than assumptions about how the application was designed.

None of these numbers tell you what to fix. They tell you where to ask questions.

That's not a bug report. That's a starting point for understanding the codebase.

Untangled and Deptrac — Different responsibilities

Deptrac is enforcement: you write rules about which layers may depend on which, and it fails when a rule breaks.

Untangled can help you figure out which rules are worth writing in the first place.

For example, you might want to prevent controllers from depending directly on persistence implementations. Deptrac can enforce that architectural boundary, while Untangled can help reveal the existing relationships before you decide how to establish or enforce it.

They're complementary tools. One helps you understand the architecture you have; the other helps enforce the architecture you want.

Evidence isn't judgment

This distinction is important to me, especially when running Untangled against my own code. All code grows and with that growth leaves a good amount of room for polishing up later on. This is why Untangled was hatched.

Untangled supplies evidence. The engineer supplies judgment.

Where Untangled goes next

There are other ideas I'm considering for Untangled, particularly examining Git history to understand how dependencies have changed over time.

A snapshot tells you what the architecture looks like today. Being able to trace its evolution could help explain how it arrived there.

I'm also considering package tracking to better understand how dependency changes influence an application's structure. These are ideas under consideration, not promises about upcoming releases.

Beyond PHP

I've focused on PHP in this article, but Untangled also supports Python and JavaScript/TypeScript.

In Closing
I invite you to try it out, give me some feedback. I'm always open to some good suggestions and even criticism.

Editorial note: I wrote this article based on my experience building Untangled. AI tools assisted with editing, organization, and refining some of the technical explanations. The project, observations, and conclusions are my own.

GitHub logo krazybean / Untangled

Dependency health your whole team can understand: cycles and coupling in PHP, Python, and TypeScript projects, in a report anyone can read.

Untangled

PHP Python TypeScript Packagist PyPI npm

Dependency health your whole team can understand. Cycles, coupling and trends over time, in the same terms for PHP, JavaScript/TypeScript and Python.

Untangled scans a codebase, builds the graph of which files depend on which and turns it into numbers and a readable report: where the cycles are, which parts everything leans on, and which parts lean on everything. The reports are written for engineers and for the people they report to, with plain-language explanations of why coupling matters.

An Untangled report: the summary at the top of the page

More reports and graphs, on real PHP, Python and JavaScript projects, are in EXAMPLES.md.

Status: 1.0.0 is out for all three languages, on Packagist PyPI and npm. See Roadmap.

Credit: snakefood

Untangled is a re-imagining of snakefood by Martin Blais. The idea and the design are his: scan source files into a plain list of (from, to) dependency pairs, then filter, cluster and render that…

Try PHP

composer require --dev krazybean/untangled
vendor/bin/untangled report --cluster=namespace:2 --output=report.html
Enter fullscreen mode Exit fullscreen mode

Try Python

pip install untangled
untangled report /path/to/your/project --output=report.html
Enter fullscreen mode Exit fullscreen mode

Try JS

npm install -g @krazybean/untangled
untangled report /path/to/your/project --output=report.html
Enter fullscreen mode Exit fullscreen mode

Top comments (0)