There’s a quiet confusion that has lingered in our field for years. We talk about software architecture as if it were a discipline separate from programming, almost a higher art, when in truth it has always been there, embedded in the act of programming. What changed wasn’t the need for architecture. What changed was the world around it.

When the Programmer Was Everything

If you started working with software more than 25 years ago, or at least studied how things worked back then, you know the central figure was the programmer. There was no “software architect” as a job title. No “tech lead”, no “staff engineer”, no “platform engineer”. There was the person who understood the problem and wrote the code.

That doesn’t mean there was no architecture. There was. But it was a natural consequence of the work. The programmer who was going to build an inventory system for a shop thought about structure, defined how modules talked to each other, decided what would be separated from what. They did it because they had to, not because someone created an org-chart role for it.

Kent Beck tells how in the early days of Extreme Programming, on Chrysler’s C3 project in the late nineties, the idea was exactly this: the whole team was responsible for the architecture. There was no guy on a pedestal drawing boxes and arrows while everyone else just executed. Everyone thought, everyone coded, everyone decided together.

And it worked.

The Industrialization That Complicated Everything

Then software became a real industry. Teams grew. Projects got bigger. Companies started hiring 50, 100, 500 developers for a single product. And what happens when you put 500 people together to build something? Communication becomes the bottleneck.

Fred Brooks had warned us back in 1975, in The Mythical Man-Month: adding more people to a late project only makes it later. And the main reason isn’t lack of technical competence. It’s the cost of communication. When you have 5 people, there are 10 possible communication channels. With 50, it’s 1,225. With 500, it goes past 124,000. The math is merciless.

It was in this context that software architecture gained weight as a discipline. Not because programmers didn’t know how to organize code, but because someone had to organize people. And here’s a truth few people say out loud: a good chunk of what we call “software architecture” is, in practice, a mechanism for managing the complexity of human communication.

Take microservices, for example. The technical motivation is real, sure — independent services that can scale separately, be deployed individually, use different technologies. But what really drove the mass adoption of microservices in large companies? Conway’s Law. The idea that the structure of the software will mirror the communication structure of the organization. Spotify didn’t create squads and tribes by accident. They realized that the way people organized themselves defined the way the software was built. Architecture became a reflection of the org chart.

The Brutal Mistake of the Civil Engineering Analogy

Now, here we need to talk about one of the most persistent mistakes in our field: the comparison between software and civil construction.

Everyone has heard some variation of “building software is like building a bridge” or “we need real engineering, like in construction.” It seems to make sense on the surface. You have requirements, a project, a construction phase, and a delivery. But this analogy hides a fundamental difference that changes everything.

A bridge, once built, doesn’t change. It’s designed to support a specific weight, resist winds of a certain speed, last 100 years doing exactly the same thing. If someone shows up and says “now this bridge needs to be 30% wider and support trains in addition to cars,” the answer is simple: you build another bridge.

Software doesn’t work that way. Good software is the kind that changes. That adapts. That evolves. A system that can’t be modified without pain is a system that’s dying. Martin Fowler talks about this a lot when he discusses evolutionary design. Uncle Bob — Robert C. Martin — goes even further and argues that the main metric of a good design is the cost of change. If changing is cheap, the design is good. If changing is expensive and risky, the design failed, no matter how “elegant” it looks in the diagram.

And there’s that idea — I can’t remember exactly who first said it, but it lands hard: software rots. It’s not an exaggerated metaphor. It’s what actually happens. A system that doesn’t get maintenance, that isn’t adapted, that stays still while the world around it changes, starts to rot. Dependencies get out of date. The abstractions that made sense three years ago now get in the way. Integrations with external systems break because the other side’s API changed. The software that seemed solid becomes a burden, a weight, a legacy in the worst sense of the word.

Civil engineering builds for eternity. Software engineering builds for change. And that difference should completely change how we think about architecture.

Architecture as Intention, Not as Documentation

Here we get to what I consider the most important and most misunderstood point: what a good architecture should actually be.

I’m not talking about that architecture of a 200-page document, 47 UML diagrams, detailed specifications of every class and every method before writing a single line of code. That model, “Big Design Up Front,” has been criticized extensively, and in practice everyone who has worked on a project like that knows half those documents are out of date before the first sprint ends.

But I’m also not talking about no architecture at all, that “let’s just code and see what happens” mindset. That works when you’re alone on a personal project. On a team of 20 people under business pressure, it becomes chaos in weeks.

