In previous posts, I introduced style checking (linting) using jshint, integration testing using mocha and should, code test coverage using coverage, and vulnerability identification using retire. If you’re starting a new project, I strongly encourage you to integrate these packages into your routine–it will save you plenty in the long run.
I now want to turn to managing software complexity. Defect rates increase superlinearly to code complexity, so managing software complexity is part of a broader strategy of risk reduction. Software complexity is not as cut & dried as style checking and unit testing, and refactoring requires effort and entails its own risks. Automated complexity measures primary benefit is that it help focus your attention on high-risk sections of your code. A thorough treatment of the topic is beyond the scope of a blog post, but you can get started quite easily.
I use two node packages: ‘complexity-report’ and ‘grunt-complexity’. Install them using npm, and then set up your gruntfile:
src: ['app/**/*.js', 'public/modules/*.js'],
exclude: ['app/yyy/**', 'app/tests/**', 'app/services/xxx/**' ],
//jsLintXML: 'report.xml', // create XML JSLint-like report
//checkstyleXML: 'checkstyle.xml', // create checkstyle report
//pmdXML: 'pmd.xml', // create pmd report
errorsOnly: false, // show only maintainability errors
cyclomatic: [10, 20, 50], // or optionally a single value, like 3
halstead: [10, 20, 50], // or optionally a single value, like 8
hideComplexFunctions: true, // only display maintainability. Set to false to get more detailed output.
broadcast: false // broadcast data over event-bus
Note: you cannot use grunt.registerTask(‘complexity’, ‘complexity’). It’s like crossing the beams, if you remember your Ghostbusters analogies.
If you’re just starting your new application–great! If you’re following TDD/BDD, then complexity is just another tool to help you make choices as you refactor–you’re always trying to keep the board green! (But see my caveats at the end). Using the settings above (particularly hideComplexFunctions: true), when you run grunt complex, you’ll get a nice summary of the maintainability metric for each module, with the output ordered from the lowest score to the highest. Maintainability was introduced by Paul Oman and Jack Hagemeister in 1991, and combines 3 different metrics to create an overall availability score. Don’t worry about the details for now. You can control the coloring of the output by changing the value of maintainability: 75 to something else. We’ll discuss this in a bit. Anyway, this is the output format I recommend you use–as long as everything is green, you’re good to go. In my opinion, BDD/TDD helps to prevent a lot of the issues that retrospective complexity analysis was designed to address. I use complexity-report as another safety check during development & deployment, and to help triage code for manual review.
If you’re dealing with existing code, then things are a bit more…err…complex. The first time you run grunt complex, there’s a good chance that you see a lot of red and yellow bars. Don’t panic! To understand where the trouble spots are, we have to dig a little deeper. Set hideComplexFunctions: false, and rerun grunt complex. It still provides the maintainability graphic for each module, but nested underneath each entry are the complexity metrics for every function found in that module. I don’t recommend that you attribute any prescriptive power to these metrics. Clean Code: A Handbook of Agile Software Craftsmanship, by Robert C. Martin, is a great resource for improving the maintainability of your code. The examples are in java, but the principles are universal.
Do not try to reduce complexity purely for the sake of lowering a metric below a specified number. Adjust the thresholds based on your experience and what you (and your peers) see in the code. If you’re OK with a function, then leave it alone. Phil Booth, the author of complexity-report, has accepted my suggestion to allow module and function-level overrides of the global threshold values for the individual metrics, similar to what jshint allows. This is an important feature, because you can then put complexity-report into your standard development & deployment practices, setting thresholds to sensible values in various places. Currently, you have to do complexity reporting out-of-band, or set ridiculously high thresholds that render the capability useless for deployment.