Understanding Systems, Design, and Architecture
- What Is Software Architecture?
- How to Design a System
- Five Questions
- Seven Principles: The Overarching Concepts
- Designing for an Online Bookstore
- Designing for the Cloud
- Summary
What Is Software Architecture?
Software architecture is a plan to build a software system. This plan usually involves two things: defining a system as a set of components and specifying how those components work together. In complex systems, this decomposition happens recursively, where the architect breaks down each component into smaller components and defines their behaviors.
There are good plans and bad plans. The same goes for software architecture. What are the goals of good software architecture?
The overarching goal of software systems (hence, for software architecture) is to build systems that meet quality standards and that provide the highest return on investment (ROI) in the long run or within a defined period of time. Long run is the operative ideal here. For example, if we do not invest long term and build a crappy product, we end up with unhappy users, ultimately losing their revenue or spending too much money trying to make them happy. Cheaper in the short run is often more expensive in the long run. You pay now, or you pay later. On the other hand, adding a critical new feature, thus spending more money, can give us more revenue, improving the ROI.
Three kinds of uncertainty complicate architecting. First, we have a partial understanding of our users and what they want. Second, we have a limited comprehension of how our systems behave, especially in complicated and new situations. Third, we fail to recognize that as use cases and users evolve, their requirements change. Therefore, we want our architectures to be easy to understand and flexible, enabling us to handle surprises.
When reaching for the best architecture, we can employ several best practices or tactics:
Making decisions as late as possible. For example, when we start the design, we know the least about a system and the problems we are supposed to solve. If we can postpone solving some problems, we have an opportunity to learn more. This approach lets us make better design decisions.
Envisioning a design that is easy to understand and to change. Problems change over time, and we may face a few surprises. Because a system goes through frequent changes over its lifetime, good architecture makes those changes easier.
Saying no to features as much as possible. Many system features are rarely used, although they were often deemed important at design time. Knowing the audience, implementing only necessary features, and communicating why not may save us money in the long run.
Many argue that these tactics are always good, even considering them to be goals of the architecture. I disagree. To me, even those tactics incur costs; thus, everything is relative, and these tactics are useful only if they help us achieve the overarching goal. Let’s look at a few examples.
If we know that a use case will significantly change in the future, forcing us to rewrite the system from scratch, we should not invest in making the current design extensible.
When writing a cloud app, we have two choices: We can choose a single cloud, taking advantage of its unique strengths for our application, or we can make the application portable across several cloud providers. Choosing only one cloud vendor makes development easier, but there is a chance that we may have to rewrite the system or parts of the system if porting to a different cloud. The cost of making the current version portable may be much higher than rewriting it, only if required.
You may have a great architecture that makes the system easy to change; however, it involves complicated concepts that will be hard for the current team to handle. (It may have been an adverse call forcing us to choose this architecture.) You fight with the army you have, at least until you have resources to enlist a better army.
You may be a startup trying to get your MVP out. Then, chances are you will rewrite everything once you are successful. You would not care for scale or even ease of change at this stage.
Uncertainties create risks, but the remedies have their costs too, which also create risks. Architecting is balancing those risks. Context is king, and it is hard to architect with evergreen rules and to get them right. To quote a Star Trek: Discovery episode:
Universal law is for lackeys. Context is for kings.
Understanding all types of risks, planning around those uncertainties, communicating the plan, enlisting people, and managing risk along the way define leadership. This book discusses the leadership process in software architecting.