Home | About Us | Stelligent  

TestEarly Weblog

Code Complexity

Code Complexity and Publications and Agile and /Brothers12 May 2008 02:55 pm

I don’t always agree with Mr. Yegge, but this is a great presentation/script on Dynamic Languages with some very interesting ideas and discussion topics.

One point that he never actually got to, but one that I think is worth more discussion - how do you maintain a million-line codebase w/out static types?

My answer is that with a good dynamic language, you have some seriously elegant features and patterns that allow you to keep the size of your codebase small.  Essentially, you don’t maintain a million-line codebase - because it doesn’t take a million lines to write your application.

Which fits in with some of the agile concepts as well - if you are building a project in an agile style, the constant refactoring and removal of technical debt will keep the application smaller and lighter.  The unit tests ensure a clean level of separation of concerns, the DRY and YAGNI principles reduce bloat.

Code Complexity and Code Metrics and /Gurses and Tutorial and Agile11 Jun 2007 04:05 pm

Who cares about software metrics? As a Java developer, I do. Measuring certain aspects of my code lets me quantify my schedule, work effort, product size, project status, and code quality. Oh, and then my project manager cares too. If I don’t measure my current status (number of classes, dependencies to other modules, complexity, code coverage, defects as well as code smells - yes, we all know when we have code smells) and use the data to improve my code and my future work estimates, those estimates will just be guesses. And my project manager expects a little better than guesses.

Metrics is an Eclipse plugin that helps me take a holistic view of various aspects of my code. I use it on a daily basis to see how my project grows and if there are any deviations from the reference architecture. The plugin lets me take a closer look at:

  • Number of Classes
  • Method Lines of Code
  • Number of Methods
  • Nested Block Depth - Is nice, I like!
  • Depth of Inheritance Tree - nobody wants to traverse a 16-node deep tree trying to find what they need. 1-5 maybe, but 16….too many levels…
  • Afferent Coupling, Efferent Coupling - yea, whatever… we saw this here…same story…
  • Cyclomatic Complexity - If my code is simpler than Paul’s does that make me look “simpler” too?
  • …and more…

Quick Tutorial

  1. Download and start Eclipse 3.2
  2. Navigate to Help | Software Updates | Find and Install
  3. Select Search for new features to install
  4. Add a new remote site Metrics and point it to http://metrics.sourceforge.net/update
  5. Finish the wizard and restart Eclipse
  6. Download the sample solarsystem project from http://www.testearly.com/resources/agile/solarsystem.zip. Open the project in Eclipse. Note that this sample requires Java SDK 6.0.
  7. Build the project
  8. In the Package Explorer, right-click the solarsystem project and open the Properties dialog. Locate Metrics and check Enable Metrics
  9. Rebuild solarsystem
  10. Navigate to Window | Show View | Metrics | Metrics View
  11. You are looking at a table of various metrics covering the solarsystem project
  12. metrics001
  13. Feel free to dig down on each metric as it goes from source folder to package to class down to the method
  14. Notice all metrics for this simple project are in blue and there are no exclamation marks in the source code indicating any problems. Let’s change that and make life difficult for ourselves.
  15. Open the preferences page for Metrics and check Enable out-of-range warnings. Drill one level down to Safe Ranges, locate Nested Block Depth and enter 5 as the Max value. Similarly, enter 10 as the Max value for McCabe Cyclomatic Complexity
  16. In a Java Perspective open PlanetUtil.java and add the following method:
    public void thisIsAComplexMethod() {
            if (true) {
                    if (false) {
                            if (true) {
                                    if (false) {
                                            if (true) {
                                                    System.out.println("This is a deeply nested if sequence.");
                                            }
                                    } else {
    
                                    }
                            }
                    }
            }
    
            if (true) {
                    if (false) {
                            if (true) {
                                    if (false) {
                                            if (true) {
                                                    System.out.println("This is a deeply nested if sequence.");
                                            }
                                    } else {
    
                                    }
                            }
                    }
            }
    }
    
  17. Rebuild solarsystem and return to the Metrics View. You now see two red lines in the results table and an exclamation mark in the code editor. Clearly the new code failed the cyclomatic complexity and nested statements rules of Metrics and therefore was marked red. The cyclomatic complexity of the method is 11, while the if statements nest for 6 levels. Both values are out of the acceptable ranges we defined earlier.
  18. metrics002
  19. Locate the Dependency Graph View icon in the Metrics View. It can be found on the upper right corner and it is denoted by four dots connected in a square. Click the icon to open the graphical dependency view of Metrics.
  20. metrics003
  21. The packages in the project are denoted by boxes in different colors. Blue means normal relationship; red means cycles and orange denotes the selected node. There is also a circle in the middle, the tangle center, which displays the number of packages in a particular tangle and the longest walk. In general, the longer the walk the more layers are involved and therefore there may be refactoring opportunities. Similarly, the existence of red nodes and tangles indicates cycles in the package dependencies and that’s also something that may be avoided with a little care. Finally, every node and the tangle center provide right-click menus to drill-down for more details.
  22. The reason for this tangle (cycle) is the JUnit test PlanetUtilTest, which is located in com.solarsystem.jupiter. Let’s do something about this.
  23. In the Package Explorer, locate PlanetUtilTest, right-click and select Refactor | Move. Move the test into a new package com.solarsystem.test.jupiter.
  24. Rebuild the project. Open the Metrics View. Click the graph icon. Everything should be blue. There should be no tangles. No tangle centers. Blue is better than red. Life is good…
  25. metrics004
  26. Enjoy.
