In the beginning there was me. And I created a tool. And the tool was good.
Well, it was better than the ad hoc interfaces we had used before, mostly based on internal test applications. It had a generic structure which allowed it to take in a description of the device being supported, and a converter which generated that description off the definitions used in the design of the product. It followed up-to-date user interface conventions. It had a modular structure which allowed new features to be supported easily, and these features could be keyed to the definitions in the product interfaces.
And the tool grew, and its test harness grew around it, and life was good.
And so it continued for about 5 years. During this time, the tool grew as features were added to our devices. And with 100 designers extending our devices and just me extending the tool, I had to become selective in which features I gave support for and how. So I provided tools for hardware engineers to extend the descriptions of the devices they supported so that they could take some of the load without having to become software engineers.
By this time the codebase had grown to several hundred thousand lines of code, but I had written all of it and knew my way intimately around it. And anything I had forgotten was captured in the unit tests so I knew instantly when I broke anything.
And then finally the company agreed that the tool was now so important, and the features were overtaking me so fast, that it was time to extend the team. And suddenly from a team of 1 we had a team of 8 (4 permanent and 3 contractors plus me, spread across 2 sites).
We all had a fast learning curve that summer…
I had to learn how much guidance to give, how much to trust, and how much to check.
The team had to learn a large codebase. They also had to learn the in-house test harness, agile development, and a lot of domain-specific knowledge. And they struggled.
When there was just me, I didn’t need to document the architecture. I knew how it all fitted together, and I could hold it all in my head. To have written a design document would have been waste – there was only me to read it, and I didn’t need to.
When the team joined, I sketched out some high-level architecture documents and gave some training, and hoped that would be enough. I was so busy running the team and managing expectations and giving guidance that I didn’t have time for more. So they started, and they picked things up, and I reviewed the code and gave guidance when it wasn’t up to my stringent standards. And they began to despair. And then they learned how to get code accepted by me, and I stopped reviewing every line of code, and they grew happier.
In the mean time, they had been learning about this “agile” process. And they learned from me. And there were many tests. And I stressed the tests and test-driven development, and I pulled them up in reviews when they didn’t have tests. So they learned to write tests. But because I had had the whole design in my head and knew how to extend it, I had never written down a design before I started working, and so although there was this skeleton architecture document, no-one cared for it and it grew more and more out of date.
So people picked up stories and started working on them, and we would come to code review and find they had spent several weeks doing the wrong thing. And we had a painful choice – either restart and throw away several weeks work, or try to bodge the code so it was closer to what was needed. (We’re still hurting from some of those bodges, by the way. Technical debt extracts a very high rate of interest.)
So we retrospected and decided we needed to catch this before a long time had been wasted on incorrect code, and we introduced design documents and design reviews. And these were text documents describing the changes which would be made (text so that they were easy to compare one revision to the next). And they went into great detail of the new functions which would be created and the parameters they would take and exactly how they would be implemented. And these documents took several days to write and were daunting (and boring) to review, and needed to be reviewed in the context of the existing code. And then the design would have to change once it came to implementation and we learned. It did mean we caught issues earlier, but it was not much more efficient. And these documents were no help to anyone new trying to learn an area of the code because they were too detailed to study and only told part of the story.
So gradually they fell out of usage because they slowed us down. And the errors started coming back and we were again developing the wrong thing. And different developers working on different stories found they were working at cross purposes, and the code I had written was taken as sacrosanct even when it no longer fitted the design, and the bodges began to accumulate.
So we stopped the line and held a review. And we concluded that we did need the pre-code design review, but we needed a different approach which was quick to assimilate and led to useful design documentation at the end.
Now we have a new approach. Our design documentation is a collection of one-page diagrams describing different aspects of the product. When we start on a new story, we modify the appropriate design diagrams to show how the change will affect the design, and we review that. Because it is a diagram, it is easier to assimilate detail quickly, and it encourages “just enough” detail without going into low-level specification that is irrelevant to the architecture. Because we’re updating the actual design, we have an up-to-date design, and we have a record (in the check-in log) of how and why the design has evolved.
We have also started using SpecFlow (Cucumber for .Net) to specify acceptance test examples during the design phase, so we can agree before starting how we will know when we are done. And this should give us documentation of how the product behaves that anyone can read, and which is checked every time the tests are run (which is on every checkin).
It’s early days yet, but this seems to satisfy the conflicting demands of providing enough documentation for people to understand the code, and to allow changes to be reviewed when it’s cheap, while still remaining cheap to do. It can take a few hours to update the diagrams, but that’s better than a few weeks to refactor the code into shape.
Of course, your situation will be different, so this may not be the right approach for you. You may need more documentation. You may need less. When there was just me, the design documentation was waste. As soon as we had a team, documentation became essential, though it has taken us a couple of years to really appreciate this. The text version of the design documentation for each change was waste because no-one was really interested in reading it and it wasn’t much quicker to write than the code it was replacing.
How do you know how much documentation you need? I always fall back on the question “who is going to read it?” If someone will read it and look for it, and people will be interested in keeping it up to date, you should write it. If no-one will read it, it’s waste. If it won’t be maintained and gets out of date, it’s waste. If it’s too verbose so that no-one can find the information they’re looking for, again it’s waste. Either cut it out, or find a more digestible way of capturing the information that people will read and maintain. But even if you’re agile, you probably do still need some level of documentation unless you’re a team of one (and possibly even then).