agentleFS
auralshinauralshin/coding-skills/skills/cyclomatic-complexity/SKILL.md

cyclomatic-complexity

Skill0 starsChanged 3 months agoMITView on GitHub

This skill should be used when the user asks "how complex is this function", "count the branches", "find overly complex functions", "set a complexity threshold", "reduce cyclomatic complexity", or asks to interpret a complexity report from radon, ESLint, gocyclo, PMD, or lizard. Also applies when a function "has too many if statements" and needs simplifying.

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 3 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈3

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Start at 1. Add 1 for each decision point.

    line 12
  2. Very specific:

    Polymorphism: When the same switch on a type appears in several functions, move each branch onto the type.

    line 93
  3. Distinctive:

    Define errors out of existence: Every failure mode removed from a callee deletes a branch from every call site.

    line 97
  4. Distinctive:

    Use cyclomatic complexity to estimate test count, cognitive complexity to judge readability.

    line 114

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Cyclomatic complexity

Cyclomatic complexity counts the independent paths through a function. It estimates two things: the minimum number of test cases needed for branch coverage, and how much a reader must hold in mind at once.

Counting rule

Start at 1. Add 1 for each decision point.

Add 1 forNotes
if, else ifelse adds nothing, it is the fall-through path
for, while, do-whileeach loop
case in a switcheach case label. The switch and default add nothing
catch / excepteach handler. try and finally add nothing
&&, ||, ??each short-circuit operator
ternary ? :one each
?. optional chainingone each, though some tools skip it
pattern-match arm with a conditioneach arm

Nothing else counts. Assignments, calls, returns, and straight-line statements are free.

Worked example

def classify(user, order):          # 1  base
    if user is None:                # 2
        return "anonymous"
    if order.total > 100 and user.is_member:   # 3 if, 4 and
        return "vip"
    for item in order.items:        # 5
        if item.flagged:            # 6
            return "review"
    return "standard"

Complexity 6, so 6 test cases for full branch coverage.

Interpreting the score

ScoreBandAction
1-5SimpleNone
6-10ModerateAcceptable, monitor
11-20ComplexRefactor when next touching it
21-50High riskRefactor now, hard to test fully
50+UntestableRedesign

Gate CI at 10 per function if a single threshold is needed. Apply it consistently rather than debating case by case.

Two constraints on interpretation:

  • The metric is per function, never per file. A large file of simple functions is fine.
  • Parsers, state machines, and generated code score high legitimately. Exclude them rather than distorting code to satisfy a linter.

Reducing it

Removing decision points lowers complexity. Moving them elsewhere does not. Splitting a complexity-20 function into two complexity-10 functions helps only when each half is independently understandable.

Guard clauses. Flatten nesting by returning early. The count is unchanged but nesting depth and cognitive load drop sharply.

function pay(user) {
  if (!user) return null;
  if (!user.active) return null;
  if (user.balance <= 0) return null;
  return charge(user);
}

Table lookup. Replace an if/else chain that maps values to results. This genuinely removes branches.

// complexity 5
function rate(tier) {
  if (tier === "free") return 0;
  if (tier === "pro") return 10;
  if (tier === "team") return 25;
  return 100;
}

// complexity 1
const RATES = { free: 0, pro: 10, team: 25 };
const rate = (tier) => RATES[tier] ?? 100;

Extract a named predicate. Move a compound condition into its own function. The operators still cost 1 each but now live in a small, separately testable unit.

Polymorphism. When the same switch on a type appears in several functions, move each branch onto the type.

Split by responsibility. A function that validates, transforms, and persists is three functions. This is the only refactor that reliably lowers the total complexity a reader absorbs at once.

Define errors out of existence. Every failure mode removed from a callee deletes a branch from every call site.

Tooling

LanguageToolCommand
Pythonradonradon cc -s -n C path/
JS/TSESLint"complexity": ["error", 10]
Gogocyclogocyclo -over 10 .
JavaPMDCyclomaticComplexity rule
C/C++ and many otherslizardlizard -C 10 path/
Multi-languageSonarQubereports cyclomatic and cognitive complexity

Cognitive complexity

Cyclomatic complexity scores ten flat if statements the same as ten nested ones. Cognitive complexity (SonarSource, 2017) adds a nesting penalty and does not charge for a switch.

Use cyclomatic complexity to estimate test count, cognitive complexity to judge readability. When a tool reports both, gate CI on cyclomatic and use cognitive as the refactoring signal.

Limits

The score says nothing about naming, coupling, duplication, or correctness. A complexity-3 function can be entirely wrong. Treat a high score as a candidate for review, not a verdict.

To decide which complex code is worth fixing, cross the score with change frequency using code-hotspots. To explain why a change is hard rather than why a function is hard to test, use connascence.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.