“What a stupid design! You are single-handedly responsible for the destruction of our ultimate weapon and battle station!” This was Darth Vader’s reaction to the person who placed the exhaust ports on the Death Star. We may design complex, flawed software architectures and survive without being choked by a Sith Lord, but the core question remains: how can we architect better software?
Before we dive in, let’s define our terms. What do we actually mean by software architecture?
- Is it non-functional requirements?
- Is it a specific framework?
- Is it just high-level decisions and abstractions?
Here is a low-level description that I like: “All architecture is design, but not all design is architecture. Architecture represents the significant design decisions that shape a system, where significance is measured by the cost of change.” (Grady Booch)
Ralph Johnson offers a more high-level perspective: “Architecture is the decisions that you wish you could get right early in a project, but that you are not necessarily more likely to get right than any other.”

1. Change should not be expensive
As the project moves forward, introducing changes should not be expensive. If the cost of changing or adding a new feature is high, it would take a longer time to release your software. In some cases, you may be unable to add a new features due to the constrains of the architecture.
2. It should have a feedback loop
“Architecture is a hypothesis that needs to be proven by implementation and measurement.” (Tom Gilb)
Architecture is an expression of an idea. Your job is to test that idea and prove that it works. What you can build is influenced and constrained by how you build it, and vice versa. Because it is expensive to know everything upfront, you need to be comfortable going back and making changes as you move forward. Architectural thinking is based on knowledge, and knowledge requires continuous learning throughout a project. For this reason, development increments should be based on functionality rather than just component structure.

3. Make it agile and lean
Agile thinking emphasizes teamwork and responsiveness to change, while lean thinking focuses on eliminating waste and improving flow. Making every significant decision upfront is irresponsible: if architecture is a hypothesis, we must use an empirical approach to test it.
It is important to remember that sustainable agility requires a solid architectural foundation, whereas fast initial development does not. These two concepts are frequently confused.

4. Aim for “sooner” rather than “faster”
o illustrate the difference, imagine you and a friend decide to go on a road trip in two separate cars. While your friend decides to use a GPS navigator, you decide to “wing it.” You both leave Point A at the same time to meet at Point B using different routes. While you may drive a faster car, if you get lost on the way, your friend might arrive sooner than you.

5. Work in smaller teams
“Today, we suffer from an almost universal idolatry of giantism. It is therefore necessary to insist on the virtues of smallness where this applies.” (E.F. Schumacher)

The bigger the team, the harder it is to be flexible. Having more people on a project does not mean that the project will be completed sooner. There is no linear correlation.
Bigger teams require more communication. When there are more points of communication, the original message can be lost or become inaccurate between the source and receiver.
Once at an Amazon offsite, managers had the reasonable-sounding suggestion that employees should be increasing communication with each other. To their surprise, founder and CEO Jeff Bezos stood up and announced, “No, communication is terrible!”
This stance explains his famous two-pizza team rule, that teams shouldn’t be larger than what two pizzas can feed. More communication isn’t necessarily the solution to communication problems — it’s how it is carried out. — Janet Choi
Software development does not have economies of scale. Development has diseconomies of scale (Allan Kelly)
6. Do not use speculation to add complexity
Use speculation and future requirements to decide between design alternatives. Use past change to forecast future change. Look for change hot spots and high defect density. Structure the system with respect to rate of change and (un)certainty. Change is often the only constant.
“The ability to simplify means to eliminate the unnecessary so that the necessary may speak.” (Hans Hofmann)
7. Always think of three things that might go wrong
“Nothing is more dangerous than an idea when you have only one idea.” (Émile-Auguste Chartier)
We should always understand the context and richness of the software that we are designing. Based on that, we should propose at least 3 things that might go wrong. We need to validate our hypothesis on these potential failures or errors. When we are making progress developing our software, we are gaining more knowledge from things that work and don’t work. We should be comfortable starting from the beginning if we noticed a flaw in the architecture.
Architectural questions and considerations when redesigning our software:
▪ Where are the defects? Is there a reason they are distributed that way?
▪ Where are the ‘hard bits’? What makes them hard?
▪ Where are the ‘easy bits’? What makes them easy?
▪ What keeps changing? Why?
▪ What stays still? Why?
If you can’t think of three things that might go wrong with your plans, then there’s something wrong with your thinking — Jerry Weinberg

Conclusion
Architecture is about codifying knowledge. Good architecture should be flexible. You should be able to iterate on your architecture until you get a Minimum Viable Product. Any changes in the future should not be costly. Keep it simple. Adding more developers to a project as the project grows will not automatically speed up the project. Don’t spend too much time at the beginning designing; instead, just build it and see what happens. With good architecture, you can make mistakes continually and still make progress.
Sustainable development is development that meets the needs of the present without compromising the ability of future generations to meet their own needs