Have you ever put 20 agile engineers in a room and asked them each to write down their definition of architecture? I did once. I got 20 different answers. Some were negative, some positive, and several contradictory.
It’s not their fault. Our industry has yet to agree upon a definition of architecture. But that's causing real problems. For example, there are some agile engineers that believe architecture isn’t agile, and some architects that believe agile hurts architecture.
Before we can show how agile engineers approach architecture, we must first define architecture.
There are several great people improving our industry’s understanding and practice of architecture (Matt Stine, Simon Brown, Robert Martin, Grady Booch, and Martin Fowler come to mind); taking our cues from them, we’ve started to align internally on the following definitions at Pivotal Labs:
- Architecture: decisions that are hard to reverse.
- Software Architecture: the high level shape and flow of the software that’s independent of the problem domain, but dependent on the desired user experience.
Let’s look at each of these definitions in detail.
In his two-volume work Software Architecture for Developers, Simon Brown resurfaces some of Grady Booch’s writings on architecture, including the following gem: “Architecture represents the significant design decisions that shape a system, where significance is measured by cost of change.”
Simon then goes on to note, “The architectural decisions are those that you can’t reverse without some degree of effort.”
I’m sure that with that definition, even the most die-hard “anti-architecture” agile developer will admit that they’ve made architectural decisions. For example, every engineer has at some point chosen a programming language for a project. The programming language is a very significant decision. Imagine you choose Java on day one of a project, then 3 months later, you decide Go would have been a better language choice. Well, too bad. You’re three months into writing Java. You’re unlikely to quickly reverse that decision.
Software architecture, specifically
Software architecture refers to something more specific that’s hard to reverse: it’s the high level shape and flow of the software that’s independent of the problem domain and dependent on the desired user experience.
As Robert Martin notes in his post “The Domain Discontinuity”, there are at least three broad, common categories of software architecture:
- Request/Response. I make a request to the system, I get a response.
- Event Driven. Events happen. Actors independently react to those events, and may generate new events in the process. Actors may be both human and non-human.
- Batch. The system processes data without user interaction or involvement, including error handling and recovery.
Most web applications have Request/Response architectures. If you examine the codebases of those web applications, you’ll find that they have a very similar high level shape and flow.
But how can that be? Those web applications are all solving different problems for their users. Remember: the software architecture is independent of the problem domain, but dependent on the user experience. So even though those web applications are all solving different problems, they all have a very similar user experience. Click a link, navigate to a new page. Submit a form, see validation errors. Submit request. Get response. Start. Stop. Request. Response. Get it?
Interestingly, it’s not just most web applications that have Request/Response architectures. So do most command line programs. Type
whoami into your unix terminal, see your username. Type
pwd, see your current working directory. Type command. See output. Start. Stop. Request. Response. Get it?
That means: most command line applications and most web applications have the same user experience! And if you examine the codebases, you’ll see that those command line applications and those web applications have the same high level shape and flow—a Request/Response software architecture.
Let’s look at another category of software architecture. Have you ever played a video game? A sidescroller like Super Mario Bros? Or a first-person-shooter like Doom? (Am I aging myself?) A video game user experience feels very different from those web and command line applications we just discussed. With these games, you press “Start”, and a whole world is set in motion. Your character is just one of the actors in those worlds. You press “right arrow” and Mario moves to the right. The game notices your move event and reacts by updating the screen so that you are centered in it. While you’re moving, a goomba strolls into the frame and collides with you. The sound engine notices the collision event and reacts by playing a sound. Many actors. Many events. Many reactions. Get it?
Most games have Event Driven architectures. But so do a lot of distributed systems. For example, Diego, the core distributed system powering Cloud Foundry, is event-driven. And if you examine the codebases, you’ll find that all of these applications have a very similar high level shape and flow—despite the fact that a distributed system is made up of many different processes running on different machines on a network, and a game might be made up of a single process running on a single machine.
Let’s look at one more software architecture. Do you get a paycheck? Perhaps it’s mailed to you every couple of weeks, or it’s deposited into your bank account. Do you have to submit a request to some software every two weeks to make sure you get a paycheck? No! Of course not. That paycheck gets sent to you (and likewise to all of your fellow employees) every two weeks without you having to interact with any software. That’s because, behind the scenes, the system is processing the payroll on a schedule, without direct user involvement.
That’s the defining characteristic of a Batch architecture: the system is processing requests for the users without the users having to actually make the request! And that has a profound consequence on the shape and flow of the software. When a user submits a request to a website, if the response is “503 Service Unavailable”, then the user can simply refresh and try again. In other words, in a Request/Response architecture, the responsibility of “retry” is given to the users. But if a Batch job is processing some data behind the scenes and suddenly encounters some transient exception, there’s no user there to notice it and make the software retry processing the job. The Batch architecture has to do that for us, automatically, without our intervention.
So obviously, payroll processing applications have a Batch architecture. But so do many other systems; automated clearing houses and subscription management systems, for example. And, you guessed it: examine the codebases, and the high level shapes and flows are the same.
But why is software architecture “architecture”?
Remember, architecture, generally speaking, refers to the decisions we make that are hard to reverse. And the software architecture—the high-level shape and flow—is hard to change. All of your code—and tests—are built around that flow. Once you decide to go with a specific software architecture, you can’t easily reverse it.
Software Architecture is an expensive decision. You need to make sure you’re making the right decision when you choose the software architecture. But before we talk about how we choose, let’s talk about when we choose.
When do Agile engineers “architect”?
Do we determine it all up front? Does it evolve over time? Does it emerge naturally through refactoring?
The mistake many agile engineers make is assuming that the software architecture will emerge naturally through the process of test-driven development (TDD) and refactoring. It won’t. Software architecture is a significant decision; as Robert Martin notes in The Domain Discontinuity, you can’t even begin to TDD until you know the software architecture within which you will TDD—because you're tests are, by necessity, coupled to the software architecture.
This implies that we must determine the software architecture before development.
However, agile engineers don’t foolishly attempt to plan all of the features they’re going to build into the software up front, before beginning development. They know that good software is built through a process of validated learning, not through extensive up front planning. They know that the quicker they can put working software in front of users and get their feedback, the quicker they can discover the product assumptions that they got wrong. And lastly, they know that software is never “done.” Software solves problems for people—and people are always changing, and the software has to be ready to change right along with them or risk obsolescence.
This means that, on an agile product development team, the user experience of the product will grow and evolve over time in ways that can’t be predicted or planned up front. Most products grow to encompass multiple user experiences—and therefore multiple architectures.
This implies that we must determine the software architecture during development.
This might seem paradoxical. First we say we have to decide before development. Then we say we have to decide during development. But in fact, those two statements are not mutually exclusive. Let’s look at how agile engineers determine the software architecture before development first.
How do Agile engineers “architect” before development?
Before you can write your first test, you need to know the software architecture. So how do you figure that out? You determine the software architecture by determining the desired user experience—by talking to your product owners and product designers about the features you’re going to build, and understanding how the users are interacting with the product.
Imagine we’re building an application for the game “Rock, Paper, Scissors.” Our product manager shows you the following story:
Feature: Play Scenario: Rock v. Scissors Given player one throws rock And player two throws scissors Then player one wins
What software architecture does this story demand? Request/Response? Event Driven? Batch?
Answer: it’s impossible to know based on this story. Why? Because this story doesn’t give us enough information about the desired user experience. Do player one and player two throw rock and scissors in real life, and then enter their throws into the application to see who the winner is? Or are they actually using the application to the play the game?
We ask our product manager to clarify; they send us an updated version of the story:
Feature: Play Scenario: Rock v. Scissors Given player one throws rock in real life And player two throws scissors in real life When one of them enters the throws into the application Then the application tells them that player one wins
That's much clearer. Now we need to figure out which software architecture maps to this user experience. After talking about it a bit, we decide that it’s probably Request/Response. The “When” statement seems to indicate that a user enters throws into the system, and the “Then” statement indicates a response. We ask our PM for a wireframe to confirm our suspicions:
It’s as we suspected! The user enters the details into a simple web form and presses the “Play” button. The system replies with a result. This is a Request/Response user experience. We know what the high level shape and flow of a Request/Response architecture looks like, and we know the boundaries within which we’ll TDD.
But what if...
Imagine for a second that the product owner had replied to our clarification query with the following story:
Feature: Play Scenario: Rock v. Scissors Given player one submits a “rock” throw When player two submits a “scissors” throw Then player one should see that he or she won And player two should see that he or she lost
This feels a bit different. This doesn’t feel like a start/stop, request/response flow. This feels like there are multiple actors in the system. We ask the product owner for wireframes to confirm our suspicions:
It’s just as we suspected! This is an event-driven user experience. There are two users simultaneously using the application. The users are generating “throw” events. The system is watching those “throw” events, and computing and generating “result” events once both players have thrown. The UI is watching “result” events, and updating the display accordingly. And again, now that we know that we need an event-driven architecture, we know the boundaries within which we will TDD.
How do Agile engineers “architect” during development?
As I mentioned before, software architecture happens both before and during development. You can’t begin to TDD until you know the software architecture that you’ll TDD within. The software architecture flows from the desired user experience.
But products grow and evolve over time. The desired user experience grows and evolves over time too. On an agile team, at any time, you could pick up a story that demands a different user experience than the one your application has facilitated up till now. You have to ask yourself, with each and every story, "What user experience does this story demand?" And if the answer is different from the user experience that you've been building up until this point, then now you’ll have a product that facilitates two different types of user experiences. Which means your code will need to house multiple software architectures simultaneously. That's a complexity booster for your codebase; maintaining the multiple necessary architectures requires care and craft.
As my fellow Labs director Mik Freedman is fond of saying, “Be ever vigilant.”
Does anything besides the user experience influence the software architecture?
Yes! Folks like Matt Stine and Simon Brown are working hard to help our industry understand the impact of “quality attributes” on shaping the architecture of a system. Quality attributes (also known as non-functional requirements) include things like availability, security, consistency, performance, etc.
In future posts, Matt Stine will look at how some of these quality attributes play out in some real world systems—especially systems that are dealing with many non-human actors behind the scenes.
About the Author
Matt Parker is Head of Engineering for Pivotal LabsMore Content by Matthew Parker