Code Coverage and Code Complexity and Code Metrics and /Owens and Vidcast11 Jun 2007 03:30 pm

Paul Duvall and Levent Gurses have posted a vidcast (video podcast) discussing Levent’s presentation at the Better Software Conference next week in Vegas. Levent’s talk covers Eclipse and how a carefully selected set of plugins (JDepend4Eclipse, Checkstyle, Coverlipse, PMD, and Metrics) can help you create better quality code.

If you can’t check out his live presentation in Vegas, simply have a watch below and let us know what you think. Enjoy the show!


Code Complexity and Code Metrics and /Gurses and Tutorial and Agile07 Jun 2007 02:42 pm

What does dependency analysis mean to you? To me it mostly translates to the fact that Common Services (CS) packages should not depend on any of their clients. Does that happen in practice? More than enough to make the case for dependency analysis.

Just like the other tools in this series JDepend can be used in Eclipse or as part of a Continuous Integration cycle. I like to see what’s going on in terms of dependencies and other architectural metrics all the time, so I use both. JDepend calculates:

  • Total number of classes and interfaces - number of concrete and abstract classes (and interfaces) in the package is an indicator of the extensibility of the package. How many?
  • Afferent couplings (Ca) - number of other packages that depend upon classes within the package is an indicator of the package’s responsibility. Who depends on me?
  • Efferent couplings (Ce) - number of other packages that the classes in the package depend upon is an indicator of the package’s independence. Who do I depend on?
  • Abstractness (A) - the ratio of the number of abstract classes (and interfaces) in the analyzed package to the total number of classes in the analyzed package. The range for this metric is 0 to 1, with A=0 indicating a completely concrete package and A=1 indicating a completely abstract package. How abstract I am? If I change today, how many of my dependents have to change with me?
  • Instability (I) - the ratio of efferent coupling (Ce) to total coupling (Ce + Ca) such that I = Ce / (Ce + Ca). This metric is an indicator of the package’s resilience to change. The range for this metric is 0 to 1, with I=0 indicating a completely stable package and I=1 indicating a completely instable package. How stable am I? Do I change often?
  • Distance to Main Sequence (D) - the perpendicular distance of a package from the idealized line A + I = 1. This metric is an indicator of the package’s balance between abstractness and stability. A package squarely on the main sequence is optimally balanced with respect to its abstractness and stability. Ideal packages are either completely abstract and stable (x=0, y=1) or completely concrete and instable (x=1, y=0). The range for this metric is 0 to 1, with D=0 indicating a package that is coincident with the main sequence and D=1 indicating a package that is as far from the main sequence as possible.
  • Package dependency cycles - dependency cycles are reported along with the hierarchical paths of packages participating in package dependency cycles. If I depend on you and you depend on me, we have to work hard to adapt to each other’s changes, don’t we?

The JDepend4Eclipse plugin is an implementation of the JDepend tool. It follows the same principles and provides visual analysis inside Eclipse. One thing to note is that the plugin works on compiled java bytecode, so its important for your project to build. Related to this is the fact that if you’re following the same package/folder structure for your unit tests as your source code it might be a good idea to compile them into a separate output folder, separate all unit tests into another Eclipse project or just change the unit test package names (by appending the name test for example ), like I show below.

