There’s an old saying that the more things change, the more they stay the same. The saying certainly applies to the field of software development. Things change all the time, and the things that change tend to change for the same reasons: Changing business priorities, changing costs, changing understanding of needs, changing actual needs, changing competitive pressures, changing technologies, changing availability of resources. Change occurs at all levels from the enterprise strategic plan all the way down to individual technical tasks. The details of all these changes may be very different, but there is one basic idea that can help us handle change effectively, wherever and whenever it occurs: Keep things small.
Small things are easier to move around than big things. I can’t cite any published academic studies that prove that assertion, but you can conduct a quick-and-dirty experiment right now. Find the largest, heaviest piece of furniture in the room where you are. Try to move it across the floor. Oh, come on. You’re not even trying. Put your back into it! Heave! Heave!. Yeah, that’s more like it. Now imagine what it would be like if you had to line up the furniture one piece behind another to represent a “priority sequence” of furniture. What would happen when your customer demanded a sudden and immediate change of priority? How easy would it be to shuffle all those heavy pieces around into a different sequence? How easy would it be to “cancel” a heavy piece of furniture by picking it up and throwing it out the window?
Now, locate a small, light object on your desk; perhaps a wireless mouse, a thumb drive, or (if such objects still exist in your world) a pencil. Try to move it around the room. How did the level of effort compare with that of moving the piece of furniture around the room? Line up a few of these objects on the desktop to represent a “priority sequence.” Now pretend your customer demanded a sudden and immediate change of priority. How difficult was it to re-order the small, lightweight objects? How hard would it be to “cancel” one of them by dropping it into the wastebasket? Did you notice a difference in the level of effort required as compared with rearranging the furniture?
It’s the same with the changeable things in our work. Small things are easy to shuffle around and relatively cheap to drop in mid-stream. Big things tend to take on a sort of inertia. They are hard to turn and harder to stop. There are even some insidious problems with large chunks of work that don’t seem to occur with smaller chunks. For instance, people tend to associate size with importance. Rather than considering the true business value of various projects, people tend to assume the larger projects are inherently more important than smaller ones. People don’t like the idea of canceling large projects, but they will cancel smaller ones without worrying about it too much. It’s the same psychology casinos take advantage of to keep people on a losing streak: We’ve sunk so much money into this already, we just have to see it through to the end! We don’t usually feel that way about small projects; not even when it’s the same work as the larger project, but divided up into smaller pieces.
The same rule of thumb applies at all levels from managing a portfolio to defining a set of test cases, and to all activities involved in software delivery from requirements elaboration to solution deployment.
- Portfolio management — it’s easier to reshuffle, rescope, and cancel small projects than large ones; break large initiatives into a series of smaller ones.
- Budgeting — keep cash resources as liquid as possible; have funding available for exigencies; take periodic checkpoints of projects in flight, reassess priorities, risks, and potential value on a regular basis; adjust budget allocations accordingly.
- Requirements — create a high-level vision for a solution; refine requirements in detail for features soon to be developed, mitigate risks for features somewhat farther out in the future, avoid details for features planned for the distant future; think of “requirements” as options that have potential value and an expiration date; keep fine-grained feature definitions as independent of one another as possible, so they can be rescheduled without excessive impact on other work items.
- Coding — develop code in small chunks, interleaving unit test activities with coding activities to build quality in; strive for small, cohesive code units, loose coupling, and non-brittle interfaces; practice incremental refactoring to keep the codebase clean and easy to modify without introducing regressions.
- Integrating code — refresh your working copy of the codebase frequently; check in changes a little at a time and frequently; minimize the impact of collisions, minimize the need for tedious merges.
- Testing — test incrementally as code becomes available; avoid delays in providing feedback to upstream process steps for defect correction; participate directly in requirements elaboration to ensure all system behavior is testable, to minimize the number of different artifacts that express the same information, and ensure everyone involved in development has the same understanding of “done.”
- Writing user documentation — develop documentation incrementally, in sync with code development; don’t wait until all the code is finalized before beginning; avoid uneven workloads and the confusing results that can be caused by waiting long periods of time and then rushing to complete a mass of documentation quickly.
- Packaging for deployment — package and repackage incremental results frequently so that the packaging process does not become overwhelming and error-prone, and to expose problems early in the development process.
- Deployment — practice deploying the solution throughout development, to avoid surprises and significant implementation effort at the end; incrementally refine deployment scripts and procedures as the solution is incrementally built up.