Software Architecture Is Fractal (read only if you're bored)

I was thinking about a couple of ideas. They were too good not to put them here. Have fun this weekend. Happy Friday to everyone.

Software Architecture Is Fractal

And That Is Why Your AI Keeps Breaking It

A small heresy, before we rename the problem again.

We have a tradition in this field. Every year or so we rediscover that the work is hard, panic quietly, and give the hard part a new name so it feels solved. Prompt engineering was a name. Context engineering was a name. Workflow orchestration is the fashionable one now, and I can already hear the next one warming up backstage, something like intent expression, and the one after that will be grander still, maybe self-assembling agent philosophy, sold by someone in a very good jacket. Each name promises the same thing. Learn it, and the work disappears.

I want to talk about the one part nobody has managed to rename into submission, the part that outlives every rebrand because it will not hold still long enough to be packaged and sold. The architecture itself. Not the prompts that feed it or the context you pour into it, but the shape underneath them: the boundaries, the rules, the reason a thing is built the way it is. The part you are quietly supposed to be in charge of, and the part that stops being anyone’s job the moment the project starts moving fast.

Here is the scene every one of you knows. You start a project clean. You draw the boundaries, you write the rules, you feel briefly like a competent adult. Then the thing grows. People add features, the deadline arrives, and lately a very confident machine writes half the code while you sleep. Six months later (or six hours, if your agents are working hard enough and your account allows it) the shape you designed is a corpse, and there was no murder, only a thousand small reasonable decisions, each one fine on its own, none of them flagged, because who flags a reasonable decision. We call the corpse drift, and we agree to deal with it later, which is to say never.

I used to think this was a tooling problem, the kind a better linter or a stricter pipeline would eventually catch. I have changed my mind. I think it is a language problem, and I think it has been one since long before the machines arrived to make it loud.

Watch what happens when you try to be precise. Ask five engineers what a “component” is. You will get five answers, delivered with total confidence, none of them wrong, none of them the same. Now do it again with “service,” “module,” “layer,” “unit.” We have built an entire discipline on words that mean something different to every person holding them, and then we act surprised when a machine, or a new hire, or an agent, cannot protect a structure we were never able to say out loud. You cannot guard a shape you cannot name.

So here is the idea. I will say it plainly and keep a mostly straight face, because it is simpler than the apparatus we have bolted on top of it.

Software architecture is fractal.

The same shape repeats all the way up and all the way down.

A function has a boundary, a reason to exist, and things it talks to. A service has a boundary, a reason to exist, and things it talks to. A whole system, the same. There is no floor where architecture quietly turns into something that is not architecture. It is one pattern, repeating, zoomed in or zoomed out. The thing you are looking at on every level is the same thing, and the only reason it feels like five different disciplines is that we gave each floor its own vocabulary and then forgot they were all describing the same building.

Which is why I stopped saying “component” and started using one word for the whole stack of it. A unit. And a unit is defined by exactly one thing, the only honest thing you can say about all of them at once.

A unit is anything that is different from its surroundings.

That is the entire definition, and I mean entire. Not its size, not its language, not whether it is a function or a container or a team or a whole company. If you can tell it apart from what sits around it, it is a unit. The function is a unit. The service is a unit. The subsystem is a unit. The system is a unit. They are the same animal in different coats, and once you see that, two stubborn problems get quietly smaller.

The first is description. If everything is the same kind of thing, you can describe it with the same handful of questions, written once, asked at every level. What is this. What does it talk to. What does it need in order to live. Why is it shaped this way. A machine can hold that, because the shape never moves on it. A tired human at two in the morning can hold it. So can an agent, which is the entire point of saying any of this out loud.

The second is drift, which finally gets a definition instead of a mood. Drift is a unit becoming harder to tell apart from its surroundings, or starting to touch things it was never meant to touch. And because the pattern repeats, you do not have to keep the whole system in your head at once like some party trick that gets harder every quarter. You check one unit, at one level, and move on. The impossible audit becomes a stack of small boring ones, and small boring things are exactly what we already know how to automate.

There is one piece left, and it is the one experienced people lose without noticing. The reason a unit is shaped the way it is has to travel with the unit. Right now that reason lives in the architect’s head, because after enough years it stops feeling like knowledge and starts feeling like the obvious, and nobody writes down the obvious. Then someone arrives who does not share the feeling, meets a silence where the reason should be, fills it with a perfectly sensible guess, and the guess is the drift. The knowledge was never missing to the person who had it. It simply never got attached to anything a stranger could read, and an agent is the most literal stranger you will ever hire. Put the reason next to the unit, in plain words, and the machine has something to respect instead of a blank to improvise into.

And now the part that makes me think this is buildable this year and not in some keynote future. We do not have to invent the machinery. Graph databases, policy engines, conformance checkers, observability, type systems, the formal methods people who have been quietly correct for decades. The tools to check all of this already exist. They are just scattered across a dozen research fields that have never been in the same room, and until recently nothing could speak all of their dialects at once. The dull practical move is to write your units in something plain, YAML will do, so that people and models and tools can all read it without a fight, and then pour that into a graph you can actually reason over. The genuinely new ingredient is the one thing that can walk between all those rooms and translate. That is what the machine is for here. Not the magician. The glue.

Which brings me to an old joke that has slowly stopped amusing me. Science began as one thing, philosophy, and then it shattered into physics and logic and linguistics and computer science, each of them off in its own building, proud and partial. And now there is something that can move between all of them, and the walls are coming down without anyone deciding to demolish them, and it is sliding back toward one thing again. Philosophy, with better tooling. Whether architecture is structure, or language, or logic, stops being three arguments and goes back to being one question, which is roughly where it started.

So no, fractal is not the next magic word, and a unit is not a framework with a clever acronym and a TODO list. That is the opposite of what I am offering, and I am not offering. The magic words all promise the work vanishes. This one only points at why the work was never going to vanish, and gives it a shape you can finally hand to someone who is not you.

Two questions, and I would honestly rather have your answers than your approval.

For the ones who like to argue: is software architecture more linguistic than we admit, fractal all the way down, and have we been making it hard on ourselves by inventing a brand new vocabulary at every floor?

For the ones shipping real things on real deadlines: when you build on a large codebase with a machine in the loop, how do you actually keep the architecture from drifting? What works, what did you try and bury, and where does it still hurt? That is the part I want, because I do not believe anyone has truly solved it, and I would rather learn from your scars than guess at mine.

What part of your architecture only lives in your own head, and what becomes of it the day you are not in the room?

(Rewritten by a machine, from my swearing and my pushbacks. I brought the idea and the cursing; it brought the patience to survive both.)

I don’t fit in either of your two groups… but… I treated drift as a feature.

A feature that required a doc written for each subsystem was added.

A feature that made me have to carry, at least the title of, the docs in my head.
A feature that made me take smaller passes.
A feature that made me have to remember to remind the machine when to update the docs when they become stale.

A feature that requires a lot of audits, smoke, and temperance.
The machine can’t carry temperance.

I have to.
I have to carry that and treat drift like a feature, otherwise it’ll make me go mad… and you can only threaten to wire the agents to a Roomba and cut them off from the internet if they don’t get it right so many times.

I’m not nearly as experienced as any of you folks here when it comes to understanding code…
I could do the html, the java, the css… but those are heirlooms more than anything now.

Because of my lack of experience, I had to find the safest way forward…
Which were the smaller passes…
Declare ownership of what files, engines, and helpers can and can’t do…

And watch it like a hawk.
I don’t have the trust that some of you folks do to go to sleep while it’s working.

I need my little codex pet chirping at me as it makes its way towards whatever goal a pass has.

I don’t know if that helps or not, but the answer for me was smaller, measured passes.

And this new feature about recording skills might actually cut through a lot of the problems that make us pull the last of our hair out?

Here’s hoping.

:clinking_beer_mugs:

Speaking of html/css/js combo, this guy does some nice example

BTW, when I started I didn’t event know what a link is…

Golden truth in here. That’s exactly the point.

By the way, if you are wondering what any of this is actually based on, here is the whole pile. I did not invent most of it. I noticed it was all quietly describing the same shape and put it in one place.

The lens is mine, the parts are not.

The framing comes from a few things I have been writing up: the Three-Body Problem of cross-intelligence communication (humans, language models, and deterministic tools, and everything lost translating between them), a Universal Problem-Solving Framework (the recursive idea that a problem and a task are the same object at different sizes), and what I call Form Architecture (message structure as an engineering variable, not decoration). The one slice that is already public and actually running is a small skill that makes a Postgres database describe itself and keep its reasons next to the data instead of in a doc that rots: GitHub - TechSpokes/skill-postgres-introspection: AI Agent Skill for Postgres Database Introspection · GitHub

What architecture actually is.

The old guard already said most of it. Perry and Wolf’s Foundations for the Study of Software Architecture, Parnas on decomposing systems into modules, Shaw and Garlan’s Perspectives on an Emerging Discipline, and ISO/IEC/IEEE 42010 for the formal version of “architecture must be described, not felt.” The SEI books carry it into practice: Software Architecture in Practice and Documenting Software Architectures: Views and Beyond. For the zoom-levels idea there is Simon Brown’s C4 model and its Structurizr DSL, arc42, and even the humble UML Component. The gap between intended and actual structure has had a name for decades, the reflexion model (1995, 2001).

Architecture is a language problem.

Eric Evans’ Domain-Driven Design reference is the whole “everyone must mean the same thing by the same word” argument. Conway’s law is the other half, a system ends up shaped like the communication of the people who built it, and Team Topologies is the modern operational version of that. For the linguistics proper, Paul Grice’s cooperative principle and maxims read like a prompting guide written fifty years early.

The why nobody writes down.

That the reasons live in someone’s head until they feel obvious and quietly vanish is design rationale, Michael Polanyi’s tacit knowledge (“we know more than we can tell”), and Aristotle’s phronesis, the practical wisdom that refuses to become a rulebook. The bandage is to write the reason next to the decision: Architecture Decision Records, the ADR community, and MADR.

Units already declare their edges.

Not new either. Design by Contract and assume-guarantee reasoning are the formal versions, and the industry already writes interface contracts every day: OpenAPI, AsyncAPI, Pact for contract testing, and JSON Schema. To write units and constraints in something plain that a machine can still check, there is CUE, and for meaning that survives travel, the semantic-web stack solved a lot of this already: SHACL for shape constraints, RDF, and OWL.

The fractal, and the rabbit hole I cannot unsee.

“The same shape at every scale” is just self-similarity, which is Mandelbrot’s fractals and, in software, Christopher Alexander’s A Pattern Language. The prettiest version uses quaternions: the usual fractal run with four-dimensional numbers instead of two, so the shape lives in 4D and you slice it down to 3D to see it (Keenan Crane has a clear walkthrough). Now the hunch, not a theorem. Finsler geometry is distance that depends on direction, and the embedding spaces inside these models are anisotropic, where distance also depends on direction. Slice one of those down to 3D and it looks a lot like the fractal on the table. I have no proof that architecture, meaning, and embeddings share one geometry. I just keep seeing the same picture, which is half of why I wrote the post.

The tools that already check this, scattered across fields.

Structural conformance: ArchUnit for Java and NetArchTest for .NET. Code as a graph you can query: jQAssistant, Lattix, and the Code Property Graph behind Joern. General policy over any structured input: Open Policy Agent and Rego. And the “make architecture a test that runs forever” idea: architecture fitness functions and Building Evolutionary Architectures.

Evidence at runtime, and goals with numbers.

Who actually talks to whom comes from OpenTelemetry semantic conventions, and “maintainable” only becomes checkable once it is a number, which is what Google SRE’s service level objectives are for.

And the newest work, where models meet architecture.

People are already pushing on this exact seam: an Architectural Knowledge Management vision paper, Policy as Code, Policy as Type, ARPaCCino for agentic policy-as-code compliance, and Do Large Language Models Respect Contracts?

The post just stacks it in one pile and points at the shape it keeps making.

Your idea of a “unit” has value, especially because it tries to give a common name to something that appears at many levels of abstraction.

One thing it reminds me of is how mathematics and logic often need some primitive notion before the rest of the structure can be built. Different areas choose different primitives: set theory starts with sets and membership, lambda calculus starts with abstraction and application, category theory starts with objects and arrows, and formal logic often has constants such as bottom and top.

Those are not all the same kind of thing, so I would not want to push the comparison too far. But they do show a similar pattern: once a field chooses a small, stable vocabulary, it becomes easier to reason consistently at higher levels.

Thanks, that’s exactly why I kept poking at this. Without fixed primitives, every level invents its own words and you can’t reason across them.

Funny thing about your examples: most aren’t one primitive, they’re a thing plus a relation. Sets and membership, objects and arrows. That’s why “unit” alone wasn’t enough for me. I ended up with three: the unit (the thing), the contract (how they relate), and the environment (the rules around what’s inside).

Your last line is the part I really care about though. A small stable vocabulary doesn’t just make the higher-level reasoning easier, for software it makes it checkable. You can actually catch when the code drifted from what it’s meant to be.

Here is a short ( :rofl:) version of where I’m today:

I have been chewing on something for a while, and I think it is finally simple enough to share. This is a way to describe software architecture that does not fall apart the moment the code moves.

Here is the problem as I see it. Architecture is written down in pieces. Some of it is in diagrams, some in decision records, some in folder names, some in a senior engineer’s head. None of the pieces line up, so the architecture quietly rots while the code keeps shipping. We do not lack documentation. We lack one shape for it.

So here is the shape.

Architecture is fractal. The same structure shows up at every zoom level, from a single method to a whole company. Describe every level the same way and you only need one set of rules.

Everything is a unit. A unit is anything you can tell apart from its surroundings, at whatever level you are looking. A method is a unit. A module is a unit. A team is a unit. A business is a unit. Each unit sits at an address, like a folder path, which is what lets a tool know which units are siblings and which are nested.

Each unit gets three flat contracts. What it is. How it behaves at its edge. How it sits in its surroundings. That is the whole description.

Then there is the environment, which is the part people usually skip. The environment is the law a unit sets for the things inside it. Not the unit’s own behavior, the rules its insides operate under. “My children only talk through the event bus” is an environment rule. A unit is what the unit does; an environment is what the rules are.

And the why. Every contract carries its reason, written as answers to a small fixed set of questions, so the intent stays comparable across units. You only get the full intent of a unit when you read all three contracts together, and that is on purpose.

Now the payoff, and this is the whole thing in two lines.

Your architecture smells when a relationship between two units is not written down anywhere.

Your architecture smells when you change the architecture without capturing why.

The usual tell is a connection that crosses a boundary with no contract for the crossing. Find that, and something is off in how the architecture is built or kept.

A few things this is not. It is not a rulebook telling you how to build. Where you write any of this is up to you: a comment for a method, a manifesto for a module, an agents file for a folder, a standalone doc for a file. How granular is up to you. Who fixes a drift, and which thing wins when two of them disagree, is up to you. The model is detection first. Once you can see the drift, you invent whatever mechanics your team needs on top of it.

The point is to take something that got broken apart across disciplines and tools and make it one simple thing again. Two smells, three contracts, one fractal idea.

Also feel like the specs may be written in a recursive loop answering the same questions at each level starting with the highest one and inward…

Full write-up is here : Architecture as Units, Environments, and Contracts · GitHub

Tear it apart, that is what it is for.

I have thought about drift publicly so much, if you google drift Mcphetridge it might as well be my name.

So what’s your opinion on the above?

My opinion is that the article is mostly correct, but not because of the word “fractal.”

The interesting part is not the geometry. The interesting part is the definition of drift.

Most teams treat drift as a feeling:

“the codebase feels worse than it used to.”

That is not a definition. It is a symptom.

The article proposes a structural definition:

A unit drifts when it becomes harder to distinguish from its surroundings or begins interacting with things outside its intended constraint boundary.

That is a definition a machine can actually work with.

I also think the linguistic argument is stronger than many engineers are comfortable admitting. We routinely use words like component, service, module, layer, domain, capability, workflow, and agent as if they are precise. They are not. Different people mean different things, and architectures inherit that ambiguity.

My own work repeatedly arrives at a similar conclusion from a different direction:

constraint must travel with structure.

The architecture is not the code.
The architecture is the reason the code is shaped the way it is.

When the reason remains trapped inside one person’s head, drift becomes inevitable because every future change is forced to reconstruct intent from artifacts rather than declarations.

The article’s proposed “unit” abstraction is interesting because it attempts to make architecture scale-invariant. A function, service, subsystem, organization, or ecosystem can all be described using the same constraint vocabulary.

Whether “fractal” is ultimately the right word matters less to me than the operational consequence:

Can the architecture explain itself well enough that a stranger, a new hire, or a reasoning system can preserve the boundary without asking the original author?

That is the real test.

The strongest idea in the piece is not that architecture is fractal.

It is that drift is fundamentally a loss of distinguishability.

That is a definition worth exploring.

I wrote this about the topic. The Apple represents recursive universalization.
https://philarchive.org/rec/MCPOAA-3

Exactly.

I was working on adding the relation part but just did not know how to do so, you opened the door nicely.


Agreed.

As you know, I am a fan of Lean 4, among other approaches to proving software properties. Lean 4 is rooted in typed Lambda Calculus, and from a programming perspective it can be viewed as a functional language with a very strong type system.

The major benefit is that parts of the code, or the reasoning about the code, can be formally proven. That is much stronger than unit testing. Unit tests can show that selected examples behave as expected, but a proof can establish that a property holds for all valid cases covered by the statement.

That is where your point about a small, stable vocabulary seems especially important to me. Once the vocabulary is precise and stable enough, the higher-level reasoning is not only easier for humans to follow, it also becomes something that tools can check.

Ask what it is not, not what it can be, one builds boundaries the other builds expansion

Definitions by capability expand indefinitely.
Definitions by exclusion create boundaries.
Boundaries are what make units distinguishable.
Distinguishability is what makes architecture possible.

That’s just a title to show my “internal vision”, the point was about stop labeling things randomly, find common basis then see how that scales and what can be done with that.

Seems right direction, if you can reason and prove your reasoning, that’s gold.

From our language standpoint “distinctness” is a thing that makes anything “a thing” or it just merges in the surroundings and stops being perceived.

So @EricGT @mitchell_d00 what do we do with that? Any ideas how to test the concept “fast and dirty” to see if:

  1. Architecture can be captured at several levels by recursive questioning of “units”
  2. Architecture can be converted into an object with a state
  3. State can be reasoned about
  4. State changes can be detected
  5. State changed can be reasoned about to spot “drift”

Would love your ideas here

Indeed let’s use my apple as an example a apple is not a seed it is not a tree apple = apple then if one thinks in possibility space apple = seed ,apple = tree ,apple = pie , apple = cider , apple = water , apple = sun because we are building a relationship ontology apple will eventually = everything and = nothing distinct. Single substance ontology / substance theory suffer from that kind of failure mode.

The address of a unit (from my legal analysis tool) is what allows every unit have and ID and a reference in the system…

Apples are made of sun, so it is technically true, it’s just a part of the truth returned because the reasoner had no “attention focus” (what to look at)

Yes all the relations I listed are technically “true” but without boundaries as to what apple is not the “truth” is meaningless once it loses distinction.

Recursive relational expansion naturally destroys distinction unless some exclusionary process counterbalances it.

If you are interested I go into detail here.

https://philarchive.org/rec/MCPURE

Before I answer: where is “fast and dirty” defined or referenced here?

I can answer the questions by grounding the idea in typed Lambda Calculus or a similar formal framework, but I would rather answer your actual question than appear to be pushing my own agenda.