For the past year I have worked as a Senior Solutions Architect for a large multinational company in the construction sector. While in the past I functioned as a hybrid developer-architect, designing and building enterprise-scale systems and mission-critical line-of-business applications, my new role has been squarely focused on software architecture. As such I thought it would be a good time to reflect on the meaning of software architecture and why it has an indispensable role to play in designing applications and services that add business value and will stand the test of time.
This exercise was inspired by a 15 minute talk, titled “Making Architecture Matter,” presented at an O’Reilly conference in 2015 by renowned software architecture guru, Martin Fowler.
What Is Software Architecture?
Martin begins his talk by emphasizing that software architecture isn’t about pretty diagrams. Although these can help communicate some important aspects of application architecture, they themselves do not capture or define the actual architecture of a software development project. Rather, the totality of the application architecture can be best expressed as the shared understanding of system design by expert developers who are most familiar with the system. In other words, it’s about the code. And if you’re going to understand the de facto architecture of a system, you have to understand the code, not simply as an abstraction, but how it functions and how the pieces work together.
As Martin states that if you’re going to be a software architect you need to know how to write code, and you need to communicate with lead developers as a coder among coders. This is important, because in order to provide technical leadership, which is critical to the proper implementation of software design principles, you will need to gain their cooperation and respect. At the risk of sounding trite, you need to show them that you know what you’re talking about, so that you can engage in technical discussions that produce sounds technical decisions.
Part of the process should be regular code reviews, whether in-person or via pull requests. If possible, you can augment this with periodic pair programming sessions (check out VS Live Share), which will help to promote geek bonding. For this reason, it’s important to keep your coding skills up-to-date. This can be challenging if you’re a full-time software architect, so you might want to consider contributing to some open-source projects, which shouldn’t be too difficult now that companies like Microsoft and Google have open-sourced most of their cutting-edge technologies. (If you’re interested you can also volunteer to resolve issues and implement features for some of my open source projects: URF, a generic Unit of Work and Repository framework, Trackable Entities, an Entity Framework extension, and EF Core Scaffolding with Handlebars, reverse engineering customizable classes from a database.)
The Purpose of Software Architecture
Martin grapples with how to define precisely what software architecture is, and in so doing he refers to an article he wrote, Who Needs an Architect, in which he quotes Ralph Johnson as saying, “Architecture is about the important stuff. Whatever that is.” There are different ways of defining what is important to a project. It could be related to technical decisions, such as programming languages and frameworks, which once implemented are difficult to change without incurring substantial cost.
Given this characterization, one might speculate concerning the process by which important technical decisions are made. In my view it’s the job of the architect to ensure that consequential decisions are made in a strategic manner. In other words, the architect is perhaps the person who sees the larger technological landscape, who discerns emerging trends, and who can discriminate between enduring paradigm shifts and passing fads.
For example, back in 2016 I wrote a somewhat influential (and controversial) blog post titled, WCF Is Dead, in which I argued against using Windows Communication Foundation for building greenfield web services. While at the time there were some valid use cases for WCF (for example, inter/intra-process communication and message queuing), SOAP-based web services have since been fully supplanted by RESTful Web API’s using modern toolkits such as ASP.NET Core which incorporate Dependency Injection as a first-class citizen. With the planned merging in the year 2020 of disparate versions of .NET (both full .NET Framework and Mono / Xamarin) into a version of .NET Core that will simply be called .NET 5, Microsoft will be officially classifying WCF services as legacy — will not be a part of .NET 5. (Microsoft is recommending gRPC as a successor to WCF for efficient inter-process communication, for which support will be included in ASP.NET 3.0.) My prescient assessment of WCF’s demise is an example of the kind of strategic architectural advice that can spare companies from making costly mistakes betting on tech stacks that are on their way out.
Choice of software frameworks is but just one crucial decision in which software architecture can play a role. We are in the midst of a number of other paradigm shifts that will influence the direction and focus of our software projects. Here are a few notable examples:
- From On-Premise (costly, inefficient) to Cloud-Based Computing (economies of scale)
- From Centralized (SVN, TFVC) to Decentralized Version Control (Git)
- From Monolithic (binary references) to Modular Design (packages)
- From Server-based (virtual machines) to Server-less Architecture (Docker, K8s, SaaS)
- From Platform-Specific (Windows) to Platform-Independent Frameworks (Linux)
- From Coarse-Grained (n-tier) to Fine-Grained Services (microservices)
- From Imperative (scripts) to Declarative Infrastructure (YAML)
Many of these changes are just beginning to gain dominance in the software industry, so it’s important for architects to train and mentor dev teams in the transition from outdated practices to modern approaches that will result in greater longevity as well as yield increased levels of reliability, scalability and cost-effectiveness.
The Right Level of Abstraction
Martin goes onto state that our goal as software architects is to design systems that have fewer and fewer aspects that are difficult to change. In my view this is where SOLID programming principles come in.
Adhering to these principles will more likely result in a more modular design of loosely coupled components, each of which have just one responsibility. Dependencies are represented by interfaces that are injected into constructors and supplied by a dependency injection container. Systems are also designed using well-known design patterns, including the Gang of Four and Domain-Driven Design patterns. In addition, it is important to establish how microservices interact with one another, abstracting away direct service dependencies in favor of indirect communication based on models such as publish-subscribe (for example, Amazon SNS / SQS) or event sourcing with a unified log (for example, Apache Kafka, Amazon Kinesis). Martin has an excellent presentation on the Many Meanings of Event-Driven Architecture, in which he discusses the pros and cons of various event-based architectural patterns.
The purpose of applying these patterns and enforcing separation of concerns is to reduce parts of the system design that are difficult to change, thereby increasing flexibility and helping insure against premature obsolescence. This is an approach I described some time ago using Jeffery Palermo’s Onion Architecture, which aims to decouple an application from infrastructure concerns using dependency injection. A benefit of this approach is code that is more testable and uses mocking frameworks to stub external dependencies such as data stores or other services.
Trust But Verify
It is important for software architects to understand that, no matter how beautiful and elegant your designs are, dev teams will almost always adulterate them. As I pointed out in a talk at a London software architecture conference in 2015, entitled Avoiding Five Common Architectural Pitfalls, often times developers will apply architectural patterns without fully understanding them and will, as a result, introduce hacks and workarounds (a.k.a. anti-patterns) that defeat the very purpose for which the pattern was created. This may be because the team is composed of members with various levels of skill and experience, or devs may be used to a coding practice that has been displaced by a newer approach.
A case in point would be the use of dependency injection, where devs may “new” up objects in classes rather than allowing the DI framework to do its thing. Another example would be the use of modal dialogs directly from view-models while implementing the MVVM pattern in client apps. Furthermore, I’ve seen devs struggle to take full advantage of Git, not understanding how to pull down upstream commits with rebase, or using the same branch for all their pull requests.
As Martin puts it, the best architects are like mountaineering guides: “a more experienced and skillful team member who teaches other team members to better fend for themselves yet is always there for the really tricky stuff.”
Why Architecture Matters
One of the most challenging aspects of a software architect’s job is to communicate to stakeholders and key decision-makers the significant business value that effective software architecture brings to the business. Sometimes companies fail to recognize that, in the 21st century, all companies to a certain extent need to see themselves as being in a technology company, or at least they need to recognize that well-built software can be a differentiating factor that provides a crucial competitive advantage.
Even if a company acknowledges the importance of software in general, they may not recognize that adequate software architecture is essential to building systems that are large, complex or play a critical role in running the business. Responsibility for communicating this message lies with us, and our profession suffers the extent to which we fail to convey the role software architecture plays in both avoiding costly mistakes rapidly introducing new product features in a shorter span of time. Martin calls this the Design Stamina Hypothesis, which postulates that, at a certain point in the app development lifecycle, good design allows features to be introduced at a faster pace than when design is not considered, or poor design choices resulted in an inordinate accumulation of technical debt.
I worked on a large project where the lead developer lacked design expertise. The code base was so intertwined and convoluted that releasing new versions became a nightmare. Classes with over 3000 lines of code made maintenance almost impossible, and lack of unit tests meant that new features or bug fixes would break existing functionality or make the system unstable.
One of the misperceptions that architects face is that we are engaging in architecture for architecture’s sake, or that we are keen on introducing new technologies mostly because of the coolness factor. Our challenge is to counter this misperception by arguing not for the aesthetic value of good design, but for the pragmatic, economic value. We need to frame the need for intentional design as something that can save the company millions of dollars by averting otherwise disastrous technology and design choices, produce a distinct competitive edge through market differentiation, or pave the way for increased customer satisfaction by making it possible to evolve the product in a sustainable fashion. If we can communicate a clear understanding of the nature and purpose of software architecture, and we forcefully articulate the value proposition of sound architectural design and the benefits of ensuring correct execution of that design, we will be able to help mature the ability of an organization to deliver software solutions that can both ward off obsolescence and add features that delight customers.