|
| 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 {sidebar id=1} 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.
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.
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.
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.
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. {mos_sb_discuss:10}
Set as favorite
Bookmark
Email this
Hits: 11710 Comments (0)
|
| Last Updated on Saturday, 20 October 2007 08:20 |
Agile Marketplace - Announcements and Special Offers
AgilePalooza - Serious Learning in a Fun Atmosphere
AgilePaloozas are community events sponsored by VersionOne and Agile Journal. These one day conferences provide serious learning in a fun atmosphere. Two tracks are included: Learning Agility and Advancing Agility. Speakers include internationally recognized agile coaches and trainers. The next seminar will be held August 27th in Dallas, TX – use discount code agilejournal and save $20!
Register Here
CollabNet Subversion Edge Improves Governance, Security, Administration
Quickly configure SVN, Apache, and ViewVC with one certified stack, fronted by a powerful UI.
Try our beta version and let us know what you think!
Virtual Conference: Agile ALM for Distributed Development
Learn how Agile technologies can create efficiencies for your business, hear from industry experts, and network with your peers. CollabNet and their technology business partners present two tracks of valuable information—business and technical. This environment will be available until July 20, 2010 at 3:30pm PDT, so please come back as often as you like to access the resources and presentations.
Register Today!



