Home
Using Metrics To Help Drive Agile Software PDF Print E-mail
User Rating: / 0
PoorBest 
Written by Kirk Knoernschild   
Wednesday, 07 June 2006
The promise of agile development is to deliver high value software more quickly, while remaining responsive to change. But change tends to cause software rot where simple modifications ripple throughout the application, exercising the design in unexpected ways. Avoiding software rot and maintaining design integrity requires frequent refactoring to ensure code remains clean and concise with minimal dependencies between modules. Code quality and design metrics offer objective advice in identifying areas of the application that are solid candidates for refactoring, while coverage metrics provide the guidance and courage necessary to undertake the refactoring effort.

 

Code Metrics

There is a direct correlation between complexity and the defect rate of software, so keeping code simple is a solid first step toward lowering the defect rate of software. The Cyclomatic Complexity Number (CCN) measures code complexity by counting the number of linearly independent paths through code. Complex conditionals and boolean operators increase the number of linear paths, resulting in a higher CCN. Methods with a CCN of five or higher are good refactoring candidates to help ensure code remains easy to understand. JavaNCSS is a utility that calculates CCN.

 

It is possible that a method is clear but still lacks

Advertisement
benavioral integrity. Ignoring error conditions, unused variables, duplicate expressions, and empty statements are examples of common mistakes contributing to lower quality code. Identifying these troubled areas of the application is important in maintaining a robust codebase. While peer code reviews are helpful, they also produce inconsistent results. PMD is a utility that analyzes the source code and identifies code that violates standards defined by your team.

 

Dependency Metrics

While complex methods increase the likelihood of defects, excessive dependencies compromise architecture and design. Complex dependencies present numerous challenges for developers.

  • Dependencies hinder the maintenance effort. Applications with a well-designed dependency structure embrace change instead of resisting change. Applications with complex dependencies tend to break in unexpected ways and in unexpected places when changes are made. For instance, changing a module used by many others is difficult due to the unknown affect of the modification.
  • Dependencies prevent extensibility. Flexible software should be open for extension but closed to modification. The idea is to add new functionality to the system by extending existing abstractions, and plugging these extensions into the existing system without making rampant modifications. Heavy dependencies often result from improper use of abstraction, and extending the system is difficult when abstractions are not present.
  • Dependencies inhibit reusability. Reuse is often touted as a fundamental advantage of well-designed software. Unfortunately, few applications realize this benefit. Too often, we emphasize class level reuse. To achieve higher levels of reuse, developers should carefully consider the package structure and deployable unit structure. Software with complex package and physical dependencies has less likelihood of achieving higher degrees of reuse.
  • Dependencies restrict testability. Unit testing is a fundamental principle that should be employed by all developers. Tight coupling between classes eliminates the ability to test classes independently. Tests provide you the courage to make design improvements, knowing flaws will be caught by unit tests. They also help you design proactively and discourage undesirable dependencies. Heavy dependencies do not allow you to test software modules independently.
  • Dependencies limit understanding. When you work on a software system, it's important that you understand the system's structural architecture and design constructs. A structure with complex dependencies is inherently more difficult to understand.

Traditional analysis and design techniques emphasize the design of a system's lower level structure, but rarely give consideration to higher level structures. To develop reusable, testable, maintainable, and extensible software,  you must minimize dependencies among higher level modules. With Java, for instance, designing only the class relationships while ignoring package and .jar relationships compromises the overall integrity of the design. Dependency metrics are based on the relationships between these modules, where the module dependencies are manifested based on the relationships spanning individual modules.

 

Dependencies between modules are manifest as either incoming or outgoing. Afferent Coupling (Ca) represents the count of a module's incoming dependencies. Efferent Coupling (Ce) represents the count of a modules outgoing dependencies. Knowing a modules afferent and efferent coupling allows you to more effectively evaluate the cost of change and the likelihood of reuse. For instance, maintaining a module with many incoming dependencies is more costly and risky since there is greater risk of impacting other modules, requiring more thorough integration testing. Conversely, a module with many outgoing dependencies is more difficult to test and reuse since all dependent modules are required. Additional metrics, derived using afferent and efferent coupling, allow you to evaluate a module's design integrity.

 

Abstractness (A) is the ratio of the number of abstractions (abstract classes or interfaces) in the module to the total number of classes in the module. The range for this metric is [0...1], with A=0 indicating a module contains all concrete classes and A=1 indicating a module contains all abstract classes. Concrete modules with high afferent coupling will be difficult to change because of the high number of incoming dependencies. Modules with many abstractions are typically more extensible, so long as the dependencies are on the abstract portion of a module.

 

Interesting software, however, is usually composed of modules with both incoming and outgoing dependencies. Instability (I) is the ratio of efferent coupling (Ce) to total coupling (Ce + Ca) such that I = Ce / (Ce + Ca). Modules where I approaches one is an indication the module has proportionally more outgoing dependencies than incoming, whereas modules with I approaching zero indicates proportionally more incoming dependencies than outgoing. Stable modules (I = 0) are more difficult to change because of incoming dependencies. In general, a module should be mostly abstract in relation to its incoming dependencies. Calculated using the the normalized equation |A + I - 1|, Distance (D) represents a modules balance between abstractness and instability. A value approaching zero indicates a module is abstract is relation to its incoming dependencies. As distance approaches one, a module is either concrete with many incoming dependencies or abstract with many outgoing dependencies. The first case represents a lack of design integrity, while the second is useless design.

 

