A method of illustrating program structure by showing how sections depend on each other is presented. This suggests an intuitive metric for program partitioning, which is developed with supporting theory.
Also, the lack of a way of visualizing the program structure has lead to many programmers (myself included) becoming lost in the final solution as they converted it into an actual program. I started to draw diagrams of the program that I was working on, adding, moving and deleting pieces as the program developed and problems with the translation from design through pseudo-code to high-level language code were overcome. Over time, I decided that it was most helpful to make the diagram show the relationships between sections of the program, so that if one of the fundamental units of the program required alteration (for some unforeseen reason), it was obvious which other units this would affect.
I noticed that these diagrams and their construction could be formalized, as presented here.
All units can be classified as either dependent or independent. An independent unit does not invoke any other program unit. That is, it is written using only the language's built-in keywords and library routines supplied to the program. A dependent unit invokes at least one other program unit.
Either of the above definitions could equally well have been stated as ``a unit is dependent if and only if it is not independent.'' Being able to classify each unit as either dependent or independent, by itself, is not very useful in helping to assess how well the problem has been separated, but it is an essential step towards this. More useful, is the generalization of dependence, provided by the following definition:
A unit, x, is said to be dependent on another unit, y, if x contains a call to y. Equivalently, x is called a dependent of y.This is the basic relationship that I will work with. Notice that the unit y does not need to be called whenever x executes, only that x may need to call upon y. This definition of dependency was arrived at by the original need for documenting these relationships -- to verify that unit x would still work correctly if unit y is changed, regardless of the fact that x may not always call y (and so errors may exhibit themselves in some tests, but not others).
One way of keeping track of dependency relationships is through the use of a dependency table, which lists each unit, followed by its dependent units. For example, such a table could look like this:
| Unit | Dependent Units |
|---|---|
| NewList | Copy, Sort |
| Length | Copy, Search |
| Copy | -- |
| Search | Sort |
| Sort | -- |
The table shows that Copy includes calls to Length and NewList; Search includes a call to Length; Sort includes calls to NewList and Search.
A dependency table is a useful tool for tracing the effects of changes through small programs, but searching through a table of any length (even as little as 15 units) is rather tedious. Therefore the dependency diagrams were developed.
So, a unit A dependent on a unit B would be represented on a dependency diagram like so:

A dependency diagram may be built up by using the following algorithm:
To create a dependency diagram for the example shown in the dependency table, we first place the independent units NewList and Length on the graph:

Then, Copy and Search link only to NewList and Length:

Finally, Sort links to Search and NewList:

A few features of dependency diagrams deserve special mention:


If the compiler being used does not support such a situation, it may be identified and eliminated before compilation.
So, in a program that has been divided up well, each of the ``top-level'' units will call a few units from the levels just ``beneath.'' This can be quantified by the dependency level:
In fact, all level one units have a dependency level of zero. By definition, the only units that level one units may call are level zero units, so the average level of the units that level one units are dependent upon is always zero.
For the Sort unit in the example, the dependency level is:
DL(Sort) = (Level of NewList + Level of Search) / 2 = ( 0 + 1 ) /2 = 1/2
So, the level two unit Sort has a dependency level of 1/2.
By annotating a dependency diagram with the dependency level of each node we can create a dependency level diagram.
So for the above example, the dependency level diagram is:

As can be seen, the dependency level provides a measurement of program partitioning. However, this alone does not allow judgments to be made about whether a program is sufficiently well partitioned. Generally, aiming for a dependency level of at least one quarter of the unit level for all units that have level two or above seems to be a good rule of thumb. It should be noted, however, that this is an entirely empirical result.
Also, it should be noted that there are ways of ``fiddling'' the dependency levels. But these should not be part of the structured programmer's habits, so they do not really pose a significant problem.
Copyright 1996 by Mark Ray
Want more Crossroads articles about Software Engineering? Go to the index or to the next one or to the previous one.
Last Modified:
Location: www.acm.org/crossroads/xrds2-3/dependency.html