Book review: Fundamentals of Software architecture
- Authors
Welcome to my latest blog post, where we dive into the essential guidebook Fundamentals of Software Architecture: An Engineering Approach by Mark Richards and Neal Ford. This book is a treasure trove of knowledge for anyone interested in the field of software architecture, especially for programmers who wanna become an architect.
Having had the opportunity to work as a software architect, I am always looking to expand my knowledge and stay updated with the latest industry practices. In this review, I will highlight the key concepts and strategies outlined by Richards and Ford, demonstrating how their engineering approach can be practically applied in real-world software development. I will also share the sections and insights that I personally found most valuable.
Who is an architect?
Overall, it's hard to say, but in the book, we can read about the base requirements for the architect position, including:
Making an architecture decisions
Architects are responsible for architecture, so it is not surprising that we expect them to plan and create it.
An architect should provide direction instead of deciding on each technology stack, for example, when an architect chooses Angular to create a user interface, we are making a technical decision
(not an architectural one). The choice of a particular technology depends on more things that the architect should not be interested in (e.g., team preferences, programmes knowledge). So, instead of making a technical decision, going by example, the architect should point in the direction of using a reactive library to build the user interface (e.g. React, Angular, Svelte).
Continuous architecture analysis
Every project flows, and the same applies to architecture. So, one of the primary responsibilities of an architect is to monitor and continuously develop the architecture to keep it in good condition, refresh it and solve current problems.
Tracking the latest trends
Staying up-to-date with the latest trends, tools, and technologies in software architecture to ensure that the system remains current and competitive.
Ensuring compliance with decisions
Making sure that the development teams abide to the architectural decisions and guidelines, ensuring consistency and alignment with the overall architectural vision.
Rich and diverse experience
Drawing from a broad range of experiences and knowledge in different domains and technologies - will expand this one later.
Business knowledge
Understanding the business domain and requirements to align the architecture with the organization’s goals and strategies.
Interpersonal skills
Possessing strong communication
and collaboration
skills are necessary to work effectively with various stakeholders, including developers, project managers, and business leaders.
Knowledge of company policies
Being familiar with and able to apply company policies and procedures to ensure that the architecture complies with organizational standards and practices. It's handy especially when it comes to the security rules.
The laws of software architecture
Fundamentals of Software Architecture introduces a set of guiding principles known as the "Laws of Software Architecture." These laws serve as essential guidelines for architects aiming to create robust, scalable, and maintainable systems
The first law: everything in software architecture is a trade-off
One of the most fundamental principles is that every architectural decision comes with trade-offs
. This law emphasizes that architects must balance various factors such as performance, scalability, maintainability, and cost.
The second law: why is more important than how
Understanding the rationale behind architectural decisions is crucial
. This law stresses that knowing why
a particular choice was made is more valuable than knowing how
it was implemented (or how it works). The why
provides context and justification, enabling future architects and developers to understand the decision-making process and adapt the architecture as needed.
Remember: the architecture is something more than just a conection some elements.
Architectural Thinking
An architect looks at things in a very different way than a programmer. First: architectural thinking is not just thinking about architecture itself.
Mark Richards and Neal Ford points a four aspects of architectural thinking, let's discuss them:
Architecture vs project
Where does architecture end and project begin?
Architecture defines the general structure of the system, including key components, their interactions, and the overall approach to fulfilling business requirements. On the other hand, project involves more detailed decisions regarding the implementation of individual components, their interfaces, and behaviors.
The boundary between architecture and project may be blurred, but there are some key differences:
- Level of Abstraction: Architecture operates at a higher level of abstraction, defining the overall shapes and patterns of the system, while project focuses on the specific implementation of individual modules.
- Scope of Decisions: Architectural decisions typically have a broad scope and impact the entire system, whereas project decisions may concern specific parts of the system or even individual features.
- Durability and Flexibility: Architecture often has a long-term nature, establishing the foundations of the system that should remain stable even amidst changing requirements. Project may be more short-term oriented, such as implementing specific functionalities within a defined sprint or iteration.
Remember
: close cooperation between the architect and the development team is essential and crucial to the success of the project
Technical span
The extent of technical depth is different for an architect and a programmer. A programmer's job requires depth
, but an architect must have the technical span
to think about architecture and from an architectural perspective.
Simply put, it is better for an architect to know that five solutions exist than to have an in-depth knowledge of one of them.
Analysis of trade-offs
Programmers know the benefits of everything and the tradeoffs of nothing. An architect needs to know both. ~ Rich Hickey
Every architectural decision involves a trade-off. From the choice of technology stack to the design of system components, an architect is constantly faced with decisions that require balancing competing priorities. In essence, architecture is the art of compromise
.
When analyzing trade-offs, several factors come into play:
- Functionality vs performance
- Scalability vs maintainability
- Flexibility vs complexity
- Cost vs quality
Business factors
Architects must align technical decisions with business goals, consider cost implications, prioritize time to market, manage risks, and ensure compliance and security. By incorporating these business considerations, architects create solutions that not only meet technical requirements but also contribute directly to the organization's success.
Balance between architecture and coding
Every architect should actively code, maintaining a balance between coding and architecture while maintaining a certain level of technical depth. Here are some practices that can help architects practice coding in their work:
Proof of concepts (my personal favourite)
Architects can engage in creating proof of concept (POC), which are prototypical implementations demonstrating selected architectural solutions in practice. Creating POCs allows for the verification of architectural assumptions and a quick understanding of their potential benefits and limitations.
Team support and bug fixing
Architects can actively participate in the development team's work, offering their knowledge and experience in solving technical issues. They can also work on bug fixes in existing code, introducing architectural optimizations and eliminating technical debt.
Code review
Practicing regular code review sessions is crucial for ensuring high-quality code and architectural consistency in the project. Architects can lead code review sessions, analyzing code written by team members and providing constructive feedback on its quality and adherence to architectural principles.
Automation of Technical Processes
Architects can also engage in automating certain technical processes within the project. This could involve setting up automated testing frameworks, continuous integration pipelines, or deployment automation scripts. By automating repetitive tasks, architects free up time for more strategic architectural thinking and coding activities.
Modularity
Modularity refers to the practice of dividing a software system into separate, independent modules that encapsulate distinct functionalities.
At its core, modularity involves organizing a system into cohesive units, each responsible for a specific aspect of the overall functionality. These modules are designed to be self-contained, with well-defined interfaces for interaction with other modules
.
Trying to split a coherent module would only increase coupling and decrease readability. ~Larry Constantine
In terms of modularity, we can speak about cohesion, coupling, and connascence. It's crucial for an architect to be able to calculate these factors.
This topic is incredibly fascinating and extensive. For a more detailed exploration, I invite readers to check out my in-depth article on this subject: Calculate modularity of software architecture.
Parameters
In software architecture, parameters are essential characteristics or aspects that define the design and behavior of a system. Defining these parameters is crucial for several reasons. Firstly, it helps architects and development teams better understand project goals and functional and non-functional requirements. This enables the development of a coherent architectural vision and ensures the system meets client expectations.
Secondly, identifying key parameters allows teams to focus on the most important aspects of architecture, avoiding distractions and ensuring focus on elements critical to project success.
However, it's important to note that selecting too many parameters can lead to a generic solution, commonly known as the general architecture
anti-pattern.
Typical architecture operational parameters
Parameter | Description |
---|---|
Availability | Downtime - time system is unavailable. SLA (Service Level Agreement) - agreed service level |
Continuity | The ability to restore the system after a disaster |
Performance | Response Time - time taken to respond to a request. Throughput - number of operations system can handle in a given time |
Recoverability | How quickly the system is expected to recover from a disaster |
Security | Authentication - verifying user identity. Authorization - determining user access rights |
Resilience | The degree of resistance to dealing with mistakes |
Scalability | Vertical Scalability - ability to increase system capacity vertically. Horizontal Scalability - ability to increase system capacity horizontally |
Structural architecture parameters
Parameter | Description |
---|---|
Modifiability | Ability to change configuration aspects of the software more easily |
Extensibility | Importance of adding new functionalities seamlessly |
Installability | Ease of installing the software on different systems |
Reusability | Ability to use common components in different products |
Localization | Support for multiple languages |
Maintainability | Ease of making changes and improving the system |
Supportability | Level of logging required for diagnosing errors in the system |
Upgradeability | Ability for easy and quick updates |
Fitness functions
Fitness functions serve as crucial mechanisms or criteria for evaluating the quality of a system's design and behavior. They encompass various aspects such as performance, scalability, security, and maintainability.
For instance, a fitness function could focus on preventing cyclic dependencies between components/modules in the architecture (example package for JavaScript). This function could analyze module imports to detect cycles and trigger alerts or actions when circular dependencies are identified. We could even combine such a fitness function with a git hook to prevent code containing CI from being pushed out.
Domain-Based vs. Technical-Based Decomposition in Software Architecture
When designing a software system, one of the critical decisions architects face is how to decompose the system into manageable components. Two common approaches are domain-based decomposition and technical-based decomposition. Each has its own advantages and disadvantages.
Domain-Based Decomposition
Advantages:
- Alignment with Business Goals: Reflects the natural structure of the business, making it easier to understand and manage.
- Improved Maintainability: Changes are isolated to specific components, reducing risk.
- Enhanced Modularity: Promotes independent work on different domains, facilitating parallel development.
- Better Scalability: Each domain can be scaled independently.
Disadvantages:
- Complexity in Initial Setup: Requires deep understanding of business domains and careful planning.
- Potential for Domain Overlap: Managing interactions and data sharing between overlapping domains can be complex.
- Higher Coordination Costs: Ensuring consistency across teams can be challenging.
Technical-Based Decomposition
Advantages:
- Focus on Technical Expertise: Leverages team’s technical skills, optimizing work in specific technical areas.
- Simplified Development Process: Clarifies responsibilities and simplifies implementation.
- Efficient Resource Utilization: Optimizes resources for specific technical requirements.
Disadvantages:
- Misalignment with Business Goals: Harder for stakeholders to understand and for teams to respond to business changes.
- Increased Coupling: Technical changes in one component may require changes in others.
- Challenges in Scaling: Scaling one component might not benefit others, leading to inefficiencies.
Monolith vs distribiuted architecture
When starting a new project, choosing the right architectural style is crucial. Begin by evaluating the operational and structural parameters of your project.
For instance, if your project needs to prioritize performance and scalability, you might consider a simple monolithic architecture. Monolithic applications are typically faster to develop, easier to manage, and less expensive initially.
However, what if only a specific part of the application needs to be highly performant? Scaling an entire monolith vertically can be costly, which is where distributed architecture becomes advantageous. In this case, the critical part of the application can be developed using a different, faster technology and scaled independently.
Notice a pattern? It's essential to view your project not just as a whole, but as a collection of architectural quants
. Simpler, less demanding projects might only require 2-3 key parameters, which can be effectively managed within a monolith. However, if different parts of your project have distinct requirements, adopting a distributed architecture might be more appropriate.
In summary, if the various components of your project have different needs, distributed architecture is a better choice. Otherwise, a monolithic architecture is often simpler and more cost-effective.
Conclusion
"Fundamentals of Software Architecture: An Engineering Approach" by Mark Richards and Neal Ford is a must-read for programmers aspiring to become architects and experienced architects looking to refine their skills. The book equips readers with the necessary knowledge and tools to design robust and scalable software systems.
I highly recommend it!