Software Architecture must evolve - or it becomes the Problem
Architecture is often treated as something written in stone. In reality, software continuously changes - and architecture must evolve with it. The real danger isn’t changing architectural decisions. The danger is when they never change at all.
In many organizations, software architecture is treated like a monument - something designed once, etched in stone, and feared by those who have to change it. This rigidity is the first sign of a failing system.
I believe architecture is a journey, not a destination, and if the system is continuously modified its architecture should evolve as well. The problem is not when you change an architectural decision after a year of continuous development. The problem begins when the architecture remains the same.
Decisions (not) written in stone
Years ago I read an interesting statement about software architecture:
"Architectural decisions are design decisions that are hard to make or costly to change."
Olaf Zimmermann
And I have to admit that the way of thinking represented by this quote was the opinion I shared for some time. I also liked it - it tells us that architectural decisions are not easy. They require deep thinking, experience, and knowledge, and once a decision is made the cost of change is something we cannot ignore.
The second part of the statement strengthens the first. If decisions are expensive to change, then it is definitely worth investing time in making them well. It is worth evaluating multiple options, perhaps building a few proof‑of‑concepts, before committing to the final direction.
At the same time, this sentence indirectly suggests that once architectural decisions are made, they will stay with us for a very long time. And let’s be honest - no one wants to invest money in a costly change until it becomes almost inevitable.
The dangerous conclusion
This way of thinking can lead to a dangerous conclusion: that architecture is something written in stone. Once a decision is made, it should not be touched. It makes software architecture sound almost like a fairy tale - long ago a group of people fought the great battle of design decisions and won. Now we simply live in the kingdom they built, even though years later the circumstances are completely different.
I believe this thinking is dangerous. Software must evolve, and the pace at which we can introduce change depends heavily on architecture. That is why, instead of treating architecture as something distant or intimidating, we should apply a similar mindset to what DevOps taught us about releases: if you are afraid of something, do it more often.
If the current architecture becomes an obstacle or stops supporting the system’s growth - change it. And by evolving it regularly, you will learn how to make decisions that support continuous change instead of resisting it.
How about making sure we are just ready for change?
If architectural decisions can be expensive to change but must also support future evolution, perhaps we should simply spend more time during the design phase gathering information about future directions and making decisions that prepare us for them.
Unfortunately, this is much easier said than done.
The problem of time
The first challenge is time. How much additional effort should you invest in collecting requirements? When will you know that you have enough information? How do you decide when to stop? Should it be time‑based, scope‑based, or should you simply wait until someone says “that’s everything”?
The problem of certainty
The second challenge is certainty. Even if you gather many requirements, how confident can you be that they will remain stable? Many of you probably recalled the famous quote while reading this: "The only constant in life is change."
You can ask these questions and attempt to answer them, but in the end you will most likely reach the same conclusion: there is no amount of time that will allow you to gather all requirements, and preparing for every possible future scenario would be extremely expensive.
And let’s be honest - this entire exercise assumes architects are not already trying to make informed decisions. In reality, every good architect does their best to make decisions based on the best information available at the time.
The trap of designing for everything
The situation becomes even trickier when we realize that not only will requirements change, but even our understanding of them will evolve. Instead of relying on certainty - which we already know is impossible - maybe we should try to build software ready for any possible extension.
At first glance this might sound like a good direction. However, the moment we start thinking about implementation, the problems quickly appear.
First there is the time required to build such a system. Then there is the cost. For the moment, let’s assume those constraints do not exist and continue the thought experiment.
Even in that ideal scenario the result would not be pleasant to work with.
Maintaining such software would quickly become overwhelming. Developers would constantly wonder why there are so many abstractions, layers, and interfaces in places where only a single well‑established implementation exists. The codebase would be filled with structures prepared for hypothetical futures that may never arrive.
Engineers would spend hours navigating through code only to discover that large portions of it are not even used. And all of those unused elements would still need to be maintained.
If we tried to keep the system “ready for everything,” every future change would have to follow the same pattern of extreme generalization. Over time the amount of unused or speculative code would inevitably grow. Eventually the system could contain more unused logic than real functionality.
So no - designing software ready for every possible future change, even if it were feasible, would not produce a system anyone would enjoy maintaining.
Why change is inevitable
But why is change so inevitable in the first place? What are the real chances that something unexpected will appear in the future?
While it is difficult to provide precise numbers, experience strongly suggests that the probability is very high.
You want to build something successful
Unless the system exists purely as a learning exercise, every product is created with the hope of success. And success almost always brings change. Clients request extensions. Users ask for improvements. Stakeholders demand new features. Bugs are discovered and must be fixed.
It is extremely rare to build software and never return to it again. And if success is the goal - which it usually is - then we are indirectly inviting continuous change.
The more you do, the more you learn
When the first version of a product is built, the team has only a partial understanding of the business domain. The application contains just enough knowledge to solve the problems currently known. Not more - because unnecessary complexity only introduces noise. Not less - because missing concepts make the system harder to understand and evolve.
As the system grows, new features are introduced and existing ones are modified. Some of these changes react to market forces or regulations that nobody predicted earlier. Through this process the team learns more about the business domain. New concepts appear, relationships between ideas become clearer, and previous assumptions must be reconsidered. That learning inevitably reshapes the way domain knowledge is structured inside the system. Some parts grow and must be split. Others converge and should be merged. And every structural adjustment of domain knowledge translates into architectural evolution.
If the architecture cannot accommodate that learning process, teams usually fall into one of two problematic scenarios:
- The existing structure simply keeps expanding. More and more responsibilities accumulate until it becomes a massive bottleneck that nobody fully understands.
- The team realizes that the structure no longer supports the growing knowledge of the domain. Yet, remember that "spotting the problem" is not the same as getting the chance to fix it. This is the moment when difficult conversations with stakeholders about investing in architectural improvements starts.
Everything is moving forward
Architecture must also evolve because the ecosystem around our systems evolves as well. Libraries, frameworks, programming languages, platforms, and infrastructure continuously move forward. Imagine building an application today using Java 21, Spring Boot 4, and Apache Kafka 4 - the latest versions available at the time of writing. Excellent choices today. But in a few years new versions will appear. Features will be added, others deprecated, and eventually removed. Some solutions may even lose support entirely.
If the application becomes tightly coupled to those technologies, upgrades will gradually become painful. At first the team may postpone an update “just for now.” Unfortunately, those temporary decisions tend to accumulate until the moment arrives when an upgrade becomes urgent. If that urgency is caused by internal planning you might still negotiate more time. But if it is triggered by a critical security issue in your current version, there will be no room for delay.
And with an architecture that does not support such evolution, teams end up modifying code under pressure and hoping everything still works in production - a situation nobody wants to face.
New solutions no one predicted
Finally, architecture must evolve because the industry itself keeps inventing new approaches.
There was a time when Model‑View‑Controller (MVC) dominated application design. Later we saw the rise of Event‑Driven Architecture, Microservices, Containerization, Cloud platforms, and Data Mesh. Today discussions include ideas like AI agent orchestration, GraphRAG architectures, or even Quantum‑as‑a‑Service.
Of course this does not mean every application must adopt every new trend. Software architecture is not a Pokémon game where the goal is to collect them all. The correct solution always depends on the problem and the context. However, if a strong business case appears for adopting a new capability, it is far better to work with a system whose architecture allows such evolution rather than one that resists it.
Summary
Today software is responsible for more critical parts of our world than ever before. At the same time, the speed of innovation can dramatically accelerate how products grow.
In such an environment architectural decisions must support evolution rather than prevent it.
Change will come. In some cases it will be incremental. In others it will be disruptive.
I am curious about your experience.
- What do you do to keep architecture evolvable?
- What were the biggest architectural problems you faced when systems needed to change?
Feel free to share your thoughts and experiences - I would love to continue this discussion.
Comments ()