include::../intro.adoc[]

== Why is this an issue?

include::../why.adoc[]

=== What is the potential impact?

include::../impact.adoc[]

== How to fix it

include::../how.adoc[]

=== Code examples

**Extraction of a complex condition in a new function.**

==== Noncompliant code example

The code is using a complex condition and has a cognitive cost of 5.

[source,python,diff-id=1,diff-type=noncompliant]
----
def process_eligible_users(users):
    for user in users:             # +1 (for)
        if ((user.is_active and    # +1 (if) +1 (nested) +1 (multiple conditions)
            user.has_profile) or   # +1 (mixed operator)
            user.age > 18 ): 
            user.process()
----

==== Compliant solution

Even if the cognitive complexity of the whole program did not change, it is easier for a reader to understand the code of the `process_eligible_users` function, which now only has a cognitive cost of 3.

[source,python,diff-id=1,diff-type=compliant]
----

def process_eligible_users(users):
    for user in users:             # +1 (for)
        if is_eligible_user(user): # +1 (if) +1 (nested)
            user.process()

def is_eligible_user(user):
    return ((user.is_active and user.has_profile) or user.age > 18) # +1 (multiple conditions) +1 (mixed operators)
----

**Break down large functions.**

==== Noncompliant code example
  

__Note:__ The code is simplified here, to illustrate the purpose. 
Please imagine there is more happening in the process. +

The bellow code has a cognitive complexity score of 8.

[source,python,diff-id=3,diff-type=noncompliant]
----
def process_user(user):
    if user.is_active():             # +1 (if)
        if user.has_profile():       # +1 (if) +1 (nested)
            ... # process active user with profile
        else:                        # +1 (else)
            ... # process active user without profile
    else:                            # +1 (else)
        if user.has_profile():       # +1 (if) +1 (nested)
            ... # process inactive user with profile
        else:                        # +1 (else)
            ... # process inactive user without profile
----

This function could be refactored into smaller functions:
The complexity is spread over multiple functions and the breaks in flow are no more nested. +
The `process_user` has now a complexity score of two.

==== Compliant solution

[source,python,diff-id=3,diff-type=compliant]
----
def process_user(user):
    if user.is_active():             # +1 (if)
        process_active_user(user)
    else:                            # +1 (else)
        process_inactive_user(user)

def process_active_user(user):
    if user.has_profile():           # +1 (if) +1 (nested)
        ... # process active user with profile
    else:                            # +1 (else)
        ... # process active user without profile

def process_inactive_user(user):
    if user.has_profile():           # +1 (if) +1 (nested)
        ... # process inactive user with profile
    else:                            # +1 (else)
        ... # process inactive user without profile
----

**Avoid deep nesting by returning early.**

==== Noncompliant code example

The below code has a cognitive complexity of 6.

[source,python,diff-id=4,diff-type=noncompliant]
----
def calculate(data):
    if data is not None:  # +1 (if)
        total = 0
        for item in data: # +1 (for) +1 (nested)
            if item > 0:  # +1 (if)  +2 (nested)
                total += item * 2
        return total
----

==== Compliant solution

Checking for the edge case first flattens the `if` statements and reduces the cognitive complexity to 4.

[source,python,diff-id=4,diff-type=compliant]
----
def calculate(data):
    if data is None:      # +1 (if)
        return None
    total = 0
    for item in data:     # +1 (for)
        if item > 0:      # +1 (if) +1 (nested)
            total += item * 2
    return total
----

=== Pitfalls

As this code is complex, ensure that you have unit tests that cover the code before refactoring.

include::../resources.adoc[]

include::../rspecator.adoc[]