Picture Your Code With Metrics
Learn how to use and interpret the new code metrics feature in Team System.
- By Jeff Levinson
Visual Studio 2008 introduces new features in each edition. In the coming months, this column will examine these features and demonstrate how you can benefit from them in your development process. This column focuses on the addition of the code metrics feature in the Team Edition for Software Developers.
What are code metrics? They provide you with indications regarding various aspects of your code. They do not provide "good" or "bad" indications. Instead, the value associated with these numbers and their meaning is different for different types of projects. Once you learn how to interpret different metrics, you can restructure your code to make it more testable, maintainable and flexible. (However, remember that you shouldn't live and die by these metrics.)
Here are the different types of new code metrics:
- Maintainability Index: A value between zero and 100 indicates how maintainable your code is. Greater numbers are better (this is the only metric where this rule applies). The maintainability index is sometimes thought of as the "magic number," meaning it is a calculation that contains aggregate information from the cyclomatic complexity, lines of code and computational complexity. For more information, visit http://www.sei.cmu.edu/str/descriptions/mitmpm.html.
- Cyclomatic Complexity: A number that indicates the amount of paths through your code. It is a measure of if, loop and switch statements + 1 on which your code relies. For more information, visit http://en.wikipedia.org/wiki/Cyclomatic_complexity.
- Depth of Inheritance: A number that notes the depth of your inheritance chain. Greater numbers are more difficult to maintain and indicate a greater likelihood of a change breaking multiple classes (for example, having a cascading effect).
- Class Coupling: A number that shows the dependency graph for a particular class. It indicates how many other classes relate to this class (either the classes it uses or the classes that use it). Greater numbers mean it is more difficult to maintain.
- Lines of Code: Everyone's favorite measurement — the more lines of code you have, the more difficult to test, maintain and debug.
You can use numerous additional measurements to determine code metrics (although, these measurements are not available in Team System) . For example, NDepends (for a fee product) provides a large number of code metrics. The problem with using this type of tool is that you may get buried in code metrics. Many metrics are academic and they are not usually used by developers. Microsoft picked the main indicators that would benefit you without drowning you in numbers.
Now that you understand the code metrics, it's time to look at some examples and learn how code metrics can help you (and what to do, and not do, when you see them).
Listing 1 shows some basic code for a LineItem class. Figure 1 shows the code metrics results based on the code in Listing 1. As you can see, this code is easily maintained. It has little complexity (one indicates only one path through a given method). It does not have an inheritance chain (one indicates the relationship between the LineItem and System.Object; it will always be the minimum value displayed for a class). It has little class coupling (one indicates the relationship with the DateTime class). And, it has a small number of code lines.
What happens if you add some business rules to the set property? Listing 2 shows these changes to the code, and Figure 2 shows the new code metrics results. Now you may notice some issues with the code. The maintainability index of the StartTime set block has taken a major dive. The cyclomatic complexity and class coupling are greater.
In general, these types of changes to the code metrics results are a bad sign. You may find it ridiculous to worry about them in this small example, but you should pay attention to the trend that caused the changes and look at what's behind them. A simple comparison between the Figure 1 and Figure 2 indicates to you that adding business rules to the set property makes the code more difficult to maintain. Writing test cases for it is also more complex. Instead of writing two test cases (one for the get and one for the set), you have to write four test cases (get, past, future and valid). As a general rule of thumb, you need to write at least one test case for each point of cyclomatic complexity (this rule doesn't apply to the constructor because it is the default constructor and does not need testing).
How do you deal with these potential issues? In object-oriented programming (OOP), you are taught to encapsulate everything, use inheritance freely and aggregate classes as needed. However, you can see from the code metrics results that following these "rules" can lead to problems. Here are three best practices to help you avoid these issues:
- Limit the depth of the inheritance chain to five levels or less. If you have to go deeper, consider flattening the chain somewhat and combining classes. You may find this counter-intuitive to OOP, but it will save you hours of work when you find a bug.
- If you have too many lines of code, consider going back and refactoring your code to reduce the number of lines. You may have to create a base class and move common code into it, or simply combine similar operations into a single method in order to better reuse your code. In addition, generics provide a simple method for reducing the number of code lines and increasing reusability.
- Cyclomatic complexity is the most difficult number to reduce, as it relates to the business rules. However, you can reduce this value by restructuring how your code evaluates the business rules. For example, instead of a series of if statements, you can create a rule evaluation infrastructure and use attributes to contain rules. This would eliminate many paths through your code and reduce the number of code lines.
Don't let the numbers dictate everything! Some code is inherently more complex (for example, if you are writing embedded systems, operating systems or servers). For each project, you should create a chart showing what is acceptable for each of the metrics (for example, define your ranges: your application may note that its goal is a complexity of less than 20 and another app may note that its goal is less than 7). Finally, use numbers that make sense to you, but always try to use the numbers that will improve your code.
Jeff Levinson is the Application Lifecycle Management practice lead for Northwest Cadence specializing in process and methodology. He is the co-author of "Pro Visual Studio Team System with Database Professionals" (Apress 2007), the author of "Building Client/Server Applications with VB.NET" (Apress 2003) and has written numerous articles. He is an MCAD, MCSD, MCDBA, MCT and is a Team System MVP. He has a Masters in Software Engineering from Carnegie Mellon University and is a former Solutions Design and Integration Architect for The Boeing Company. You can reach him at Jeff.Levinson@nwcadence.com.