This post is about things that can go wrong on an agile software delivery team. One thing that I have learned over years of software delivery is that you have to finish something, sometime. You have to deliver a working capability, feature, or story. When you finish an iteration with no working stories – you are doing something wrong; very, very wrong.
One working feature is better than 5 half working ones. One of the problems is our multi-layered systems. Back in the day, we had a programming language and maybe a database with a library. Then databases got their own language and I had to know how to stitch SQL into my COBOL or C code. Then we became object oriented so we had to translate data in a database to data in objects. Then we had PC’s, networks and servers and distributed processing. Then we had graphical user interfaces, and the libraries and patterns used to manage them. Ultimately we have evolved patterns for implementing software in many physical and logical layers each requiring knowledge of different language and object constructs and patterns. On a recent we application built, I counted 17 distinct technology skill sets required to complete the project.
One problem with modern layered systems is that it is becoming more and more rare, to have a single individual who is skilled at design and implementation across all layers so that he can design and build a complete software capability unassisted. But this post is about a different problem.
When you have a bunch of people who are more skilled in some areas than others, we tend to allow them to stay in their comfort zone, and play to their strengths. The problem comes when we have multiple people building multiple layers concurrently. They need contracts, and collaboration, and they need to make a simple case work, then add in the complex cases. What we often do instead is to allow one layer to run ahead, and build out many things that are not fully integrated into the software, and then we risk having re-work as we discover that our contracts were not correct, or we had misunderstandings in collaboration.
There are two core principles of managing software development projects, value delivery and risk retirement. Working with large teams often challenges both of these principles, because the parallelism required to make larger teams cost effective can interfere with both principles.
The primary tenet of value delivery is that you don’t get “credit” for delivering value until you put some working useful capability in the hands of the customer. For the development team, that customer might be QA, or the product manager, but for sure, looking pretty is not considered working. Partially integrated is not considered working. So when we allow part of the team to “run ahead” and build parts of software capabilities waiting for some other part of the team to “catch up”, we get ZERO CREDIT for delivery. If the catch up time is more than “a couple days” we are simply adding risk to the project – that risk is that we have spent the money for partially working features that may end up as a waste, because those require rework.
We have all kinds of risk in the software deliver process. The risk I am talking about here specifically, is that the development team’s proposed solution or design will not result in a working capability. More specifically, that the estimated work required is somehow different in some way that will have a negative impact to cost and schedule. Risk can arise from specific component integration, concerns about resource skills or know how, or simple unknowns in the design. Risk is retired, not by proof of concept, or by white paper, but by actually making something work according to the solution as designed. I don’t mean partly working, or partially integrated, or demo-able, but working (as in passes QA).
What To Do
The thing is, both of these principles benefit from a single method. Building the thinnest completely working capability as quickly as possible. Then building another one, then another, and so forth and so on. When I do this I can deliver value and retire risk simultaneously. I can sequence my delivery so that certain risks are retired earlier, and that certain value propositions are delivered earlier.
When I work without completely integrating from end to end for any length of time, or when I build layer wise – more than end to end, I increase the probability that I will have multiple partially working capabilities, or partially retired risks,
Software can be designed to enable more end to end integration. This is an art. Only few artisans know how.
Developers can work against contracts and mocks, but this is like building a bridge from both sides of a river and joining in the middle. it requires precision and skill. Contracts that I have seen typically contain structural information (e.g. some class, some method, some type, producing this result, interfaces.) Unfortunately, in order to ensure integration, contracts need to be semantic. This means that the contract not only needs to specify a named, typed data element, it has to specify a definition, based on the usage pattern, and potentially a usage scenario. Preparing contracts in this manner is a skill, and requires discipline on both sides.
For my money, nothing compares with serializing the end to end integration of one capability after another. Once you recognize that the cost of the first integration is higher than the next, you realize that the incremental cost of building serially, is much less than the cost of continual re-work of “batch” integration. Moreover, the things that you do frequently, you become better at. What you are better at, you become more sensitive to, so while following this practice, developers often devise design patterns that make this practice easier.
There some of you out there saying, “Wait isn’t that what automated tests and continuous integration are for?” Yes and no – unit test or test driven and CI only get you so far. They test what you already know. They cannot test to prove that you and someone else you are interfacing with, know the same thing. You can do “integration tests”, but to do that you must have integrated software. They are usually implemented after the fact, not before as unit tests are, and they are not usually semantic. They are implemented to test the developers understanding of the information passed from layer to layer – not whether the correct information is passed. The learning in integration is not, can I pass information, but is the information reasonable, unfortunately, if the developers don’t understand that test of reason-ability (and they often do not), there is no way for them to write tests for it. So, no, tests written by developers alone, are not sufficient to solve this problem.
Suggestions for making this work better:
- Build the Bridge First – establish the contract, and build the interfaces first with good tests, let them be a constraint on the rest of your build. If you need to change the bridge – fine but one side can’t change the bridge without the other.
- Supply Default Values – as a temporary measure to make the contracts work, have the layer doing data management or behavior implement the default values or behaviors first. This at least allows the contract to be satisfied in a simple case without “faking it”. Then once the integration is complete with default values or behavior, adding the alternative cases one by one, completing and testing the integration for each as they are completed.
- Hard Problems First – sometimes developers have an instinct that one part of the problem space will be harder to solve than the rest. Do that one first. Get that one working end to end, then implement the rest on top of the more difficult solution. You still work end to end, one at a time, but you retire the bigger risk early. This also still gives you intermediate states where you can demonstrate working software – even if it is not complete.
- Show Me A Sample – Act as if you don’t know what you think you know. I really mean it. Most developers are so confident that they know how to do something. They are willing to bet my money that you can work ahead of integrating parts of a solution together. Seriously, don’t build the second one until the first is working from end to end. Been burned a lot in this way. Won’t get fooled again.
Feel free to add additional suggestions in the comments below. I’m sure that I am not the only one who has experienced this…