Cyclomatic complexity is one way to measure how complicated code is, it measures how complicated the structure of the code is and by extension how likely it may be to attract bugs or additional cost in maintenance/readability.
The calculated value for the cyclomatic complexity indicates how many different paths through the code there are. This means that lower numbers are better than higher numbers.
Clean code is likely to have lower cyclomatic complexity that dirty code. High cyclomatic complexity increases the risk of the presence of defects in the code due to increased difficulty in its testability, readability, and maintainability.
Calculating Cyclomatic Complexity in Visual Studio
To calculate the cyclomatic complexity, go to the the Analyze menu and choose Calculate Code Metrics for Solution (or for a specific project within the solution).
This will open the Code Metrics Results window as seen in the following screenshot.
Take the following code (in a project called ClassLibrary1):
public class Class1
If we expand the results in the Code Metrics Window we can drill down into classes and right down to individual methods as in the following screenshot.
So the project or release or iteration is done. We’ve “finished”. The customer and users are (hopefully) reasonably satisfied, and we say that we’ve delivered the software.
There’s a second hidden deliverable that we don’t usually think about and that’s the malleability of the thing we’ve just delivered. How easy it will be to modify the software in the future.
We can think of it as the “potential cost of future change”.
Even though we cannot measure productivity and estimating this future cost is likely to be either impossible, pointless, or both; it still may be a useful concept.
Once we start to treat the potential cost of future change as a deliverable in its own right, we can have important conversations with the team/customer/user. We can trade off the potential cost for future change against getting the next thing released as soon as possible.
It can be hard for those new to development or those wanting to move upwards from a junior role to mentally collate some of the ideas that constitute a senior/experienced outlook. The list below attempts to piece together some of the ways to improve both the management and development of software. It is by no means comprehensive but hopefully will provide a helpful starting point...
Do Realize That We Are The Business
- There can sometimes be an 'Us' and 'Them' mentality when it comes to our relationship to 'The business'. We should accept (and respectfully challenge where appropriate) the view of 'The Business' when it comes to using IT to meet those business goals.
- We should seek to explain the basics of software development, just as we should seek to understand the 'The Business' more.
- We should work with 'The Business' to come up with new innovate uses of IT, rather that wait for 'The Business' to come to us with initiatives.
- NOTE: I don't think there is anything intrinsically wrong with using the term 'the business' as long as it's not divisive.
Do Use Metrics (but don't rely on a single one)
Managing and tracking a project through the use of a single (often poor) metric such as Lines of Code (LOC) does not give an accurate view, e.g. do you count generated code in LOC reports?, or if you refactor then LOC may go up or down when the functionality remains the same.
Some useful metrics that can be used together:
- Number of tests
- LOC for solution (for pure volume)
- LOC for test code
- Number of bug / number of bugs per iteration/sprint
- Average time to close bugs
- Number of formal code reviews performed
- Number of use cases completed
- Number of/frequency of demos given to users/client
- Average LOC in methods
- Number of classes/methods
Do Dependency Injection
DI is a great way to decouple the parts of the solution. If you start out with DI in mind it may also have a beneficial effect on the overall design of the system (it can force you to think in abstract terms such as interfaces). Learn a tool that offers DI (such as Windsor) or for simple scenarios create you own DI framework.
Do Test Driven Development
- Remember that TDD should be though of more as a design tool that happens to produce the hugely valuable side-effect of 'unit' tests.
- Once you get into the rhythm of TDD it is a great way to be (and feel) productive: great satisfaction from all your green lights!
- Don't neglect the overall big-picture architecture.
Do Regular Automated Builds
For projects with more than a couple of developers, it can be beneficial to set up an automated build (using CruiseControl, Team Foundation Server, a simple scheduled batch file, etc.). At a minimum schedule a nightly build.
Consider a Continuous Integration (CI) build with larger teams, remote workers, heavy volumes of check-ins, or anytime when ASAP failure feedback is required. A CI build can still be useful even with smaller teams - it's better to know sooner when we break the build.
Do Iterative/Incremental/Evolutionary Development
Whether you're formally 'Agile' (XP, Scrum, UP, etc.) or not, taking an evolutionry approach to software development can still yield benefits. This is especially true if you demo the product to users/clients after each evolution/iteration/sprint/use case/feature.
Much as we would like to think the 'Waterfall State of Mind' is dead there can still be a lot of resistance in business to idea of Agile development, regular demos could be one way to start to introduce the 'feeling' of agile.
Do A Daily Stand-Up Meeting
But do it properly: I have seen stand-ups which have gone on for over half an hour - the whole purpose of a stand-up is to encourage people to keep it short!. The team lead (or devs take it in turns if there is no 'lead' role) should 'chair' the meeting; each person simply needs to state: (1) what they have achieved\worked on since the last stand-up; (2) what they intend to achieve\work on until the next stand-up' and (3) anything that is 'blocking' their progress. If there are any problems then the relevant people should meet after the stand-up to discuss further.
Devs shouldn't see it as micro-management or a way of management applying extra pressure (nor should management), rather as a way of reporting problems earlier-on. This also puts an onus on the 'chair' and other devs to support each other to resolve problems.
Do Code Reviews
Formal code reviews can be time-consiming and as the reviewer is another Dev there can be a view that it is wasted time. There is a balance to be had; if every line of code has to be reviewed then of course a large amount of dev time will be otherwise-used. The main benefit is that code reviews can find bugs that automated unit tests cannot - again if these bugs are found earlier, they cost less to fix. One method is to target code reviews at pieces of code that are deemed to be a higher-risk (e.g. integration with 3rd parties components) or code that, if wrong, would have serious consequences (e.g. life-support system or financial transfers).
This seems like an obvious statement but sometimes it is easy to get bogged down on a particular element. in the desire to implement the most elegant and optimized code (which is a good and natural state of mind for a developer) we can loose sight of the bigger goal which is: to deliver value to the business/client.
Do Risk-Oreinted Prioritization
Code/test higher risk areas as early on in the project as possible and leave the 'easy' parts until later. This helps to prevent nasty surprises when you are nearing your deadline and you have less room for maneuver.
Higher risk can come from:
- 3rd party suppliers & components;
- previously unused hardware/tech/libraries/APIs/versions;
- more intrinsically complex algorithms; etc.
High Cohesion & Low Coupling
Do Buy Over Build
When low risk/high benefit/safe, consider using 3rd party components rather than reinventing the wheel, but bear in mind: vendor support/warrenty; license cost present & future; downloading trial version to evaluate. This ties in with the point on delivering value.
Do Test Driven Bug-Fixing (TDB??) (or "Find A Bug, Write A Test")
If you have been assigned or found a bug, the first thing you should do once you have isolated/identified the cause is to write a (unit/integration) test to reproduce it. Once you can reproduce it you then fix it. The test then becomes a valuable part of the regression testing performed on every test run.
Whether you keep bug fixed test in a separate assembly/project is a matter of choice, but if you using formal bug-tracking software it's a good idea to comment the test with the bug reference number.