Cyclomatic complexity is a measure of the structural complexity of a program. This is based on the number of linearly independent paths data can take through a program’s control flow; the more paths there are, the more difficult the program is to understand.

The simplest version of this is a linear script, with no flow control, that simply executes top to bottom.

The best way to manage this is to continually ask how complex a specific function we are writing is. The difficulty is not in keeping the lines of code low, it’s by minimizing the quantity of branching points through the logic. For example:

def create_customer(customer: Customer) -> Customer:
	if customer:
		if customer.name:
			if customer.age < 99 and customer.age >18:
				return Customer(customer)
			else:
				raise ValueError("Not the right age!")
		else:
			customer.name = "N/A"
			if customer.age < 99 and customer.age >18:
				return Customer(customer)
			else:
				raise ValueError("Not the right age!")
	else:
		raise ValueError("No customer provided!")
			

This code has very high complexity, with multiple branching points that makes it very difficult to read, although it is theoretically correct.

The solution is to implement happy path coding like with Recursive reasoning, write a function assuming everything will work correctly:

def create_customer(customer: Customer) -> Customer:
	return Customer(customer)

Then we can consider the failure scenarios and simply add a few safeguards:

def create_customer(customer: Customer) -> Customer
	if not customer:
		raise ValueError("no customer provided")
	if customer.age > 99 or customer.age < 18:
		raise ValueError("Not the right age!")
	if not customer.name:
		customer.name = "N/A"
 
	return Customer(customer)

A few more general techniques:

  • Break down complex functions into smaller, simpler functions that perform specific tasks.
  • Use control structures, such as if-else statements and loops, in a consistent and predictable way.
  • Use functional programming concepts and techniques, such as immutability and Pure Functions, to reduce the need for complex control flow.
  • Use design patterns, such as the state pattern, to simplify complex control flow.
  • Regularly review the code and refactor it to simplify the control flow.
  • Use static code analysis tools that can detect and report high cyclomatic complexity in the code.