Good architecture lives in a middle ground that’s hard to define but easy to recognize when you see it. It’s about intention. About making clear what problem we’re solving, what the limits of the system are, where the boundaries between responsibilities are, and which decisions we’re consciously making versus which we’re deliberately leaving open.

A practical example: imagine you’re building a payments system. A good architecture isn’t going to specify whether you should use PostgreSQL or MongoDB, whether the notification service should be written in Go or Python, or whether the message queue should be RabbitMQ or Kafka. It will say things like: payment processing is the core of the system and has to be isolated, with clear input and output contracts. Customer notification is a side effect that can’t block the main flow. Financial reconciliation has to work idempotently because it’ll run as a batch and may be re-executed.

See the difference? The architecture defines the intentions and the important constraints. The tools, the languages, the implementation details stay open. That gives the team the flexibility to pick the best solution for each part, to change their minds when they learn something new, to adapt the system without having to throw everything out.

Simon Brown, creator of the C4 model for architecture documentation, has a phrase that sums it up well: “architecture is about the significant decisions, where significant is measured by the cost of change.” If swapping the database is easy because you isolated the persistence layer well, then the choice of database isn’t an architectural decision. If swapping the communication model between services means rewriting half the system, then yes, that’s architecture.

The Age of AI and Why Intention Matters Even More

Now let’s get to the present, and the future that’s already knocking.

With the advance of AI tools for code generation, the ability to write code is becoming less rare. Tools like Cursor, Copilot, Claude Code — all of them can generate functional code from natural-language descriptions. And they’re getting better fast.

This changes the architecture equation in a way many people haven’t noticed yet. If generating code got easier and cheaper, what got more valuable? The ability to define what needs to be built. Intention.

Consider this scenario: you ask an AI to generate an authentication service. It will generate something. It’ll probably work. But if you didn’t specify that it needs to support multiple identity providers, that tokens need a specific lifecycle, that the system needs to work across multiple regions with acceptable latency, the AI is going to make decisions for you. And those decisions can be perfectly reasonable for one scenario and completely inappropriate for yours.

Architecture, in this context, isn’t about controlling how the code is written. It’s about defining the space of acceptable solutions. It’s about saying “within these limits, any solution that works is good.” That applies to humans and applies to AIs.

And here we come back to the question of specification. Not heavy specification, with hundreds of documents nobody reads. But specification as the act of thinking clearly. Of writing, in a few words, what the system needs to do, why it needs to do it, and what the real constraints are (not the imaginary ones). That’s what separates a prompt that generates useful code from a prompt that generates code you’ll throw away in two weeks.

Grady Booch, one of the fathers of UML and object orientation, said something that became even more relevant now: “the function of architecture is to reduce complexity.” It’s not to add complexity with more layers, more patterns, more abstractions. It’s to reduce. It’s to make the system understandable enough that any person — human or artificial — can work in it without fear of breaking everything.

The Invisible Contract: Definition of Ready and Definition of Done

And since we’re talking about intention, about clarity, about reducing ambiguity without falling into bureaucracy, it’s worth closing with something that seems simple but in practice separates teams that work from teams that live in firefighting mode: Definition of Ready and Definition of Done.

Most people know these concepts from Scrum and treat them as ceremony. One more checklist to fill out, one more process artifact. But when you look carefully, DoR and DoD are architecture tools disguised as process practices.

Think about Definition of Ready. What is it really saying? It’s saying: before anyone starts working on a task, we need enough clarity about what needs to be done. Not absolute clarity, not a 40-page spec. Enough clarity. That means the problem is understood, the dependencies have been identified, the acceptance criteria exist and make sense. It’s a filter against destructive ambiguity — the kind that makes three people on the same team interpret the same story in completely different ways and only realize it at review.

In a context where AI is generating code, Definition of Ready takes on a new dimension. Because what is a DoR if not a well-written prompt? When you define that a task is only “ready” when it has clear context, defined acceptance criteria, and explicit constraints, you’re essentially saying: this task can be handed to any executor — a junior developer, a senior, or an AI — and the result will be predictable. The quality of the input determines the quality of the output. That applies to people and applies to language models.

Now Definition of Done. This one is even more architectural than it seems. The DoD is the team’s quality contract with itself. When we say something is “done,” what does that really mean? The code was written and compiles? It has tests? It was reviewed? It’s in production? Documentation was updated? Monitoring is configured?

Every team will have a different DoD, and that’s fine. The point isn’t to have the perfect DoD. The point is to have one. Because without it, “done” becomes a word everyone interprets however suits them. The developer thinks it’s done when the code works on their machine. QA thinks it’s done when it passes the tests. The product owner thinks it’s done when the customer can use it. And then what happens? Rework, frustration, bugs in production, that tense meeting where everyone points fingers at someone else.

