Ignoring Dependencies

IgnoringDependencies is the practice of paying little or no attention to the network of prerequisites and dependents that form the context for a given piece of code.

For example, a class that extends another class is dependent on the class it extends. It is often dependent on a very specific version of the class it extends.

Classes are generally aggregated into a higher-order grouping, such as a Package (in Java). Dependencies among classes therefore imply in dependencies among packages. The Java language specification unfortunately does not provide a mechanism for specifying these dependencies (a package-level equivalent to the "includes" directive).

Packages, in turn, are often aggregated into yet another higher-order grouping, such as a "feature", "module", "product", "service", "application", or whatever. Class and package dependencies imply module-level dependencies as well.

Many programmers are loath to address these dependencies, and industry tools are spotty at best. In a heterogeneous environment of multiple platforms, vendors, languages, and even developer cultures, the resulting directed graph of dependencies and prerequisites can become extraordinarily complex -- leading to a strong habit, often reinforced by short-sighted management, of IgnoringDependencies.

These dependencies are, however, very real, and the consequence of IgnoringDependencies is systems that do not work.

The book LargeScaleCppSoftwareDesign addresses the multi-layer organization addressed above, and contains some good advice on how to structure large software systems (advice that extends well beyond C++). In particular, there should be no cycles at any level of the software dependency graph, with the possible exception of small cycles of no more than 3 (preferably 2) classes at the lowest (class) level.

In other words, the class dependency graph should contain no cycles, other than small ones as indicated above; the package dependency graph should contain no cycles at all; nor should the dependency graph for larger software structures (programs, libraries, components).

One issue with both JavaLanguage and EiffelLanguage, which only consider classes and not larger structures in how dependencies are resolved, is that is is quite possible (and easy) for the class graph to be acyclic, but the package/module graph to be cycle. Consider two packages, P1 and P2, and three classes A, B, and C. Classes A and C are in package P1, B is in P2. C depends on (inheritance, composition, whatever) both B and A, B depends on A. It is easy to see that there is no cycle in the class dependency graph; but a cycle at the package level, as P1 requires P2 (as C required B) and P2 requires P1 (as B requires A).

Building on this, let me also observe that the higher-level structures are generally also the "grains" that are sold to customers. This means that decisions about which structure requires which other structure are very important -- maybe even primary -- to the business model of the enterprise. This means that the developers must, in general, adapt to the dependency requirements of the business people, and not vice-versa.

This, in turn, exacerbates the impact of IgnoringDependencies. While YAGNI is all well and good, if your high-velocity XP process leads you to, for example, stop worrying about the cycle that often exists between the presentation and object model layers, you can suddenly find yourself -- a week before deadline -- realizing that you really DID need that fancy callback registration mechanism that you took out because it was complicated, nobody wanted to figure out how it worked, and the system was passing all its unit tests.

IgnoringDependencies is an enormously dangerous practice.


EditText of this page (last edited October 30, 2004) or FindPage with title or text search