Quick Tutorial

  1. Download and start Eclipse 3.2
  2. Navigate to Help | Software Updates | Find and Install
  3. Select Search for new features to install
  4. Add a new remote site JDepend4Eclipse and point it to http://andrei.gmxhome.de/eclipse/
  5. Finish the wizard and restart Eclipse
  6. Download the sample solarsystem project from http://www.testearly.com/resources/agile/solarsystem.zip. Open the project in Eclipse. Note that this sample requires Java SDK 6.0.
  7. Build the project
  8. In the Package Explorer, right-click the source folder src/main/java and select Run JDepend analysis
  9. In the newly opened JDepend perspective click on com.solarsystem.jupiter. Notice how the dependencies, packages with cycles, efferent and afferent dependencies and the metrics views get populated
  10. Notice in the Dependencies view that the com.solarsystem.jupiter package has 3 Concrete Classes (CC), 1 Abstract Class (AC), 4 Afferent Dependencies (Ca) and 7 Efferent Dependencies.(Ce) Also the Abstractness (A) is 0.25 while the Instability (I) is 0.63. Finally, the Distance (D) to the main sequence is 0.11 which is denoted by a green dot in the Metrics view. Also in this view, notice that the package is marked as having a cycle. This is because the unit test PlanetUtilTest resides in the same package. JDepend works on compiled java code and therefore it picks up everything in the target/classes folder, including any unit tests. There is plenty of theory behind the various metrics gathered by JDepend and they are beyond the scope of this quick tutorial. It suffices to say that for any given package the shorter the Distance (D) to the main sequence, the better. In the above case the com.solarsystem.jupiter is used by four other packages, thus Ca=4. The package depends on 7 other packages, including junit.framework, therefore the Ce=7.
  11. Generally speaking cycles between packages indicate complex interdependencies and should be avoided. In this step, let’s eliminate the cycles in the project.
  12. In the Package Explorer locate src/test/java anc right-click on PlanetUtilTest
  13. Select Refactor | Move…
  14. Create a new package com.solarsystem.test.jupiter and click OK
  15. JDepend4Eclipse
  16. Return back to src/main/java. Right-click and select Run JDepend analysis. All cycles are gone. While we eliminated the cycles, notice also that the distance jumped to 33 from 11 and is now denoted by a black dot.
  17. In Java Browsing perspective select File | New | Interface. Enter a name, IBogus, select the package com.solarsystem.jupiter and click OK. This is just an empty interface.
    package com.solarsystem.jupiter;
    
    public interface IBogus {
    
    }
    
  18. Build the project
  19. In Package Explorer, right-click the source folder src/main/java and select Run JDepend analysis
  20. Notice the Distance is now 16 and the dot turned green. But what did the IBogus interface add to the package? Nothing. It just fooled the tool. This is where special care must be taken. Sometimes emty or unused abstract classes can negatively impact the static analysis and make one belive that the package is in a better state than it actually is. My recommendation is to use other tools that detect unused classes and imports and get the whole system cleaned-up before each dependency analysis.
  21. Enjoy.
Code Complexity and Code Metrics and /Gurses and Tutorial and Agile06 Jun 2007 04:17 pm

If you use Eclipse for day to day Java development take a deep breath and relax. You are neither the first nor the only one. In fact, you are part of a growing community of Java developers in the world. SD Times recently reported that two-thirds of the enterprise software developers—66.3 percent—use Eclipse. This adoption rate is a success for an open source project, albeit supported by a giant like IBM.

The agile developers know that at the end it’s often not the tool that makes the difference. It’s the fundamental engineering practices. When the discussion goes beyond the tools it suddenly opens so many possibilities that even the most staunch tools proponents/opponents can sit down and unite in a common strategy. Let’s take coding standards for example. We all know that there are tons of tools for static code analysis and early defect detection. The Agile teams have choices in this matter. They can implement a set of static code analysis as part of their Continuous Integration (CI) cycle and get great reports the instant a potential bug gets checked-in. They can also encourage individual developers to use Eclipse plugins for code analysis and perform the same checks before they commit the code.

