11

Do iterative methods such as are commonly found in modern languages such as C#, JavaScript, and (hopefully) in Java 8 reduce cyclomatic complexity's impact on understandability and supportability of code?

For example in C# we might have the following code:

List<String> filteredList = new List<String>();

foreach (String s in originalList){
   if (matches(s)){
      filteredList.add(s);
   }
}

This has a simple cyclomatic complexity of 2.

We could easily re-write this as:

List<String> filteredList = originalList.where(s => matches(s));

Which has a simple cyclomatic complexity of 0.

Does this actually result in more supportable code? Is there any actual research on this topic?

4
  • 2
    Your title asks about cyclomatic complexity reduction, but your text assumes CC reduction and asks about maintainability. This has the potential to be a valuable question, can you edit the title to be more accurate? Feb 25, 2013 at 15:29
  • @KilianFoth Is that better?
    – C. Ross
    Feb 25, 2013 at 15:36
  • I'd asociate iterative methods with a iterative developmemt metodologies. I think a better term would be higher-order functions. (And as an aside, methods have no return type/void whereas functions return something). Mar 1, 2013 at 12:38
  • @JohannesRudolph "methods have no return type/void whereas functions return something" -- in which language is this true? I've never heard this; in fact, the only real distinction I've ever heard was methods are associated with a class, functions are not.
    – threed
    Jul 6, 2015 at 19:26

6 Answers 6

14

My guess is that you just divided/moved the complexity. It decreased because you doesn't count the implementation of .where() in your CC.

The overall CC hasn't really moved, your own code's CC decreased, only because it's now moved to the framework's code.

I'd say it's more maintainable. When it's a feature of the language, use it. It's no a "ohh, I see, it's a clever trick" that you're using, only a simple inline reduce-like function.

11

All you are doing is highlighting a flaw in cyclomatic complexity as a metric, the complexity of the code really hasn't changed. You have an explicit branch in the first example and need to understand there is an implied branch in the second. The second is clearer and easier to understand provided you understand the syntax, and since it uses less basic syntax that may be an issue.

1
  • 1
    One could argue that the cyclomatic complexity of one's own code is reduced here, since where here is likely from a framework. I think cyclomatic complexity as a metric is useful even here because, while breaking a function up into several doesn't decrease the overall complexity of the system (indeed, it often increases it, if only slightly), it does help you determine when one piece of the system becomes overly complex and needs to be broken down.
    – threed
    Jul 6, 2015 at 19:32
6

In order to answer the question objectively, we'd need some sort of metric for maintainability. Cyclomatic complexity itself isn't a measure of maintainability, but it is a component of some metrics that purport to measure maintainability. For example, the formula for the Maintainability Index is:

MI = 171 - 5.2 * ln(V) - 0.23 * (G) - 16.2 * ln(LOC)

where G is the cyclomatic complexity of the code in question. Therefore, reducing the cyclomatic complexity of a piece of code does by definition improve the code's Maintainability Index and other metrics which similarly use cyclomatic complexity.

It's hard to say whether the kind of change you propose makes the program seem more maintainable to programmers; that probably depends on how familiar they are with (in your case) the where method.

4
  • Warning: Magic Numbers! (>_<)
    – Izkata
    Feb 25, 2013 at 19:12
  • There's just this one little problem with the Maintainability Index. The three components of it are Halstead Volume, Cyclomatic Complexity, and Lines of Code. Both Halstead Volume and Cyclomatic Complexity have been shown to be very strongly correlated to Lines of Code. This means that an alternative approximate equation for the Maintainability Index that depended ONLY on Lines of Code could be derived that would be almost as accurate as the original, with considerably less computational difficulty. Feb 25, 2013 at 19:17
  • @JohnR.Strohm I think you'd get a similar result even if you just use LOC as the maintainability metric. The OP's change reduces LOC to 1 from 4, and other changes that reduce cyclomatic complexity are similarly likely to reduce LOC as well. Either way, it really comes down to how you define and measure maintainability.
    – Caleb
    Feb 25, 2013 at 19:27
  • 1
    @Caleb: Agreed. The point I try to make is that people far too often go for these really complicated metrics and combinations of metrics, not realizing that just about all of them have been shown to be strongly correlated on real code with plain old Lines Of Code (LOC), and hence have no more predictive or descriptive value than LOC. Feb 25, 2013 at 20:23
3

Because it has been shown that cyclomatic complexity (CC) is very strongly correlated with code size, "so much so that CC can be said to have absolutely no explanatory power of its own." what you are really asking is whether "iterative methods such as are commonly found in modern languages such as C#, JavaScript, and (hopefully) in Java 8 reduce code size's impact on understandability and supportability of code."

At that point, one would hope that the answer would be obvious. It has been known for decades that shorter code is generally easier to understand, maintain, and support.

2

If you're talking about the raw stat of Cyclomatic Complexity, sure. You just nuked it from 2 to 0. If you're going by numbers, pure gain, all the way.

From a practical (read: human) perspective though, I would argue you've actually increased the complexity by 2. One point of that comes from the fact that now some other programmer must bring or get knowledge of fluent-syntax LINQ to understand this code.

Another point of increased difficulty comes from understanding Lambda Expressions; though a Lambda is fairly straightforward in this instance, there are some paradigm shifts that have to be made to fully appreciate them.

This case, using a.where(x => matches(x, arg)) isn't terrible, and in all honesty is a great way to get some coworker to see and work with LINQ and Lambda expressions for the first time (I actually presented a tutorial on LINQ/Lambdas to some former coworkers using this and other sets of code, to great effect.) However, the usage of these does require some knowledge.

I recommend caution, because I've seen cases where the LINQ refactor is actually significantly worse to read than what that foreach loop becomes.

1

Subjectively, it depends on the developer audience, if they understand lambda expressions, then;

List<String> filteredList = originalList.where(s => matches(s));

is quicker to understand, and perhaps slightly easier. I would be more concerned about using s, and matches(). Neither is self-descriptive, something like;

List<String> filteredList = 
    originalList.where(stringToBeTested => matchesNameTest(stringToBeTested));

Or

List<String> filteredList = 
        originalList.where(originalListString => matchesNameTest(originalListString));

gives the developer more meaningful information and is easier to analyze, without having to dive into the matches() function to determine what match is being performed.

Maintainability is not only about the ability to comprehend the code, but mainly about the speed and accuracy with which one can comprehend the code.

1
  • 2
    Hi Ace. It's an example, intentionally devoid of semantic content to highlight the structure.
    – C. Ross
    Feb 25, 2013 at 18:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.