Using Agile Techniques to Keep Projects Under Control

Good business

According to accepted business management wisdom, business success is improved by increasing throughput while reducing operational costs and inventory. In software these general map to the following:

Throughput = money raised by selling the product.

Operational costs = the cost of running the business (not paying the developers)

Inventory = the amount of time (and therefore money) invested in code that is presently not being sold to customers

If the project is managed with some of the traditional methods, throughput can dip mid-development, as customers cannot get the in-development features. Simultaneously, inventory can peak mid-development as plenty of features have been completed, or nearly completed, and are not yet deployable to the customer. In order to see how the project might be run differently, we need to imagine a situation where an important customer will pay a premium rate for the bug fixes and features that the developers have been working on since the last release, provided that they can have the release rapidly. In this situation, we are looking to cash in on the inventory of the development work that has been completed and we are going to improve our throughput with this big order. It clearly meets the requirements for successful business, but can we do it?

Rapid deployment?

Immediate questions:

1. Will the code build?

2. Can we convert the build into an installable release?

3. Are there any loose ends that need tying up?

4. Does all the new code work perfectly? Does it need testing and more fixing? If not, can we easily get to a version where the non-working code is backed out or disabled?

Barriers to Rapid deployment?

In traditional project management, it is quite common, somewhere in the middle of the project, for the above to be big barriers to a rapid release.

1. With multiple developers working on modules concurrently, it's possible that the latest code won't quite reintegrate yet. Someone may have broken the build in the last few hours.

2. Making an installation of your code is often the last thing that you do. Perhaps the process of creating an installation is in multiple steps, done irregularly and maybe this is prone to error if rushed.

3. With multiple developers, all working on their own tasks, perhaps the current codebase has various incomplete sections. These may have co-dependencies and there may be a lot of development required to move the software into a coherent state.

4. Testing is often a function that's performed towards the end of the development process. The most recent changes may not have been tested. Some regression testing may also reveal areas of the code which no longer work. The process of testing to establish the situation may take a few days. The process of tying up these problems may take even longer.

Unless your project is planned to avoid the above problems, then it cannot be rapidly deployed on demand. Moreover, it is unlikely that it will be deployed as rapidly as possible at the planned time in a long project plan. The problems in numbers 1 and 2 can be avoided easily, the problems in 3 and 4 take discipline and can be solved if the project is kept in sync and deployable throughout its lifecycle. Keeping the project deployable requires a change in planning at task level, but allows for early delivery (with reduced scope) and an rapid adjustment of the work, perhaps based on customer feedback from mid-development stable builds, which themselves should be easier to create more often.

How to improve

1. Keeping the code building - continuous integration

Set aside a machine as a build machine

Run Cruisecontrol.net (or similar) on the machine

This will monitor your sourcecontrol database (SourceSafe or similar) for changes in the project sourcecode

Every time modifications are detected, a build script will be run on the latest code

If the build fails, then an email is sent around to all the developers (or an alarm sounds), everyone has to stop and fix the problem before continuing

As a result, any breakages in the build will be resolved quickly, within a few minutes of their happening and with the revision history that illustrates the cause of the breakage easily available. Therefore, the likelihood of a hugely broken build slowing down development is reduced.

2. Keeping the code installable - installation scripts

Write a script to automate the conversion of a build into an installation program (or deployment unit)

If you can't do it with the tools you normally use, then find new ones

Put the deployment as installation script into the continuous integration build

As a result, a developer's check-in to the code will be taken from end-to-end and compiled into a deliverable version of the project, which could theoretically be given to a customer. This will happen frequently, without human error, and will make it easier to evaluate mid-development builds for feedback. You can automatically copy the last-known-good build to a public folder, or archive key builds manually.

3. Incomplete work - finish everything

Rather than allowing developers to start large scale modules, which will remain in an incomplete state for a long time, encourage them to work on something that they can finish and re-integrate within around half a day (or less). If the next part of the work is too large, then perhaps they should either replan to do a thin slice of it, or prepare the code to be in a better state before adding the new feature.

