A Software Architect’s Philosophical Notes #3

A Software Architect’s Philosophical Notes #3

The Model Is Not Reality

Imagine attending a meeting to design a new software system. Around the table are developers, analysts, domain experts, and someone responsible for making architectural decisions. At some point, someone says a word.

Customer.

Everyone nods. We know what a customer is. It doesn’t seem like a particularly difficult concept to understand. A customer has a name, an address, perhaps a tax identification number. They can place orders, receive invoices, and make payments.

We start designing the model. We introduce an entity, give it an identity and a few properties, and establish relationships with other entities. After a while, we have something that seems to represent the problem reasonably well. At least until someone asks a seemingly innocent question.

Is someone who has never purchased anything still a customer?

It depends. From the sales department’s perspective, probably yes. From accounting’s perspective, perhaps not. And what about someone who purchases on behalf of an organization? Is that person the customer? Or is the organization the customer? Suddenly, a word that seemed so clear begins to lose its solidity. Not because we necessarily defined it incorrectly.

But because we have started to mistake our representation for something that already existed, in exactly that form, in the world we were trying to understand.

Things Don’t Come Pre-Modeled

When we design software, we constantly perform operations like these. We observe part of reality, recognize patterns, distinguish concepts, and give them names. Then we decide which characteristics to preserve.

A Customer has an identity. An Order has a state. A Product has a price. We establish relationships, define constraints, and introduce behaviors. It’s a necessary process.

We couldn’t build software without reducing the complexity of the world to something we can represent and manipulate. But there’s something we rarely stop to consider.

Reality doesn’t hand us its entities, neatly packaged and ready to become classes, tables, or Aggregates.

We’re the ones who identify them. We’re the ones who decide where one concept begins and another ends. And, most importantly, we’re the ones who determine which aspects of a concept are worth representing.

This doesn’t mean every interpretation is equally valid. There are constraints, facts, observable behaviors, and consequences that can support or challenge our decisions. It does mean, however, that a model, no matter how accurate, never completely coincides with what it represents. The distinction seems obvious. And yet, while developing software, it’s surprisingly easy to forget.

A Question That’s 2,400 Years Old

In the previous article, we encountered Parmenides and Heraclitus, two philosophers who, from different perspectives, challenged us to reflect on what remains stable and what changes.

Plato, who lived between the fifth and fourth centuries BCE, also explored the relationship between what we perceive and what we can know. In his philosophy, we find a fundamental distinction between the sensible world, the one we experience through our senses, and the world of Forms, which Plato considered the intelligible, stable, and ultimately true reality. The particular things we encounter are changing and imperfect. The Forms, by contrast, make it possible to understand what those things are.

Of course, we cannot transfer this conception directly into software architecture. A Customer is not a Platonic Form. And Plato certainly wasn’t trying to solve the modeling challenges of an information system.

But his philosophy allows us to recognize a very familiar temptation. The temptation to treat our model as the true and definitive form of things.

We’ve identified a concept, described it, established its properties, and defined the rules governing its behavior. At that point, the model acquires a certain authority. We stop asking whether Customer accurately represents the domain. Instead, we begin reasoning as though the domain itself must behave according to the definition of Customer we’ve written in code.

The problem is no longer merely technical. It has become a problem of knowledge.

Shadows on the Wall

In Book VII of The Republic, Plato presents one of the most famous allegories in the history of philosophy: the Allegory of the Cave.

Imagine a group of prisoners chained inside a cave since childhood. They cannot turn around. They can only look at the wall in front of them. Behind them burns a fire, and between the fire and the prisoners, people pass by carrying various objects. The prisoners see only the shadows projected onto the wall. Knowing nothing else, they come to regard those shadows as reality itself. One of them, however, is freed. He can turn around, observe what produces the shadows, and eventually leave the cave. What he discovers challenges everything he had previously believed to be true.

The allegory concerns the journey toward knowledge and the transition from appearances to understanding. But, with some caution, we can also use it to reflect on our work. Imagine that the shadows are the information we’ve chosen to represent in software. The states of an order. The properties of a customer. The categories of a product. The rules describing a process. They aren’t necessarily false. After all, the shadows in Plato’s cave aren’t inventions of the prisoners. Something produces them.

The problem arises when we forget that we’re looking at a representation and begin treating it as the whole of reality.

When Reality Starts Adapting to the Model

But there’s something even more interesting. Our representations don’t necessarily remain separate from the reality they attempt to describe. They can change it.

Imagine introducing three possible states for a request in a software system:

Pending, Approved, Rejected.

It’s a simple, understandable classification, probably sufficient to satisfy the initial requirements. But in the real world, a request might be suspended, withdrawn, conditionally approved, or postponed pending further investigation. If the software doesn’t support these possibilities, people must find a way to keep working anyway.

Perhaps they use Rejected even when they simply mean to postpone a decision. Perhaps they introduce an informal convention. Perhaps they use a notes field to preserve information that the model cannot express. Over time, these workarounds become habits.

And a few years later, when a new architect tries to understand the domain, someone might explain that a request can only be Pending, Approved, or Rejected.

Because that’s how the system works. And by now, that’s how people have learned to work as well.

The model we built to represent a business practice has started shaping that practice itself.

At this point, distinguishing between the model and reality becomes even more difficult. Because we’re no longer observing merely an incomplete representation. We’re observing a reality that has, at least in part, adapted to that representation.

Leaving the Cave Doesn’t Mean Abandoning Models

It would be a mistake to conclude that we should distrust every abstraction and give up modeling the domain. We have no alternative. A software system cannot contain all the complexity of the world. Nor should it try.

A model is useful precisely because it selects, simplifies, and allows us to reason about something that would otherwise be too complex.

The question, then, isn’t whether our model is complete. It never will be. The question is whether we’re still aware of the choices we made when building it.

Why did we decide that particular property was important?
Why did we represent two concepts as a single entity?
Why did we introduce those specific boundaries?
And, above all, which aspects of the domain did we choose not to represent?

We can’t always answer. Sometimes those decisions were made years earlier. Sometimes they were implicit. Other times, we didn’t even realize we were making them. But we can continue questioning them.

We can return to the people who actually work in the domain. Observe what they do, not merely what the system allows them to do. We can look for exceptions, informal conventions, and situations that don’t seem to fit into our model. Not to finally construct a perfect representation. But to verify whether the one we have is still useful.

Our Own Shadows

Perhaps the greatest risk for a software architect isn’t building the wrong model. We will make mistakes. That’s inevitable. The real risk is losing sight of the fact that the model is the result of our own interpretation. The more elegant, coherent, and well-structured an architecture becomes, the harder it can be to question it.

Boundaries begin to seem natural. Responsibilities begin to seem inevitable. Rules appear to belong to the domain itself rather than to our understanding of it. And perhaps it’s precisely when a model starts to seem self-evident that we should become a little more skeptical of it.

Plato doesn’t teach us how to design an Aggregate or where to place a Bounded Context. But his cave can remind us how easily we become accustomed to our own representations. And how difficult it can be to recognize them for what they are.

We don’t need to stop building models. We need to avoid becoming their prisoners. So, the next time we look at an architectural diagram, a class, or a domain model, perhaps it’s worth asking ourselves a question.

Are we looking at the problem we wanted to understand, or merely at the shape we once decided to give it?

Condividi questo post...

Leave a Reply

Your email address will not be published. Required fields are marked *