4. What is “software architecture”?
Let’s start with the basics. The word “architecture” means many different things to many different people, and there are many different definitions published on the Internet. I’ve asked thousands of software developers what “architecture” means to them and, in no particular order, a summary of their responses is as follows.
- Modules, connections, dependencies and interfaces
- The big picture
- The things that are expensive to change
- The things that are difficult to change
- Design with the bigger picture in mind
- Interfaces rather than implementation
- Aesthetics (e.g. as an art form, clean code)
- A conceptual model
- Satisfying non-functional requirements/quality attributes
- Everything has an “architecture”
- Ability to communicate (abstractions, language, vocabulary)
- A plan
- A degree of rigidity and solidity
- A blueprint
- Systems, subsystems, interactions and interfaces
- Governance
- The outcome of strategic decisions
- Necessary constraints
- Structure (components and interactions)
- Technical direction
- Strategy and vision
- Building blocks
- The process to achieve a goal
- Standards and guidelines
- The system as a whole
- Tools and methods
- A path from requirements to the end-product
- Guiding principles
- Technical leadership
- The relationship between the elements that make up the product
- Awareness of environmental constraints and restrictions
- Foundations
- An abstract view
- The decomposition of the problem into smaller implementable elements
- The skeleton/backbone of the product
No wonder it’s hard to find a single definition! Thankfully there are two common themes here: architecture as a noun and architecture as a verb.
Architecture as a noun - structure
As a noun, architecture can be summarised as being about structure. It’s about the decomposition of a product into a collection of smaller building blocks1 and the interactions/relationships between these building blocks. This needs to take into account the whole of the product; including the foundations and the infrastructure services that deal with cross-cutting concerns such as security, configuration, error handling, etc. To quote Bass, Clements, and Kazman:
The software architecture of a program or computing system is the structure or structures of the system, which comprise software elements, the externally visible properties of those elements, and the relations among them.
Architecture as a verb - vision
As a verb, architecture (i.e. the process of creating architecture, or “architecting”) is about translating the architectural drivers (functional requirements, quality attributes, constraints, and principles) into a technical solution, thereby creating a technical roadmap or vision. Crucially, it’s also about communicating that vision to a number of stakeholders both inside and outside of the immediate software development team, so that everybody has a consistent view of what is being (or has been) built. The process of architecting is additionally about introducing technical leadership so that everybody involved with the construction of the software system is able to contribute in a positive and consistent way.
Types of architecture
What we have so far is a very generic definition of the word “architecture” but, of course, there are many different types of architecture and people who call themselves “architects” within the IT industry. Here, in no particular order, is a list of types of architecture that people most commonly identify when asked.
- Infrastructure
- Security
- Technical
- Solution
- Network
- Data
- Hardware
- Enterprise
- Application
- System
- Integration
- IT
- Database
- Information
- Process
- Business
- Software
The unfortunate thing about this list is that some of the terms are easier to define than others, particularly those that refer to or depend upon each other for their definition. For example, what does “solution architecture” actually mean? For some organisations “solution architect” is simply a synonym for “software architect”, whereas other organisations have a specific role that focusses on designing an overall “solution” to a problem, stopping before the level at which implementation details are discussed. Similarly, “technical architecture” is vague enough to refer to software, hardware or a combination of the two2.
What do all of these terms have in common? Well, aside from being able to suffix each of the terms with “architecture” or “architect”, all of these types of architecture have structure and vision in common.
Let’s take “infrastructure architecture” as an example. Imagine that you need to create a network between two offices located at different ends of the country. One option is to find the largest reel of network cable that you can, plug it in at one office, and start heading to the other in a straight line. Assuming that you had enough cable, this could potentially work. In reality though, there are a number of environmental constraints (real-world obstacles such as rivers, lakes, roads, cities, etc) and service-level agreements (performance, bandwidth, security, etc) that you need to consider in order to actually deliver something that satisfies the original goal. This is where the process of architecting is important. One single long piece of cable is certainly one approach, but it’s not a very good one because of the real-world constraints. For this reason, networks are typically much more complicated, requiring a collection of smaller building blocks that collaborate together in order to satisfy the goal. From an infrastructure perspective then, we can talk about structure in terms of the common building blocks that you would expect to see within this domain; things like routers, firewalls, packet shapers, switches, etc.
Regardless of whether you’re building a software system, a network or a database; a successful solution requires you to understand the problem and create a vision that can be communicated to everybody involved with the construction of the end-product. In order to move towards a definition of “software architecture”, let’s look at a couple of types of architecture in the IT domain that are relatively well defined.
Application architecture
Application architecture is what we as software developers are probably the most familiar with. In this context, I’m going to define an application as being a single deployable unit, written in a single technology; such as a single-page JavaScript/Angular application, an iOS or Android mobile app, a Java server-side Spring MVC web application, a .NET desktop application, etc.
Application architecture is about looking inside the application to understand how it’s designed and built. This includes how the application has been decomposed into building blocks (e.g. components, layers, packages, namespaces, etc) as well as understanding the patterns, frameworks and libraries in use. In essence, it’s predominantly about code, and the organisation of that code.
System architecture
I like to think of system architecture as one step up in scale from application architecture. If you look at most software systems, they’re actually composed of multiple deployable units (e.g. applications or datastores), each of which might be built using different technologies.
As an example, you might have a software system comprising of a client-side iOS mobile app communicating via JSON/HTTPS to a Java server-side Spring MVC web application, which itself consumes data from a MySQL database. Since each of these three deployable units (the mobile app, the web app and the database) is built using a different technology, each of them will have their own internal application architecture.
However, for the software system to function as a whole, thought needs to be put into bringing all of those separate deployable units together. In other words, you also need to consider the overall structure of the software system at a high-level, and the integration of the various parts. For example, if I make a request from the mobile app, how is that request processed by the entire software system? Additionally, software systems typically don’t live in isolation, so system architecture also includes the concerns around interoperability and integration with other systems within the environment.
Where application architecture tends to focus primarily on software (e.g. programming languages, frameworks, libraries, etc), system architecture is about understanding both software and hardware. Admittedly, much of the hardware that we deal with nowadays is virtualised or completely hidden3, but it’s an important concern nonetheless. The reason that most definitions of system architecture include references to software and hardware (whether it’s the target deployment platform or supporting infrastructure) is that you can’t have a successful software system without it. After all, software needs to be deployed somewhere in order to run! Additionally, infrastructure typically provides constraints that must be taken into account when designing software. Examples of this range from the processing power and memory of embedded devices limiting what your software can do, through to cloud providers limiting how your applications are deployed in order to facilite better high availability and scaling.
Towards a definition of “software architecture”
Unlike application architecture and system architecture, which are relatively well understood, the term “software architecture” isn’t. Rather than getting tied up in the complexities and nuances of the many definitions of software architecture that exist on the Internet, I like to keep the definition as simple as possible. For me, software architecture is simply the combination of application architecture and system architecture, again in relation to structure and vision. In other words, it’s anything and everything related to the design of a software system; from the structure of the code and understanding how the whole software system works at a high level, through to how that software system is deployed onto infrastructure. But that’s not the whole story.
When we’re thinking about software development as software developers, most of our focus is placed on the code. Here, we’re thinking about things like object oriented principles, functional programming principles, classes, interfaces, modules, inversion of control, refactoring, automated testing, clean code and the countless other technical practices that help us build better software. If your team consists of people who are only thinking about this, then who is thinking about the other things such as:
- Cross-cutting concerns; including logging, exception handling, etc.
- Security; including authentication, authorisation and confidentiality of sensitive data.
- Performance, scalability, availability and other quality attributes.
- Audit and other regulatory requirements.
- Real-world constraints of the environment.
- Interoperability/integration with other software systems.
- Operational, support and maintenance requirements.
- Structural consistency and integrity.
- Consistency of approaches to solving problems and implementing features across the codebase.
- Evaluating that the foundations you’re building will allow you to deliver what you set out to deliver.
- Keeping an eye on the future, and changes in the environment.
In order to think about these things, you need to step back, away from the code and your development tools. Working software is ultimately about delivering working code, so the detail is crucially important. But software architecture is about having a holistic view across your software system, to ensure that your code is working toward your overall vision rather than against it.
Enterprise architecture - strategy rather than code
Although this book isn’t about “enterprise architecture”, it’s worth including a short definition so that we understand how it differs from software architecture.
Enterprise architecture generally refers to the sort of work that happens centrally and across an organisation. It looks at how to organise and utilise people, process and technology to make an organisation work effectively and efficiently. In other words, it’s about how an enterprise is broken up into groups/departments, how business processes are layered on top of those groups/departments, and how technology is used to support the goals of the enterprise. This is in very stark contrast to software architecture because it doesn’t necessarily look at technology in any detail. Instead, enterprise architecture might look at how best to use technology across the organisation, without actually getting into detail about how that technology works.
While some developers and software architects do see enterprise architecture as the next logical step up the career ladder, most probably don’t. The mindset required to undertake enterprise architecture is very different to software architecture, taking a very different view of technology and its use across an organisation. Enterprise architecture requires a higher level of abstraction, and a different focus. It’s about breadth rather than depth, and strategy rather than code.
Central architecture groups
As a quick aside, if you’ve ever worked in a large organisation, the definition I’ve just given for enterprise architecture might be different to what you were expecting. Often, large organisations will have a “central architecture group” that might be referred to as “the enterprise architects”. Such groups typically manage lists of the approved technologies that you can (or must!) use when building software, and will often have some involvement in reviewing/guiding the output from software development teams to ensure consistency across the organisation. Although a useful role, this isn’t really what I deem to be “enterprise architecture”.
Architecture vs design
One of the words that people use to describe architecture is “design”, and this raises the question of whether we should use the words “architecture” and “design” interchangeably. Grady Booch has a well cited definition of the difference between architecture and design that really helps to answer this question. In On Design, Grady says that:
As a noun, design is the named (although sometimes unnameable) structure or behavior of a system whose presence resolves or contributes to the resolution of a force or forces on that system. A design thus represents one point in a potential decision space.
If you think about any problem that you’ve needed to solve, there are probably a hundred and one ways in which you could have solved it. Take your current software project/product for example. There are probably a number of different technologies, deployment platforms, and design approaches that are also viable options for achieving the same goal. In designing your software system though, your team chose just one of the many points (options) in the potential decision space. That’s the essence of design. It’s about narrowing down the solution space to find an option that works given the context in which you are working.
Grady then goes on to say that:
All architecture is design but not all design is architecture.
This makes sense, because creating a solution, and “architecting”, is essentially a design exercise. It’s about narrowing down options, and making decisions. However, for some reason, there’s a distinction being made about not all design being “architecture”, which Grady clarifies with the following statement:
Architecture represents the significant design decisions that shape a system, where significance is measured by cost of change.
Essentially, Grady is saying that the significant decisions are “architecture”, and that everything else is “design”. In the real world, the distinction between architecture and design isn’t clear-cut, but this definition does provide us with a basis to think about what might be significant (i.e. “architectural”) in our own software systems. For example, this could include:
- The overall shape of the software system (e.g. client-server, web-based, native mobile, distributed, microservices, asynchronous vs synchronous, etc).
- The structure of the code inside the various parts of the software system (e.g. whether the code is structured as components, layers, features, ports and adapters, etc).
- The choice of technologies (i.e. programming language, deployment platform, etc).
- The choice of frameworks (e.g. web MVC framework, persistence/ORM framework, etc).
- The choice of design approach/patterns (e.g. the approach to performance, scalability, availability, etc).
The architectural decisions are those that you can’t reverse without some degree of effort. Or, put simply, they’re the things that you’d find hard to refactor in an afternoon.
Architectural significance
Although this sounds relatively straightforward, we, as architects, have a degree of influence over those architecturally significant decisions. Imagine you’re building a simple server-side web application to deliver information to users, and that information is stored in a relational database. For the sake of this discussion, let’s say there are no complex requirements related to security, performance or scalability, and that the database is simply being used for data storage. Let’s also ignore non-relational (e.g. NoSQL) databases.
When building the web application, many teams will choose to use some sort of abstraction layer to communicate with the database, like an object-relational mapping framework; such as Hibernate, JPA, Entity Framework, etc. One common reason to use a database abstraction layer is to make accessing the database easier. Another common reason to use a database abstraction layer is to decouple business/domain-specific code from the choice of database. The use of an abstraction layer is a classic technique for decoupling distinct parts of a software system; promoting looser coupling, higher cohesion and a better separation of concerns. If you’re only using the database for data storage (i.e. the database only contains data rather than code wrapped up functions and stored procedures), the use of the database abstraction layer allows you to, in theory, change your database via configuration, without changing any code. Since the database can be changed so easily, many teams would therefore consider the choice of database to no longer be a significant decision.
However, while the database may no longer be considered a significant decision, the choice to decouple through the introduction of an additional layer should be. If you’re wondering why, have a think about how long it would take you to swap out your current database abstraction layer or web MVC framework and replace it with another. Of course, you could add another layer over the top of your chosen database abstraction layer to further isolate your business logic and provide the ability to easily swap out your database abstraction layer but, again, you’ve made another significant decision. You’ve introduced additional layering, complexity and cost.
Although we can’t necessarily make “significant decisions” disappear, we can use a number of different tactics (such as architectural layering, in the previous example) to change what those significant decisions are. There’s also no explicit line between the decisions that should be deemed as significant, and those that shouldn’t. Having said that, the significant decisions are usually related to key technology choices (e.g. programming languages and frameworks) and the overall structure (monolithic deployment unit vs microservices). Aspects such as “tabs vs whitespaces”, or “curly braces on same line vs the next line”, are definitely not architecturally significant! Everything else will fall in between somewhere between these two extremes. Part of the process of architecting a software system is about understanding what is significant and why, given the context you’re working in.
Is software architecture important?
Now that we understand what software architecture is, we should wrap up this chapter by looking at the importance of software architecture. The past decade or so has seen a huge shift in the way that we build software, thanks to movements such as agile, lean, software craftsmanship, continuous delivery, DevOps, the cloud and more. Together these new approaches help us to build better software that better meets the needs of our stakeholders, while carefully managing time and budgetary constraints. But there’s still more we can do because even a small amount of software architecture can help prevent many of the problems that projects face.
As I’ve already mentioned, successful software projects aren’t just about focussing on good code. Ask yourself the following questions:
- Does your software system have a well-defined structure?
- Is everybody on the team implementing features in a consistent way?
- Is there a consistent level of quality across the codebase?
- Do team members share the same vision for how the software will be built?
- Does everybody on the team have the necessary amount of technical guidance?
- Is there an appropriate amount of technical leadership?
It is possible to successfully deliver a software project by answering “no” to some of these questions, but it does require a very good team and a lot of luck. Although most software projects and products start with the best of intentions, it’s easy for them to veer off track without an appropriate amount of technical leadership; both at the code level, and above it. If nobody thinks about software architecture, you often end up with codebases that are too slow, insecure, fragile, unstable, hard to deploy, hard to maintain, hard to change, hard to extend, etc. I’ve personally seen (and worked on!) codebases that have exhibited the following types of problems:
- Components in a monolithic application were configured in different ways, depending upon the developer who built them. These different approaches were not discussed or documented, so deploying the resulting software required many “trial and error” loops.
- Code in a monolithic application could use one of three different data access layers, each built using a different framework, to access data from the same database.
- The path of a HTTP request through a server-side web application varied depending upon the developer who was implementing the feature. In essence, every developer had a different idea of what the layered architecture should be, and this was reflected in the code.
- A software system didn’t perform and scale as hoped when presented with the real-world data set. In this case, one of the key technology choices wasn’t able to meet the quality attributes, and this was unfortunately not evaluated before making such a significant decision.
Additionally, without technical leadership, many codebases also end up looking like the stereotypical “big ball of mud” or “spaghetti code”. Sure, it has a structure, but not one that you’d want to work with! These seemingly chaotic software projects do exist in the real-world, and most of us will have one or more horror stories about the time we spent working on them. If you’ve never worked on such a project, you’re probably in the lucky minority!
The benefits of software architecture
Thankfully, most of these problems are relatively easy to solve with the application of some good technical leadership, resulting in a team that therefore understands and thinks about software architecture. In summary, this can provide:
- A clear vision and roadmap for the team to follow, regardless of whether that vision is owned by a single person or collectively by the whole team.
- Technical leadership and better coordination.
- A stimulus to talk to people (inside and outside of the team) in order to ask questions relating to significant decisions, quality attributes, constraints and other cross-cutting concerns.
- A framework for identifying and mitigating risk.
- Consistency of approach and standards, leading to a well structured codebase.
- A set of firm foundations for the product being built.
- A structure with which to communicate the solution, at different levels of abstraction, to different audiences.
Does every software project need software architecture?
Rather than use the typical consulting answer of “it depends”, I’m instead going to say that the answer is undoubtedly “yes”. The caveat here is that every software team should look at a number of factors in order to assess how much software architecture thinking, a degree of which manifests itself as up front design, is necessary. These include the size of the project/product, the complexity of the project/product, the size of the team and the experience of the team.
Historically we’ve seen a tendency towards too much up front design, with teams trying to answer all of the questions and solve all of the problems before writing a single line of code. More recently, I’ve witnessed a trend towards the other extreme, and too little up front design. As Dave Thomas once said:
Big design up front is dumb. Doing no design up front is even dumber.
As with many things in life, there is a sweet spot here awaiting discovery. The answer to how much is “enough” up front design and technical leadership will be explored throughout the rest of this book.