An Example

Let's view an example of how these metrics can be used. Assume we have three modules named customer, billing, and common with the module dependencies dictated by the class relationships depicted in Figure 1. Afferent Coupling and Efferent Coupling are easily calculated by simply counting the incoming and outgoing dependencies, respectively. The formulas and resulting values for Abstractness, Instability, and Distance are shown to the right of each module.


kk0606-1


Figure 1: Class relationships

 

Interpreting these values leads us to potential refactoring candidates. The billing module's distance metric is currently 0.5, indicating it is not entirely abstract in terms of it's incoming dependencies. Refactoring the Bill class to an interface, and introducing a new DefaultBill implementation, takes the distance metric of the billing module to 0, as shown in Figure 2.

 

kk0606-2


Figure 2: Refactoring Bill Class

 

This simple change, discovered by generating and evaluating metrics, offers some rather significant advantages. First, it allows us to test Customer in isolation since Customer tests can use mock Bill implementations, whereas previously the Customer relationship to the concrete Bill class prevented independent testing. Testing Customer in isolation assures us that changes to the DefaultBill class will not break Customer tests. Second, the system is more extensible. Should we need to introduce new subtypes of Bill, the Customer class can easily work with these new subtypes. Third, placement of the Bill interface allows us to redefine the dependency structure between modules. Placing the Bill interface in the customer module and the DefaultBill class in the billing module inverts the relationship between customer and billing, as shown in Figure 3.


kk0606-3

Figure 3: Further refinement

 

Metrics, when used judiciously, help identify areas of the application that are good candidates for refactoring, contributing to more extensible, maintainable, testable, and reusable software. JDepend and JarAanlyzer are two static Java source code analyzers that calculate the metrics above. JDepend works on packages whereas JarAnalyzer works on .jar files. Each integrate well with Ant, so the metrics can be automated as part of a repeatable build process.

 

Coverage Metrics

A key benefit of code and dependency metrics is to identify potential refactoring candidates. But refactoring requires a robust suite of test cases to ensure changes in one area of the system don't impact other areas. Code coverage metrics measure the percentage of methods and classes under test. Developers should strive for 100% test coverage on classes that are behaviorally intense. The CCN can be used as a baseline for the number of tests required per method. If a method has CCN of three, a good starting point to exercise all paths through code would be three tests. Additional tests may be required that exercise error conditions and alternate behaviors. A robust suite of unit tests provides the courage necessary to refactor. EMMA is an example of a tool that can generate coverage metrics for Java applications.

 

Metrics and Agility

Metrics serve as feedback that helps facilitate agility. Rigid designs with excessive dependencies and few tests do not accommodate change well. The metrics discussed in this article, when used in conjunction with each other, help identify areas of the application that are sound candidates for refactoring. For instance, methods with high CCN or modules with excessive dependencies can be refactored to increase the quality of code and design. But refactoring can only commence with confidence if the methods or modules have good test coverage.

 

Agile teams strive to obtain constant feedback. Incorporating these metrics into an automated and repeatable build is a great way to get constant reliable feedback. Based on this feedback, teams can trend their progress over time. For instance, if test coverage declines over a certain period, that's a good indication that the team is becoming too relaxed in practicing test-driven development. If dependency metrics are trending downward, the team may not be emphasizing design integrity and should consider refactoring the areas under question.

 

Clear concise code with fewer dependencies and a robust suite of tests is much more adaptable than the less agile counterpart. Metrics provide valuable information on areas of an application lacking such design integrity. But to accommodate change requires courage to make change and coverage reports are useful in gaining that courage.



About the Author

Kirk is Chief Technology Strategist at QWANtify, where he leads based on his firm belief in the pragmatic use of technology. In addition to his work on large development projects, Kirk shares his experiences through courseware development, teaching, writing, and speaking at seminars and conferences. Kirk has provided training to thousands of software professionals, teaching courses on UML, Java J2EE technology, object-oriented development, component based development, software architecture, and software process.

Error, missing joomlaboard config file!
Comments (1)add feed
joe: ...
Rather a nice article but I fear it is preaching to the choir. Most managers treat agile development as a silver bullet to their schedule problems but when told that they will need to have real automated test suites, input simulators,output verifiers, test data, and all the other machinery that is necessary to make changes rapidly and to know you didnt break anything, they balk at the cost of such infrastructure to get agile development rolling.

I find that the time to generate date and write the code for my unit test wrappers is typically about 50% of my effort for my important infrastructual classes. I usually have stress tests, performance benchmarks, as well as actual functional testing. But I do sleep at night - I have NEVER had to answer a call at night with something wrong in my code

But for ALL the metrics, I think there is negative metric that works best of all:

"If a method doesnt fit on one screen in its entirety it is TOO big and must be factored"
1

September 16, 2007
Write comment


Write the displayed characters


busy
Tags: metrics,
Click to add your tags...,
 
< Prev   Next >






Lost Password?
No account yet? Register

Video News

Agile Poll

How important are CM tools (e.g., Version Control) for Agile projects?
 
 
 
 
 
 
Copyright © 2006 - 2008 CMC Media, Inc. All rights reserved. All marks are trademarks of CMC Media Reproduction in whole or in part in any form or medium without the express written permission of CMC Media, Inc. is prohibited  
 
 CM Yellow Pages | ALM Expo | CM Today | Configuration Management Journal | CM Crossroads