There is bound to be a way of adding the feature incrementally, a bit at a time, even though this may seem less efficient than doing it completely the first time. The benefits of keeping the project in sync outweigh the overhead of doing it in baby steps. In addition, the incremental approach allows for more early feedback, which can help avoid overengineering.

Completing the work includes completing the necessary testing to guarantee that it is deployable at every step of the way. This needs something clever to make it work.

4. Testing cannot be performed at the end to pick up the pieces - test first

Testing is something we use to determine whether the software works. Why do we leave it until the end of the process then? How can a developer be certain that they have completed what they are working on? How can they move on and run the risk that their work is incomplete, undeployable and, potentially, a source of problems in a few days' time, when short-term memory has faded?

The solution, write all the tests before the code. At first they will all fail as there is no code. Then, as the code comes into being, it will either pass the tests, or it will be in an incomplete state. As more understanding is gained of the problem, write more tests and get those to pass too. Then, when there is no further test to write and all tests pass, the task is complete.

Tests should be automated. There should be a combination of unit and system level tests. Unit tests should be defined to demonstrate the intent of the code in that unit. System level tests should be defined as customer-understandable use cases.

All the automated tests should be run on every single build. They should be included in the continuous integration for the project.

Rapid Deployment of Incremental Changes

By following all of the above, the project changes into an incremental process where people write automated tests that demonstrate a small change they plan to make. They then write code which makes these tests pass and does not break any existing functionality (the automated nature of the tests means that they are easy to run and should be quick to run, so they can be run several times by the developer). The incremental change is then checked back into the source-code library and automatically reintegrated with all other recent changes, automatically tested and then converted into a deployable installation. This could happen several times an hour. The project remains totally deployable and could even change direction within minutes based on regular feedback from customers, who can have access to very recent builds.

What happens with this approach?

With a rapid turnaround of incremental development, planning the project becomes a case of prioritising the next set of increments which could make the product more fit for sale. With the ability to review often and call an end to development within a few hours of a successful review, it becomes possible to cash in on the inventory if the developers have done enough to satisfy a key customer. The feedback process can avoid developers wasting time on a fringe case that no customer would be interested in covering. Equally, the feedback process can raise significant problems early on, before too much development time has been invested in them.

Finally, the short feedback loop between writing code and deploying it should avoid problems lying undetected among the loose ends left when a developer took too big a chunk and checking it in incomplete.

Tools

Keeping Code Clean

One final note is that code should be kept in a workable state throughout the development process. If the code starts to get complex, it will slow the developer down. When writing new code, the developer should take the "do the simplest thing that can possibly work" approach, writing simple and clean code to deal with only the problem in hand. This may be short-sighted, but it is intentional. It's possible that the code might be deployed after this step, and a simple solution may be all that is ever needed. However, as code is extended to do more, so it should be refactored. Refactoring retrospectively redesigns the structure of the code without changing its behaviour. This is usually done to allow extension to be performed, or to simplify the code to better express the simplicity of the system after something has been removed. As there will be automated tests, refactoring becomes a relatively safe process as the tests will demonstrate whether the latest refactoring has changed the behaviour of the system.

Other Agile Techniques

Agile teams use regular planning and pair programming. They also try to work in as customer-focused environment as possible, ideally in close contact with a genuine customer, who will ultimately be using the product.

Planning

Plan work in small pieces. Plan to complete a week's worth of these tasks, which should be expressed in terms of tangible benefit to the end-user, and should be priorities such that the most important tasks for the customer are done first. At the end of each week, review progress, deliver the latest version and plan the next week. These planning windows are called iterations.

Every day should begin with a short meeting where the team share what they have been doing and plan together what they will attempt to be doing that day to keep the iteration on target.

Pair Programming

Programmers work together at the same computer on the same task. One person uses the computer to enter the code, the other checks it, suggests alternatives, thinks of tests, shares in the knowledge of what is going on and tries to avoid the solution becoming too complex. When done effectively, this can improve the quality of code and understanding of the product.

Updated: 01 April 2007

Ashley Frieze