An example I’ve lived more than once: a team without a clear DoD shipped features that “worked” but without automated tests, without adequate logging, without observability metrics. In staging everything looked beautiful. In production, when something broke, nobody knew where, nobody knew why, and fixes took days. The day the team decided that “done” included integration tests, structured logs, and at least a basic health alert, delivery velocity seemed to drop in the first month. But in three months, real velocity — the velocity of delivering value that stays standing — had doubled.

And this connects with everything we’ve talked about. DoR is architecture at the entrance: it guarantees that intention is clear before starting. DoD is architecture at the exit: it guarantees that the result meets the standard that allows the system to keep evolving. Together they form a contract that protects software against the rot we mentioned earlier. Because software doesn’t rot just from time. It rots from accumulated decisions without criteria, from shortcuts nobody agreed on, from “dones” that weren’t really done.

At the bottom, DoR and DoD are architecture operationalized in the day-to-day. They’re how architectural intention stops being a pretty document nobody consults and becomes a team habit. And habits, unlike documents, scale.

What Really Matters in the End

If I had to reduce all this to one central idea, it would be this: software architecture exists to allow software to change.

Not to look pretty in the diagram. Not to impress in interviews. Not to create job titles. It exists so that, two years from now, when the business changes, when the technology evolves, when a new requirement appears out of nowhere, the system can adapt without having to be rewritten from scratch.

That was true when a lone programmer built systems in Delphi in the nineties. It’s true today with teams of hundreds of people using microservices in the cloud. And it will keep being true when most of the code is generated by AI and the human’s role is to define the intention, the direction, and the limits.

The tool changes. The language changes. The paradigm changes. But the fundamental question of architecture stays the same: how do we organize this in a way that allows us to change tomorrow without suffering?

If your architecture answers that question well, it’s doing its job. Everything else is detail.


References

Beck, K. (1999). Extreme Programming Explained: Embrace Change. Addison-Wesley. The account of Chrysler’s C3 project and the philosophy that responsibility for architecture belongs to the whole team, not to an isolated individual.

Brooks, F. P. (1975). The Mythical Man-Month: Essays on Software Engineering. Addison-Wesley. The classic that formalized the idea that adding people to a late project makes it later, and the analysis of the exponential cost of communication in large teams.

Conway, M. E. (1968). “How Do Committees Invent?” Datamation, 14(4), 28–31. The original article that gave rise to Conway’s Law, arguing that the structure of a system reflects the communication structure of the organization that built it.

Fowler, M. (2004). “Is Design Dead?” Originally published at martinfowler.com. A reflection on evolutionary design versus Big Design Up Front, and how agile methods changed the relationship between design and implementation. Available at: https://martinfowler.com/articles/designDead.html

Martin, R. C. (2017). Clean Architecture: A Craftsman’s Guide to Software Structure and Design. Prentice Hall. The argument that the quality of an architecture is measured by the cost of change, and that good abstractions protect software from aging.

Brown, S. (2018). The C4 Model for Visualising Software Architecture. Available at: https://c4model.com. The model that proposes documenting architecture across abstraction levels (context, container, component, code) and the idea that architectural decisions are those whose reversal cost is high.

Booch, G. (2006). “On Architecture.” IBM blog. The reflection on architecture’s role as a complexity-reduction mechanism, not a complexity-addition one.

Foote, B. & Yoder, J. (1997). “Big Ball of Mud.” Presented at the Fourth Conference on Patterns Languages of Programs (PLoP ‘97). The paper that describes how software systems tend to degrade toward an amorphous, structureless mass when continuous architectural care is absent. One of the most cited references on the concept of “software rot.” Available at: http://www.laputan.org/mud/

Kniberg, H. & Ivarsson, A. (2012). “Scaling Agile @ Spotify with Tribes, Squads, Chapters & Guilds.” The whitepaper that documented Spotify’s organizational model and how team structure directly influenced the architecture of the software produced.

Schwaber, K. & Sutherland, J. (2020). The Scrum Guide. Available at: https://scrumguides.org. The official definition of Definition of Done as the team’s quality commitment, and the framework that popularized DoR and DoD as work-management practices.

Hunt, A. & Thomas, D. (1999). The Pragmatic Programmer: From Journeyman to Master. Addison-Wesley. Introduces the concept of “software entropy” and the broken-windows metaphor applied to code, arguing that small ignored degradations accelerate the rot of the system as a whole.