Analysing The Development Process
In A Simple Scenario we introduced some terms for talking about risk (such as Attendant Risk, Hidden Risk and the Internal Model).
We've also introduced a notation in the form of Risk-First Diagrams which allows us to represent the ways in which we can change the risks by Taking Action.
Now, we are going to start applying our new terminology to software. In the example below, we'll look at a "toy" process and use it for developing a new feature on a software project and see how our risk model informs it.
A "Toy" Process
Let's ignore for now the specifics of software methodology - we'll come to that later. For now, let's consider a simple, toy, process for developing software and understand how it works from a risk perspective.
Something like the following:
- Specification: a new feature is requested somehow, and a business analyst works to specify it.
- Code And Unit Test: a developer writes some code and some unit tests.
- Integration: they integrate their code into the code base.
- UAT: they put the code into a User Acceptance Test (UAT) environment and user(s) test it.
- All being well, the code is Released to Production.
Can't We Improve This?
Is this a good process? Probably, it's not that great: you could add code review, a pilot phase, integration testing, whatever.
Also, the methodology being used might be Waterfall, it might be Agile. We're not going to commit to specifics at this stage.
For now though, let's just assume that it works for this project and everyone is reasonably happy with it.
We're just doing some analysis of what process gives us, and also why it is that we end up with processes for producing software at all.
Minimising Risks - Overview
I am going to argue that this entire evolved software production process is informed by software risk:
- We have a business analyst who talks to users and fleshes out the details of the feature properly. This is to minimize the risk of building the wrong thing.
- We write unit tests to minimize the risk that our code isn't doing what we expected, and that it matches the specifications.
- We integrate our code to minimize the risk that it's inconsistent with the other, existing code on the project.
- We have acceptance testing and quality gates generally to minimize the risk of breaking production, somehow.
A Much Simpler Process
We could skip all those steps above and just do this:
- Developer gets wind of new idea from user, logs onto production and changes some code directly.
We can all see this might end in disaster, but why?
Two reasons:
- You're Meeting Reality all-in-one-go: all of these risks materialize at the same time, and you have to deal with them all at once.
- Because of this, at the point you put code into the hands of your users, your Internal Model is at its least-developed. All the Hidden Risks now need to be dealt with at the same time, in production.
Applying the Toy Process
Although bitter experience has shown that this is a terrible way of delivering software, I want to use Risk-First diagrams and our new terminology to explore exactly why this is the case by examining what happens to risk, and specifically look at how our toy process should act to prevent these risks materialising in production and causing maximum harm.
First, let's consider the unhappy path. One where, at the outset, we have lots of Hidden Risks. Let's say a particularly vocal user rings up someone in the office and asks for new Feature X to be added to the software. It's logged as a new feature request, but:
- Unfortunately, this feature once programmed will break an existing Feature Y.
- Implementing the feature will use some api in a library, which contains bugs and have to be coded around.
- It's going to get misunderstood by the developer too, who is new on the project and doesn't understand how the software is used.
- Actually, this functionality is mainly served by Feature Z...
- which is already there but hard to find.
The diagram above shows how this plays out. As you can see, our truncated, two-step development process exposes us to all the hidden risks in production at the same time and disaster ensues.
Now let's follow our feature through the toy process and see how it meets reality slowly, and the Hidden Risks are exposed in a more benign way.
Specification
The first stage of the journey for the feature is that it meets the Business Analyst (BA). The purpose of the BA is to examine new goals for the project and try to integrate them with reality as they understand it. A good BA might take a feature request and vet it against his Internal Model, saying something like:
- "This feature doesn't belong on the User screen, it belongs on the New Account screen"
- "90% of this functionality is already present in the Document Merge Process"
- "We need a control on the form that allows the user to select between Internal and External projects"
In the process of doing this, the BA is turning the simple feature request idea into a more consistent, well-explained specification or requirement which the developer can pick up. But why is this a useful step in our simple methodology? From the perspective of our Internal Model, we can say that the BA is responsible for:
- Trying to surface Hidden Risks
- Trying to evaluate Attendant Risks and make them clear to everyone on the project.
In surfacing these risks, there is another outcome: while Feature X might be flawed as originally presented, the BA can "evolve" it into a specification and tie it down sufficiently to reduce the risks. The BA does all this by simply thinking about it, talking to people and writing stuff down.
This process of evolving the feature request into a requirement is the BA's job. From our Risk-First perspective, it is taking an idea and making it Meet Reality. Not the full reality of production (yet), but something more limited.
Code And Unit Test
The next stage for our feature, Feature X is that it gets coded and some tests get written. Let's look at how our Goal meets a new reality: this time it's the reality of a pre-existing codebase, which has it's own internal logic.
As the developer begins coding the feature in the software, they will start with an Internal Model of the software, and how the code fits into it. But, in the process of implementing it, they are likely to learn about the codebase, and their Internal Model will develop.
At this point, let's review the visual grammar of the diagram above. Here, we're showing how the balance of risks will change if the developer Takes Action and writes some code. On the left, we have the current state of the world, on the right is the anticipated state after taking the action.
The round-cornered rectangles represent our Internal Model, and these contain our view of Risk, whether the risks we face right now, or the Attendant Risks expected after taking the action. We're not at the stage where taking this actions is completing the goal. In fact, arguably, we're facing worse risks after taking action than before, since we now have development difficulties to contend with... but at least we have uncovered these risks that were previously hidden.
Beneath the internal models we are also showing real-world tangible artifacts. That is, the physical change we would expect to see as a result of taking action. In the diagram above, the action will result in "New Code" being added to the project, needed for the next steps of the development process.
Integration
Integration is where we run all the tests on the project, and compile the code in a clean environment, collecting together the work from the whole development team.
So, within this example process, this stage is about meeting a new reality: the clean build.
As shown in the diagram above, at this stage we might discover the Hidden Risk that we'd break Feature Y
User Acceptance Test
Next, User Acceptance Testing (UAT) is where our new feature meets another reality: actual users. I think you can see how the process works by now. We're just flushing out yet more Hidden Risks.
Observations
Here are a few quick observations about managing risk which you are revealed both by this toy software process and also our previous example of The Dinner Party:
- Taking Action is the only way to create change in the world.
- It's also the only way we can learn about the world, adding to our Internal Model.
- In this case, we discover a Hidden Risk: the user's difficulty in finding the feature.
- In return, we can expect the process of performing the UAT to delay our release (this is an attendant schedule risk).
Major Themes
So, what does this kind of Risk-First analysis tell us about development processes in general? Below are four conclusions you can take away from the chapter, but which are all major themes of Risk-First that we'll be developing later:
First, the people who set up the development process didn't know about these exact risks, but they knew the shape that the risks take. The process builds "nets" for the different kinds of Hidden Risks without knowing exactly what they are. In order to build these nets, we have to be able to categorise the types of risk we face. This is something we'll look at in the Risks part of Risk-First.
Second, are these really risks, or are they problems we just didn't know about? I am using the terms interchangeably, to a certain extent. Even when you know you have a problem, it's still a risk to your deadline until it's solved. So, when does a risk become a problem? Is a problem still just a schedule-risk, or cost-risk? We'll come back to this question soon.
Third, the real take-away from this is that all these risks exist because we don't know 100% how reality is. We don't (and can't) have a perfect view of the universe and how it'll develop. Reality is reality, the risks just exist in our head. Again, this is a theme we'll develop later in Risk-First.
Fourth, hopefully you might be able to see from the above that really all this work is risk management and all work is testing ideas against reality.
In the next section, we're going to look at the concept of Meeting Reality in a bit more depth.