Two major plugins for code analysis in Eclipse are PMD and Checkstyle. Of course, Eclipse itself is also catching-up and it is constantly increasing its own set of code style checks. A look at Window | Preferences | Java | Compiler | Errors/Warnings reveals that the number and depth of the built-in code checks is fairly substantial.

Nevertheless, in this blog entry I would like to show you a quick example of how to setup and use Checkstyle for Eclipse .

Despite the fact that I use PMD and Checkstyle (and others for that matter, like Findbugs) in my daily projects, I favor Checkstyle for the following reasons:

  • The integration of the tool to the environment seems to be tighter
  • The rule customization and management in Checkstyle is way easier than PMD. I like!
  • Checkstyle provides a few nifty features such as a pie chart depicting the distribution of problems
  • Checkstyle comes with a richer set of predefined rules
  • Better website documentation and easier learning curve

All and all Checkstyle makes my life easier, and especially after configuring a set of rules that make the sense for the project environment, it’s a workhorse! An added benefit of the custom rulesets is that they can be checked into the code repository, shared among the development team and reused in the CI static analysis phase. The latter can be achieved through integrating tools such as CruiseControl, Maven and Ant with the respective Checkstyle plugins. This brings a unified coding standard to the entire development team.

Quick Tutorial

  1. Download and start Eclipse 3.2
  2. Navigate to Help | Software Updates | Find and Install
  3. Select Search for new features to install
  4. Add a new remote site Checkstyle and point it to http://eclipse-cs.sourceforge.net/update
  5. Finish the wizard and restart Eclipse
  6. Create a new Java project, checkmeup
  7. Add the following Java class:
    public class CheckMeUp {
       public void thisMethodShouldHaveAShorterAndCoolerName(String WHYALLCAPS, int g) {
          if (true) {
          }
          else {
          }
    
          while (true) {
             String x = "The white fox jumped over the red rabbit, which in turn jumped over my yellow submarine.";
             String y = x + "...";
          }
       }
    }
    
  8. Build the project
  9. In the Package explorer right-click on checkmeup and select Properties | Checkstyle
  10. Check Checkstyle active for this project and select Sun Checks (Eclipse) - (Global) as the ruleset
  11. Navigate to Window | Show view | Other…
  12. Select Checkstyle | Checkstyle violations
  13. Notice the 27 violations in this simple Java class
  14. Now, let’s play with the rules. Assume that you do not like the Line contains a tab character rule. The sample class has 12 of it. Navigate to Window | Preferences | Checkstyle and Click on New…
  15. Select Internal Configuration, enter a name and description. Click Import… and navigate to ECLIPSE_HOME/plugins/com.atlassw.tools.eclipse.checkstyle_x.x.x and select sun_checks_eclipse.xml. This will import all Sun Eclipse rules for the new custom ruleset. Also note that Checkstyle will create a new custom ruleset xml file under WORKSPACE/.metadata/.plugins/com.atlassw.tools.eclipse.checkstyle/internal_config__xxxxxxxxxxx.xml. This file can be added into the code repository and shared among the development team. Click OK.
  16. Double click the new ruleset. Click Whitespace and uncheck the Tab Character rule
  17. Back in the project properties, select the new custom rule as your configuration
  18. Click OK. The tab warnings are gone.
  19. In the Checkstyle violations view click the chart icon in the upper right corner. This will open the pie chart view shown below

    checksytle

  20. Enjoy.
Developer Testing and Code Complexity and Code Metrics and /Glover19 Jan 2007 03:21 pm

[Intro]

Sir Mix-A-Lot

Oh my goodness. Paul, look at that code.
It’s so big. *scoff* It looks like
one of those Fortran guys wrote it
But, y’know, who understands those Fortran guys? *scoff*
They only write that stuff because
it looks like job security, ‘kay?
I mean, that method, it’s just so big. *scoff*
I can’t believe it’s so complex, it’s just out there
I mean, it’s gross. Look!
It’s just so…legacy

[Sir Refactor-a-Lot]

I like refactoring and I can not lie
You other coders can’t deny
That when a method uses too much space
And its complexity is staring you in the face
You get stung

Sir Mix-A-Lot

Wanna extract that stuff
Cuz you know testin’s gonna be tough
Deep in the logic it’s glaring
I’m hooked and I can’t stop staring

Oh, man I wanna get fixn’
And make that logic smaller
My metrics tried to warn me
But that mess its got
Exploited that code’s frailty

