Software architecture should be open
“The manipulation (of the design) suggests a mandatory behaviour, and in architecture, that is not desirable because in every space, will be someone different, that will perceive the space differently in his mind. There must be a margin for freedom. The path is there. What establishes the order is the succession of spaces.” - Siza Vieira
Álvaro Siza-Vieira is one of the most prestigious architects of all time. In a recent interview, he said that architecture “should be open”. By this, he meant buildings should accommodate multiple purposes, not dictating how people use them, but adapting to whoever occupies the space.
This resonates perfectly with software, because the engineers who build a system rarely maintain it long-term, but yet, we design as if we can predict every future need, locking future maintainers into our assumptions. And even considering that buildings are static, not changing much over time, I think it’s fair to acknowledge that the same often applies, unfortunately, to software. Doing building works is very expensive, as in software. Therefore, and making a parallel, maybe software architecture should be open too. But what it means to software architectures to be open?
The challenge towards openness
Being open means being easy to change. There are countless books that promise solving that (e.g. Clean Code, Clean Architecture). So why, despite countless books on Clean Code and Clean Architecture, is this still a problem?
Because we treat patterns as prescriptions. Someone reads “dependency inversion principle” and creates seven layers of abstraction for a simple CRUD app. The very practices meant to reduce complexity become sources of cognitive load, which is the exact opposite of the original authors intent, effectively making programs harder to manipulate.
But in turn, does that mean that architectures are useless? I wouldn’t say so! There are, after all, really good ideas and teachings in sophisticated architectures such as Clean Architecture, in blogs (for instance: Postpone-Architecture) and really good debates concerning on the relative importance between good design and consistency:
- We can all agree that the advocacy for the dependency inversion principle which is omnipresent in Clean Architecture is excellent because it can help us to separate the core logic from specific integrations (HTTP, GRPC, or databases) we might have.
- We can all agree that insufficient requirements will mean more changes ahead, so stricting to a strong opinionated architecture can lead to an initial slower development time, which might be incompatible to the project. We can also agree that over time, the form of the project will converge towards something which can be refactored to be easier to properly implement.
- Finally, in an organization, software tends to be done in a certain way, which makes the developers “think less” to effectively deliver - while they might (and believe me, they will) repeat the same mistakes. So why follow or even, know rules?
Anarchy is not the absence of rules but rather the absence of oppression
As an old teacher of mine once said, “architects are the reason why we don’t have toilets in the middle of a kitchen”. We have the freedom to do it, but reasons not to.
At the beginning, having space and freedom helps but when there are several and similar projects, certain dark traits surface: and that’s when software architecture and design knowledge might help the most.
This is why understanding architectural principles matters: not to follow them religiously, but to know when they serve your needs and when they don’t.
Here is what I advocate:
- Learn from the canon: understand the concepts, their strengths and weaknesses.
- Apply them contextually: treat them as guidelines, not as strict rules.
- Optimize for simplicity: if a pattern or practice adds complexity, question it.
Rules have a special place in my heart: by knowing them, I know when I can use them in my / team favor and when it’s fair to go ahead and break them.
Some practical examples
- Once I worked in a huge payment stack, were the integration was being done solely via HTTP. Nonetheless, there were a lot of Java interfaces. My engineering manager once told me: “I know that Spring Magic™ will match the bean complying with this interface, but let’s be honest: for us engineers, isn’t this simply indirection? I mean, doing an interface, for a single concrete class implementation, that will likely never to be changed or replaced? By the book we can have complete contradictory directions: creating an interface for support polymorphism (but only one class) and creating the interface to help DI to work regardless of the implementation. I guess the two are correct but you are not going to need it.”
- Back in 2018, I recall my first job, as an Android engineer. It was both interesting and complex. Even though I was too inexperienced for the position, it was a complex engineered piece, with at the least 3 to 5 architectural patterns built in: Clean Architecture (employed specifically to deal with HTTP requests), MVP (to implement Android views and the corresponding logic), Event Bus (to make notifications and send data between different sections of the app), and a lot more I can’t remember it all. The main thing I can recall, besides my failure as a proper engineer at time, was how hard was for newcomers to adapt to such “Howl Moving Castle”. It was among one of the most interesting things I’ve worked with but extremely hard. Even well experienced developers had an hard time configuring some aspects of the app. My learning from it was that, even if you use a solid architecture, it doesn’t mean that the code will be simpler to maintain. The complexity of business requirements and amount of features can’t save you from that. I’m sure that in this case, that it started to be open, and then, it had to go that way. It was inevitable, but thanks to some rules, they could evolve the app in something manageable.
Ending note
Like Siza’s buildings, that should adapt to their occupants, software architecture should adapt to changes without dictating them. The goal isn’t to eliminate structure, but rather to empower the people who work with it to evolve it, and not the other way around. After all, code is done for humans.