Last month, I showed you how to use code metrics to evaluate the quality of your code. While the cyclomatic complexity metrics introduced in that column focus on low-level details, such as the number of execution paths in a method, other types of metrics focus on more high-level aspects of code. This month, I'll show you how to use various coupling metrics to analyze and support your software architecture.
I'll start out with two of the more interesting coupling metrics, namely afferent coupling and efferent coupling. These integer-based metrics represent a count of related objects (i.e., objects that coordinate with each other to produce behavior). High numbers in either metric can signify architectural maintenance issues: High afferent coupling indicates an object has too much responsibility, and high efferent coupling suggests the object isn't independent enough. This month, I'll look at each of these problems and some ways to get around them.
Having too much responsibility isn't necessarily a bad thing. For example, components (or packages) often are intended to be utilized throughout an architecture, which gives them high afferent coupling values. Core frameworks (like Struts), utilities like logging packages (like log4j), and even exception hierarchies usually have high afferent coupling.
In Figure 1, you can see a package,
com.acme.ascp.exception, with an afferent coupling
of 4. This isn't a surprise because the
frmwrk packages would all expect to utilize a
common exception framework.
Figure 1. Signs of afferent coupling
As you see in Figure 1, the
package has an afferent coupling, or Ca, of 4, which in its case
isn't such a bad thing. Exception hierarchies rarely change
dramatically. Monitoring the afferent coupling of the
exception package is a good idea, however, because
drastic changes to the behavior or contract of exceptions in this
package could cause ripple effects throughout its four dependent
By further examining the
exception package and noting the ratio of
abstract to concrete classes, you can derive another metric:
abstractness. In this case, the
exception package has an abstractness of zero because
all its classes are concrete. This correlates with my earlier
observation: The high degree of concreteness in the
exception package means that any changes to
exception will affect all related packages, namely
By understanding that afferent coupling denotes a component's responsibility and by monitoring this metric over time, you can shield a software architecture from entropy, which some say naturally occurs even in the most well-designed systems.
Many architectures are designed with flexibility in mind when utilizing third-party packages. Flexibility ideally is gained by using interfaces to shield the architecture from changes within third-party packages. For example, system designers could create an internal interface package to utilize third-party billing code but only expose interfaces to those packages that use the billing code. This, by the way, is similar to the way JDBC works.
Figure 2. Flexibility by design
As Figure 2 demonstrates, the acme.ascp
application is coupled to a third-party billing package
This creates a level of flexibility: If another billing package from
a third party becomes more advantageous to utilize, then only one
package should be affected by the change. What's more,
com.acme.ascp.billing's abstractness value is 0.8,
which indicates it can be shielded from modifications through its interfaces and abstract classes.
If you were to switch third-party implementations, any
refactoring would need to happen to only the
com.acme.ascp.billing package. Even better, by
designing-in this flexibility and understanding the implications of
change, you can protect yourself from any damages from modifications through developer testing.
Before making changes to the internal billing package, you could analyze a code coverage report to determine if any tests actually tested the package. On finding some level of coverage, you could more closely examine those test cases to verify their adequacy. If you found no coverage, you would know that the level of effort to switch out and insert a new library would be riskier and could take longer.
Gathering all these factoids is very easy using code metrics. On the other hand, if you know nothing of a package's coupling related to its test coverage, then ascertaining the time to replace a third-party library is, at best, a guess!
As I mentioned earlier, entropy has a way of working itself into even the most well-planned architectures. Either through team attrition or poorly documented intents, uninitiated developers can inadvertently import what appears to be a useful package, and before long, your system's afferent coupling values begin to grow.
For example, compare Figure 3 with Figure 2. Do you see the
increased brittleness of the architecture? Not only does
dao package now directly utilize a third-party billing package, but another package that wasn't even intended to use any billing code
directly references both billing packages!
Figure 3. Code entropy creeps in
Attempting to switch out the
com.third.party.billing package for another one is
going to be challenging indeed! Just imagine the test scaffolding that
would be required to mitigate the risks of introducing defects and
breaking various behavioral aspects of the system. In fact,
architectures like this one rarely change because they can't
support modification. Worse, even important modifications, such as
upgrades to existing components, can cause things to break throughout
the code base.
If afferent coupling is a count of components that depend on a particular component, then efferent coupling is the count of components that a particular component depends on. Think of efferent coupling as the inverse of afferent coupling.
The implications of efferent coupling are similar to those of
afferent coupling, with regard to how changes affect code. For example,
Figure 4, depicts the
package, which has an efferent coupling, or Ce, of 3:
Figure 4. Efferent coupling in the dao package
As Figure 4 shows, the
package depends on the
to fulfill its behavioral contract. As is true of afferent coupling,
the level of dependence isn't a bad thing in and of itself. It's your knowledge of the coupling and how it could affect changes to related components that matters.
As with afferent coupling, the abstractness metric comes into play in efferent coupling. In Figure 4, the
package is completely concrete; hence its abstractness is 0. This means
that components whose efferent coupling includes
com.acme.ascp.dao could themselves become brittle because of
com.acme.ascp.dao's efferent coupling on
three additional packages. If one of them changes (say
com.acme.ascp.util), a ripple effect could occur
is unable to hide implantation details through interfaces or abstract
classes, any changes could then impact on its dependent components.
Examining efferent coupling's relationship data and relating it to
code coverage facilitates smarter decision making. For instance, imagine
that a new requirement is handed down to your development team. You're
able to pinpoint changes related to this requirement to the
com.acme.ascp.util package shown in Figure 4. Also, in
the past few releases, the
which depends on
util and has zero
abstractness, has suffered from a number of high-priority defects
(most likely due to limited developer testing on this package, which
interestingly is most likely because of high complexity values within the
You have an advantage in this situation because you understand the
com.acme.ascp.dao. Knowing that the
dao package depends on
util tells you that any modifications to support the
new requirement in
util could adversely
affect the troublesome
Seeing this link assists you in risk assessment and even in a level
of effort analysis. If you hadn't noticed the link, you might have
guessed that a quick coding effort would be required to support the new
requirement. Having seen the link, you can allocate the appropriate time
or resources to mitigate any collateral damage that occurs in the
Just as continuously monitoring afferent coupling can uncover entropy
in an architectural design, so monitoring efferent coupling can assist
you in spotting unwanted dependencies. For example, in Figure 5,
it appears that at some point someone decided that the
com.acme.ascp.web package had something to offer to
com.acme.ascp.user. Somewhere in the
user package, one or more objects are actually
importing an object from the
Figure 5. Efferent coupling in the user package
Clearly, this wasn't the original intent of the architecture's
design. Because you regularly monitor your system for efferent coupling,
however, you can easily refactor and correct this discord. Perhaps the
useful utility object from the
should be moved to a utility package so that other packages can utilize
it without inviting an unwanted dependency.
You can combine your system's efferent coupling and afferent coupling numbers
to form another metric: instability. By dividing efferent coupling by the sum of both efferent and afferent coupling (
Ce / (Ca + Ce)), you produce a ratio that signifies either a stable package (a value close to 0) or an unstable package (a value closer to 1). As this equation reveals, efferent coupling works against a package's stability: The more a package relies on other packages, the more susceptible it is to
ripple effects in the face of change. Conversely, the more a package is
relied on, the less likely it is to change.
For example, in Figure 5, the
package has an instability value of 1, meaning it has an efferent coupling of 4
and no afferent coupling. Changes within a package like
com.acme.ascp.dao will affect the
When designing and implementing an architecture, depending on stable packages is advantageous because those packages are less likely to change. Similarly, unstable package dependencies increase the risk of collateral damage within your architecture during times of change.
So far, I've introduced you to afferent coupling, which you can use to evaluate the affects of changing a package, and efferent coupling, which you can use to evaluate how outside changes will affect a package. I've also talked about the abstractness metric, which is helpful when you want to understand how easily a package can be modified, and the instability metric, which you can use to understand how a package dependency will affect a particular package.
You can use yet another metric to learn about factors
that affect your software architecture. This metric balances
the metrics of abstractness and instability through a straight line on an X, Y
axis. The main sequence is a
line on the Cartesian coordinates
Y=0, as illustrated in Figure 6:
Figure 6. The main sequence
By plotting packages along this line and measuring their distance from the main sequence, you can infer a package's balance. Either a package is balanced with respect to abstractness and instability, in which case its distance is close to 0, or a package lacks balance and therefore its distance from the main sequence is closer to 1, as shown in Figure 7:
Figure 7. The distance from the main sequence
Examining the distance from the main sequence metric yields
interesting results. For example, the
package from above generates a value of 0. This package is balanced in
the sense that it is an implementation package: highly unstable.
In general, the distance from the main sequence metric attempts to compensate for real-world implementations. No code base contains all packages with abstractness and instability values of 1 or 0 -- most packages have values somewhere between the two. By monitoring the distance from the main sequence metric, you can gauge whether packages are becoming unbalanced. Looking for outlying values, such as those packages whose values are closest to 1 (meaning they are as far from the main sequence as possible), can help you understand how a specific unbalancing could affect the maintainability of your architecture (for example, through brittleness).
This month, you've learned about several architectural metrics that you can monitor over time. Afferent and efferent coupling, instability, abstractness, and distance from the main sequence are all reported by code analysis tools, including JDepend, JarAnalyzer, and the Metrics plug-in for Eclipse (see Resources). Monitoring your system's code coupling metrics will help you stay on top of common trends that can undermine its architecture, namely design rigidity, package entropy, and unwanted dependencies. In addition, measuring your system's balance in terms of abstractness and instability gives you an overview of its maintainability over time.
- "In pursuit of code quality: Monitoring cyclomatic complexity" (Andrew Glover, developerWorks, March 2006): Identify risky code with simple code metrics and Java™-based tools that monitor cyclomatic complexity
- "In pursuit of code quality: Don't be fooled by the coverage report" (Andrew Glover, developerWorks, January 2006): How test coverage measurements can
lead you astray.
- In pursuit of code quality: Read the complete series by Andrew Glover.
- "Multithreaded unit testing with ConTest" (Yarden Nir-Buchbinder and Shmuel Ur, developerWorks, April 2006): A coverage measurement solution for concurrent programs.
- "Testing legacy code" (Elliotte Harold, developerWorks, April 2006): Develop a unit test suite for legacy code that's never been tested.
- The Java technology zone: Hundreds of articles about every aspect of Java programming.
Get products and technologies
- JDepend: A Java package dependency analyzer that generates design quality metrics.
- JarAnalyzer: This tool analyzes the relationships among jar files.
- Metrics plug-in for Eclipse: Calculates cyclomatic complexity and other metrics related to code complexity and coupling.
- Participate in the discussion forum.
blogs: Get involved in the developerWorks community!
Andrew Glover is the President of Stelligent Incorporated, which helps companies address software quality with effective developer testing strategies and continuous integration techniques that enable teams to monitor code quality early and often. He is the co-author of Java Testing Patterns (Wiley, September 2004).