Ooh, extract method isolates that logic
and makes testin’ easy
And refactoring is so groovy

Sir Mix-A-Lot

Many thanks to Sir Mix-A-Lot for such an amusing song (and Sir Trevor Raps-A-Lot for additional rhymes)– don’t forget to read the original lyrics.

Developer Testing and Code Coverage and Code Complexity and /Owens12 Jan 2007 04:06 pm

Paul Duvall published an article, Improving code with Eclipse plugins, the fifth installment of his “Automation for the people” series by IBM developerWorks, where he provides examples of installing, configuring, and using static analysis Eclipse plugins focused on helping developers in key areas such as:

  • Coding standards
  • Code duplication
  • Code coverage
  • Dependency analysis
  • Complexity monitoring

These code analysis areas can be uncovered with five helpful plugins:

Regardless if you chose one of the profiled tools or other Eclipse plugins, incorporating them into your work environment will contribute to real-time visual feedback on the state of the quality of your code so that you may prevent problems early in the development life cycle.

Code Complexity and Code Metrics and /Glover31 Dec 2006 02:25 pm

Code conditionals like if and else clauses trigger complexity metrics like Cyclomatic complexity. The more conditions in a method, the higher the Cyclomatic complexity value– and the higher the value, the greater chance there’s a defect lurking somewhere the code (either presently or about to be coded). Granted, there are degrees of complexity associated with conditions too– the deeper the depth a series of conditionals reaches, the more complex it becomes compared to a linear sequential series of if clauses.

There are also a host of false positives within code that’ll flag Cyclomatic complexity. Defensive coding constructs, for example, that check various input values to ensure they meet acceptable conditions will trip Cyclomatic complexity and depending on the number of checks, yield high values. For example, the following simple method, which is part of a class that implements an Ant task, yields a Cyclomatic complexity value of 4:

private void validate() throws BuildException{
 if(this.report == null){
  throw new BuildException("Report Element was null");
 }
 if(this.sourceDependencies == null){
  throw new BuildException("no source dependencies configured");
 }
 if(this.targetDependencies == null){
  throw new BuildException("no target dependencies configured");
 }
}

While this example is relatively un-complicated, it’s by no means unique– the defensive coding constructs employed here can be found across many a code base– all doing essentially the same thing. In the Aspect Oriented Programming (AOP) world, this is known as a crosscutting concern. These constructs span horizontally across a code base, which usually, using OO methodologies, are captured vertically using hierarchies.

Just like the trite logging examples that permeate AOP articles, using AOP constructs, you can effectively remove repetitive defensive coding constructs and replace them with advices defined in aspects. For example, OVal is a validation framework that makes use of AOP to facilitate the use of simple constraints, which can be checked before or after a desired method is invoked.

Taking the code above, you can retro-fit class members (like report and sourceDependencies, for example) with @NotNull annotations and make use of OVal’s @PreValidateThis annotation to automatically validate their values– thus you can effectively remove the validate method.

For example, using OVal, the class itself must be annotated with the @Guarded annotation. Then, the three class members from above are annotated like so:

@Guarded
public class DependencyFinderTask extends Task {
 @NotNull
 private Report report;
 @NotNull
 private SourceDependencies sourceDependencies;
 @NotNull
 private TargetDependencies targetDependencies;
 // class body below...
}

Next, the execute method (which previously called the validate method from above) needs to be augmented with @PreValidateThis annotation. This means that when execute is invoked, an advice will trigger OVal’s code to ensure that all three members are not null. Thus, the execute method is decorated as so:

@PreValidateThis
public void execute() throws BuildException {
 //method body with validate() removed....
}

Thus, using AOP constructs (as implemented by OVal), I’ve eliminated three conditionals (indeed, an entire method) and placed the burden on OVal. With the addition of annotations to Java and improved AOP tools, making use of AOP isn’t that much of a hurtle. There are performance issues to consider; however, in controlled environments, you can easily turn the aspects off.

Code Complexity and Code Metrics and /Glover19 Dec 2006 12:33 am

There are few metrics that serve as accurate of a touchstone for complexity than Cyclomatic complexity; yet, this metric alone can paint a somewhat misleading picture of true complexity because it doesn’t account for conditional depth.

For example, the following C++ code snippet yields a Cyclomatic complexity value of 5:

