Bolour Computing logo

October 24, 2006

Task Dependencies in Plans

Filed under: process — Azad Bolour @ 7:32 am

The GANTT chart, as embodied in Microsoft Project, has become the de facto method of representing software development plans in many organizations. This is unfortunate. For, the precise temporal positioning of all tasks within a timeline is seldom necessary for managing the progress of a software project.

GANTT charts reinforce two false assumptions about development plans: that each task must be completed within an estimated duration and effort, and that each task must be completed by a particular time. The pre-assignment of developers to tasks in specific time periods breaks down because there is significant estimation uncertainty in software tasks.

Agile methodologies recognize these shortcomings, and use simpler means of planning that de-emphasize precise temporal positioning of tasks and people in development plans. Agile developers can generally work in many different areas of the code base, partly because they can easily pair with area specialists. Since most developers can then work on most of the code base, the necessity to pre-assign developers to tasks in time is considerably diminished.

Of course, the breakdown of a release, or an iteration, into distinct tasks typically results in temporal dependencies between those tasks. But then a PERT chart representing these dependencies that also includes just effort/duration estimates for each task is a more appropriate means of depicting the task breakdown.

Still, agile methodologies can generally get away with simpler plans than even such PERT charts. So what gives? Is it important to represent task dependencies in the development plan? I believe it is. At the same time, there are simple ways to avoid the explicit representation of many dependencies in the development plan.

As development teams, we need to make and keep delivery commitments to the rest of the organization. A crude projection of the delivery of a release is obtained by estimating effort for individual tasks, summing up the estimates, and dividing the total by the available manpower. A better projection is obtained by taking task dependencies into account and by exposing critical paths.

Finessing Dependencies by Phasing

A simple strategy for ensuring the correct temporal ordering of dependent tasks is the use of temporal phases. For example, the Rational Unified Process defines four phases of the development process: inception, elaboration, construction, and transition. So the dependency of construction work on inception tasks, like baselining the release’s vision and architecture, is automatically satisfied.

Phasing has gotten a bad wrap because of the well-known shortcomings of the particular phases of the waterfall model. But it is not necessary to use waterfall phases in our plans.

An earlier blog in this series talked about a pipelined phasing model of iterative development, in which the (short) pipeline stages of R&D, construction, and deployment occur in sequence for each iteration of an iterative and incremental development project. In that model, the dependency between the program design+construction of a feature, and the R&D work needed to conceive that feature, is implicitly satisfied by the temporal ordering of the stages in which these activities are scheduled. Phasing provides a simple and intuitive method of covering dependencies without directly representing them.

But clearly phasing cannot cover all dependencies.

Service Dependencies

The most prominent example of dependency in software is the dependency of one piece of functionality on another. I will call this type of dependency a service dependency. In a service dependency, a client task is dependent on a service task.

There are times when the developer(s) providing the dependent function will also develop some of its dependencies as a side effect. In that case, from the point of view of the development plan, there is no need to track the dependencies folded into the dependent task. But folding the development of a dependency into the development of a dependent is not necessarily the right approach. Sometimes the dependency is a whole separate abstraction, and it makes sense to use a separate task for its development.

For example, a web site registration system generally depends on an email service to send opt-in confirmation messages to registering users. If we actually have to build the email service ourselves on top of a standard email package, say because our email service has special requirements, then the email service is considered to be a separate component, and the registration component becomes dependent on the email service.

The nice thing about a service dependency is that the client can be isolated from the service by using an abstract interface. Then the service may be mocked to enable the client development task to proceed in parallel with the service development task. Because the implementation of a service dependency can often be mocked, once a service interface is drafted, work on the client and the service may proceed in parallel. Later, when the production implementation of the service becomes available, the client and the service are integrated.

When all service functions needed by the client can easily be mocked, the client can be built and tested separately in its entirety, and integration with the production implementation of the service can be expected to be a simple matter. One might then forgo the explicit representation of the integration task.

But significant integration work can result from the infeasibility of mocking every last nuance of the service interface that is used by the client. At integration time, client tests that exercise the hard-to-mock functions of the interface have to be written, and these tests have to be made to pass with the production implementation of the service. In this case, the integration task needs to be tracked separately from the client, since work on it is not necessarily contiguous with work on the mock-based client.

Here is a sketch of the resulting tasks and their dependencies embodied in an activity on node PERT chart with effort/duration estimates (assuming, for simplicity, that effort and duration are the same).

Figure-1
parallelized client and server tasks and their dependencies

Clearly the benefits of parallelism come at the price of a more complicated task structure. But the benefits are often worth the price.

Of course, if we can anticipate service dependencies during the R&D stage of an iteration, then we just draft up a service interface right there and then, and the dependencies of the client and service on that interface will be implicitly satisfied.

Summary

There are clearly a variety of ways of avoiding the explicit representation of dependencies in the development plan: phasing, folding dependencies into dependent tasks, ignoring trivial integration dependencies, or not including dependent tasks in the plan until their dependencies are satisfied. But every so often, a dependency structure will crop up that cannot be finessed, as illustrated by non-trivial service integration dependencies. These dependencies may be represented in a PERT chart for critical path analysis, and to aid in anticipating the likely division of labor between developers.

© Copyright 2006 Bolour Computing.

  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • LinkedIn
  • StumbleUpon
  • Technorati
  • TwitThis
  • E-mail this story to a friend!

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Enter this code

Powered by WordPress