
Architecture
Bronislav Klučka, Jul 22, 2025, 03:21 PM
"Software architecture is design of API and database and shoudl be robust and stable."
Almost nothing in the previous sentence is correct. Modern software architecture is very different.
2 things are happening right now with my current customer that prompted me to write this article. They are recruiting for developers and often when they talk about their last job I am met with the statement "and I did architecture". When I start to pry, it's API and database design. With all due respect, that's not architecture, those are too tactical decisions. And the team at the customer is currently writing a new version of the API and focusing on how to implement DDD, the directory structure and how to do it "correctly and stably". That's not even architecture, too tactical... In all the cases mentioned, the main thing is missing. What is actually the goal of the architecture. What drives the decisions mentioned above.
What is software architecture
Here you go:
The goal of software architecture is to ensure that the software and its accessories can be maintained and evolve over time within the boundaries set by the stakeholders for the benefit of the customer. The difficulty of maintenance and development should match the complexity of the task and should not be significantly affected by existing code. A strategy to achieve this is complexity management.
Okay, let's look at the individual parts:
- accessories
Actually "everything around SW"... code, infrastructure, CI/CD, testing... everything that is needed to deliver value to the customer.
- Maintenance
Hotfixes / bugs / small changes.
- long-term evolutionary development
-
Unless it's a small custom development, most of the software we write will be in service for years, 5 years, 7 years, 10 years... We need to be able to add larger, new modules to the software.
If we are writing accounting software, for example, we need to be able to add a module that generates tax returns and other similar documents (evolution in terms of extensions). But no one can expect us to simply modify the SW to drive a nuclear submarine (not very evolutionary given the previous functionality).
- boundaries of development
-
Budget, expected number of users, reporting requirements, legal obligations, etc.
- difficulty
-
Logically, minor development should be easier and faster than adding a new major module. And this should be the case for the lifetime of the product.
The code shouldn't contain parts that developers don't really understand, that they are afraid to dig into lest something breaks, that block further development. The speed of development should be +- constant throughout the life of the product and if it changes, it should be faster and faster just because of the code that already exists.
- strategy
-
Managing complexity is the alpha and omega of all the other procedures we choose. The complexity of the code must match what the code solves. A simple module should look different than a complex one. There is no single reason to "be consistent", this will only lead to small modules being unnecessarily complicated and complex modules not being designed adequately.
- for the benefit of the customer
-
Everything we do, we do for the positive impact on the customer immediately after deployment. We don't write/edit code just for the sake of it, we don't write code "because it might come in handy one day". Value for the customer is the highest value.
Form over substance
It's something I see a lot, the pursuit of "perfect code", "perfectly partitioned". Much more time spent on structure than content. Just write code that does what you need it to do, and then refactor it so it's easy to figure out. If you have to dig into it, refactor it to get ready, add functionality, and clean it up... and repeat next time.
Another thing I see a lot is creating code structure before I even write functionality. The structure then often doesn't match the need of the functionality. Too complex, or too simple.
Focus on making the code do what it's supposed to do and make it readable and editable.
So what's with all the patterns?
To solve specific problems. I've written this somewhere before, but the more specific the pattern, the more specific the problem it solves. These are tactical decisions in one place in the code, or in one functionality. There is nothing wrong with implementing patterns, quite the opposite! But solve practical problems you have with them. Don't solve problems you don't have, don't implement them because "that's the way it's done".
Top to bottom
Okay, we have a goal and a master strategy... then what?
- high level separation of concerns
- high cohesion and low coupling
- modularization
Start here, these 3 principles serve to reduce complexity and increase readability and sustainability. Go lower, to tactics when you must.
- Is it clear what parts of the code are supposed to do?
- Is it what is related together and what is not related apart?
- Is it clear where the boundaries of one functionality end and the boundaries of another begin?
If so, don't complicate something that may be simple. I see overengineering more often than underengineering. And believe it, refactoring underengineered code is easier than the other way around.