if(HighAmountAcct != NULL)
{
  iMgrMemory->FreeUp(HighAmountAcct->GetHighPriorityMssgsResult);
}
if(AQuedAcct != NULL)
{
  iMgrMemory->FreeUp(AQuedAcct->GetQueuedMessagesResult);
}
if(NotifyAcct != NULL)
{
  AtlCleanupValueEx(&NotifyAcct->NotifyAcct, iMgrMemory);
}
if(ReplyAcct != NULL)
{
  iMgrMemory->FreeUp(ReplyAcct->ReplyQueuedMessagesResult);
}
if(RegisterAcct != NULL)
{
  AtlCleanupValueEx(&RegisterAcct->RegisterAcct, iMgrMemory);
}

Regardless of the domain or the language involved here, it’s hard to argue that this snippet of code is terribly complex. Each condition is logically separated– no one condition depends on another. Yet, compare the code above with the code below, which also yields a Cyclomatic complexity of 5:

if(ACCT_FAIL(iSettings->EnumValues(&iEnumValues)))
{
 iSettings->Remove();
}
else
{
 CHAR iActHldr[MAX_AMOUNT];
 if(ACCT_SUCCEED(iEnumValues->Get(iActHldr)))
 {
  if(ccint(iActHldr, iName, len(iName)) == 0)
  {
   sr = iSettings->Delete(iName);
   if (!ACCT_FAIL(sr))
   {
    sr = ACCT_FAIL(iSettingsMgr->Put(iSettings));
   }
  }
 }
}

Again, ignoring the domain tackled here or the perceived complexity of the language, which code snippet do you find more complex? It’s clear that the second example snippet involves conditional depth, which means one must comprehend previous state in order to ascertain the context of an individual line of code– as you get deeper into a series of conditions, the management overhead increases as well.

While Cyclomatic complexity acts as an excellent barometer of complexity, as a stand alone metric, it’s one-dimensional– it only gives a partial picture. The addition of depth gives complexity a second dimension, which yields a far more explicit view. Keep in mind though, both method snippets are testing challenges– it’s just that the second snippet is arguably harder to test.

Developer Testing and Code Coverage and Code Complexity and /Duvall14 Dec 2006 11:59 pm

Despite our best intentions, many of us don’t always get to work on the latest and greatest language or technology. In some cases, we may need to make our way around a code base that has been written by another team. Given that the average software system lives for 11 years, it’s probably safe to say that there are, in fact, many such cases where we need to quickly assess a new code base to find its flaws (i.e. risks) and determine which portions of the code may be difficult to maintain and extend and which should be easier. If you’re looking at a code base that was developed by another team or even code you just wrote yesterday, once it’s written, it’s now code that you must maintain.

If you need to analyze a code base and determine which portions of the code are more risky than others, what would you do? For me, I use tools that help me quickly assess the code quality. For instance, I’d like to know something I call the “big five” measures:

  • Code complexity
  • Amount of duplication
  • The resilience of the architecture
  • Whether coding standards are being applied
  • Amount of test code coverage

As I mentioned, I like to use tools to help me assess these measures. However, there are sometimes concerns when people consider using code metrics on their projects:

1) Don’t know which tools to use
2) Can be easily abused
3) Not sure what the data means

The table below links a measure to a tool (.NET and Java), the code smell associated with this measure, the refactoring for the smell and possible patterns that may be applied to refactor the code smell.

Measure Smell Tools Refactoring Pattern
Cyclomatic Complexity Conditional Complexity IDE, CCMetrics, Source Monitor, JavaNCSS, Eclipse Metrics Extract Method, Extract Class, Replace Conditional with Polymorphism Abstract Factory, Strategy
Number of Lines in Method Long Method IDE, PMD, Eclipse Metrics, CheckStyle, Source Monitor, FxCop Extract Method Strategy
Depth of Inheritance (DIT) IDE, PMD, Source Monitor Replace Inheritance with Delegation, Pull Up/Push Down Method Delegation

This is not a detailed list, but should give you some ideas. I plan to add to this list in the future. Do you have additional measures|tools|refactorings|patterns? Please share them with us.

Next time you need to target risky parts of your code base, use these tools to obtain metrics so that you can monitor and improve your code quality (through refactoring and patterns) throughout your development lifecycle.

Next Page »