The laws of architectural work
After completing the short “OSS” blog series series and before returning to the “Simplify!” blog series that grew quite a lot while writing, I would like to discuss a completely different topic. I call it “the two first laws of architectural work”.
Of course, what I will describe are not actual laws. “Findings” or “lessons” (without the educational undertone) would be more accurate.
And even if they were laws, I do not know if they would be the first two ones. I just called them the first two ones to emphasize their relative importance. At least I find them very relevant as I see so many places where they are – often willfully – ignored which usually leads to negative consequences.
So, “Two IMO relevant findings regarding architectural work” would probably be a more accurate title. But that would be a lot less catchy title … ;)
Also, it would not express how important I think the two observations are. Therefore I will stick with the “two first laws”.
The first law of architectural work
The first law of architectural work, as I call it, is:
Every decision has its price. No decision is for free.
The longer version is:
No decision only has upsides. Every decision also has downsides.
I think that it is essential to understand that there is no free lunch in architectural work. Whenever you decide for an architectural option, you will get a mix of advantages and disadvantages. While in coding you sometimes have situations where one option is right and the others are wrong, you will never have such a situation in architectural work.
In architectural work, there is no “right” or “wrong”. There is only “better” or “worse”. I often tend to say, while in development things like “black” or “white”, 0 or 1 exist, in architecture only many shades of gray, the numbers between 0 and 1 exist. With every architectural decision you will get something, but you will also pay a price for it.
I am not sure if this pattern is as old as our industry, but especially in the recent years I noticed a growing tendency only to mention the potential advantages of a solution and hide the disadvantages.
I’ve seen too many talks, articles, posts, and alike, that just praise some concept, technology or method, “Do <X> and everything will be fine!” being the typical message. Downsides? Trade-offs? Nil return!
We could ignore those types of messages if they would not lead to really bad solutions in actual projects when project teams blindly pick up those messages. Two little examples:
In the recent years, most IT projects went for microservices, often based on dubious rationales. But are microservices really “right” while the vilified monoliths are “wrong”?
If you take a neutral stance, you see that microservices can help you, e.g., with decoupling your teams better, to deal better with very disparate NFRs and to upgrade technology easier. On the other hand they multiply operations complexity by at least an order of magnitude. And if you do not get your functional design right, availability and response times of the resulting application will badly suffer.
(Deployment) monoliths on the other side are the simplest type of deployment artifact in operations. One executable, that’s it. Either it is up or down. Easy to monitor. Well-known and proven scaling patterns: Replication and load balancers (at least if the state handling is implemented correctly). On the other hand monoliths tend to become a big ball of mud, which is hard to maintain and leads to tight team coupling. Also technology upgrades become harder with the size of the application.
There would be a lot of details to add, but the message already becomes clear: It is not that one is “right” and the other is “wrong” (as many people try to suggest). Both have their strengths and weaknesses. Deciding for any one of the two gives you some advantages while it also gives you some disadvantages at the same time. Hiding the disadvantages does not make them go away.
Event-based communication is another fashionable concept of the recent years and its advocates like to talk down request-response-based communication.
Again, taking a neutral stance, you see that event-based communication, e.g., helps to decouple parts better on a technical level. The reversed dependency relation (receiver knows sender) can be advantageous. Event brokers can even create location transparency, timeouts can be handled differently and more.
On the other hand event-based communication quickly creates complex communication patterns that are hard to understand. This can easily result in deadlocks and other unexpected behaviors. It can create tight functional coupling between the parts if not designed very carefully and creates new challenges like dealing with lost or duplicate messages.
Request-response-based communication on the other side is easy to understand as it resembles the familiar communication flow of call-stack-based communication. It is very easy to implement and the system behavior tends to be a lot more easier to understand than with event-based communication. On the other hand, blocking requests and latency (or timeouts if you monitor the call durations) either lead to undesired system behavior at runtime or quite some added complexity.
Again, while the discussion is not complete, the same message takes shape: It is not that one is “right” and the other is “wrong”. Both have their strengths and weaknesses.
I could add a lot more examples, but these two little examples should be sufficient to illustrate what I mean with “Every decision has its price”.
If you are experienced in architectural work, this “law” should be obvious for you. Yet, I see a lot of people either willingly or unconsciously ignoring it. This becomes especially dangerous if “Agile” developer teams decide to make all design decisions without anyone on the team having the required education and experience. Based on my observations, these teams tend to be very vulnerable to “Do <X> and everything will be fine!” messages. 1
To wrap the first law up: The key to good architectural work is to see the upsides as well as the downsides of potential options and then make a decision for the option that has the best upside/downside ratio.
This leaves the question, how to determine the up- and downsides of an option, which immediately leads to the second law.
The second law of architectural work
The second law of architectural work, as I call it, is:
A decision can only be evaluated with respect to its context.
The longer version is:
Decisions are not invariably “good” or “bad”, but only with respect to a given context.
This “law” is almost as important as the first one. I see too many decisions where advantages or disadvantages of concepts, technologies or methods are discussed in a vacuum, without taking the actual context into account.
While there is nothing wrong to point out general trade-offs of an architectural option, it must be clear that this is never sufficient but can only support the actual decision making process.
To illustrate the point, let us briefly revisit the microservices example from above. I sketched some advantages and disadvantages of microservices and monoliths. Which one to take, making the simplifying assumption that the two are the only options available?
Well, it depends!
What is your context?
You are a hyperscaler or live in a market where you need to move very fast with independent capability teams and we discuss customer-facing applications? Probably microservices will be the better option for you as the competitive advantages in terms of market responsiveness they give you will outweigh their disadvantages.
You are an insurance company living in the highly regulated German market and we discuss a new policy system? While microservices will give you basically no competitive advantages in such a setting, they offer grave disadvantages compared to a monolithic solution. So, quite likely the monolith will be the better option. 2
You are a standard software manufacturer who sells solutions primarily to SME (Small & Medium Enterprises) clients who need to run the solutions on their own (i.e., no SaaS offering)? And the operations teams of the clients often do not have deep IT knowledge (a setting that you find quite often in SME-type companies)? While on the development side microservices could give you a competitive advantage, your clients most likely would be hopelessly overstrained running a microservices-based solution.
In the end a competitive advantage at development time is not worth anything if your clients are not able to run your solutions. Thus, you will need to focus on meeting the operations simplicity needs of your clients first – which means a (deployment) monolith.
Additionally, a strict modularization concept at development time resulting in independent modules that are assembled together at build time could help you to realize parts of the competitive advantage that microservices would offer. So, even with just two options – microservices and monolith – the world is not black and white.
Again, while not being complete these little examples should help to understand that one-size-fits-all solutions do not exist and that it depends heavily on the given context what the best option is.
Architectural decisions are never “good” or “bad” per se, but only relative to a given context. Still, I see too many architectural discussions and recommendations being made in a vacuum, without taking the context into account.
These were my two “laws” of architectural work. Personally, I think it is essential to bear them in mind if you want to do good architectural work. Still, I see the opposite happening too often – at conferences, in articles, and also in actual project work, usually leading to bad decisions.
There would be a lot more to discuss in the context of the two laws, but I will leave it here. I hope I was able to bring the point across and would be glad if it contained some valuable ideas for you.
[Update] November 8, 2020: Refined several potentially ambiguous statement.
To be clear: This is not an advocacy for architects. To be honest, I am quite suspicious about most people who call themselves “architects”. Based on my understanding what architectural work is about (I will write more about it in future posts), I do not see how most of them could add value to a project teams. Architectural work for me is not about persons or titles, but about the skill and experience to do this work. If you lack this skill in a development team, your architectural work and thus most likely your architectural decisions will suffer. ↩︎
Actually, something “between” microservices and a monolith would be the best option for a policy system. While microservices do not provide enough advantages to justify the massively increased operations complexity, policy systems tend to become huge software systems with several 100.000 lines of code. Putting all that code in a single monolith usually is also not a good idea. The best option probably is splitting up the policy system in several mostly independent smaller deployment units. Still, as we made the simplifying assumption that monoliths and microservices are the only two options available that third option is not available in the example. ↩︎