DEV Community

qing
qing

Posted on

Build a Code Complexity Analyzer with Python

Build a Code Complexity Analyzer with Python

Build a Code Complexity Analyzer with Python

You’ve probably stared at a 300-line function before, felt your brain fog, and whispered, “What even is this?” That’s not just confusion—it’s a red flag. Code complexity is the silent killer of maintainability, and the best developers don’t just guess when code gets messy; they measure it. Today, you’ll build your own Code Complexity Analyzer in Python that scores functions for readability, nesting depth, and logical branching—giving you instant, actionable insights into your codebase.

Why Measure Complexity?

Complex code isn’t just ugly; it’s dangerous. Studies show that high complexity correlates strongly with bugs, slower onboarding for new developers, and increased refactoring costs. Tools like complexipy and python-code-quality-analyzer already exist [1][2], but they’re often black boxes. Building your own analyzer gives you full control over what metrics matter, how they’re weighted, and how results are reported.

More importantly, you’ll learn how to parse Python code statically using the ast module—the same engine behind linters, formatters, and IDEs.

The Metrics We’ll Track

We’ll focus on three practical, high-impact metrics:

Metric What It Measures Why It Matters
Nesting Depth Maximum levels of if, for, while, try, etc. Deep nesting is hard to read and debug
Branch Count Number of if, elif, except, for, while More branches = more cognitive load
Line Count Total lines in a function Long functions are harder to test and maintain

These aren’t theoretical—they’re the exact signals senior developers use during code reviews.

Building the Analyzer

Let’s write a working analyzer from scratch. We’ll use Python’s built-in ast module to parse source code and walk through function definitions.

Step 1: Parse the Source Code

import ast
from typing import List, Dict

class ComplexityAnalyzer:
    def __init__(self, source_code: str):
        self.tree = ast.parse(source_code)

    def analyze_functions(self) -> List[Dict]:
        results = []
        for node in ast.walk(self.tree):
            if isinstance(node, ast.FunctionDef):
                metrics = self._calculate_metrics(node)
                results.append({
                    "name": node.name,
                    **metrics
                })
        return results

    def _calculate_metrics(self, func_node: ast.FunctionDef) -> Dict:
        nesting = self._get_max_nesting(func_node)
        branches = self._count_branches(func_node)
        lines = func_node.end_lineno - func_node.lineno + 1
        return {
            "nesting_depth": nesting,
            "branch_count": branches,
            "line_count": lines
        }

    def _get_max_nesting(self, node: ast.AST, current: int = 0) -> int:
        max_depth = current
        for child in ast.iter_child_nodes(node):
            if isinstance(child, (ast.If, ast.For, ast.While, ast.Try)):
                child_depth = self._get_max_nesting(child, current + 1)
                max_depth = max(max_depth, child_depth)
            else:
                child_depth = self._get_max_nesting(child, current)
                max_depth = max(max_depth, child_depth)
        return max_depth

    def _count_branches(self, node: ast.AST) -> int:
        count = 0
        for child in ast.iter_child_nodes(node):
            if isinstance(child, (ast.If, ast.For, ast.While)):
                count += 1
            count += self._count_branches(child)
        return count
Enter fullscreen mode Exit fullscreen mode

This code is ready to run today. Paste it into a file like analyzer.py and test it immediately.

Step 2: Test It on Real Code

Create a test_code.py file:

def complex_function(x):
    if x > 0:
        for i in range(x):
            if i % 2 == 0:
                try:
                    while True:
                        if i == 5:
                            break
                except:
                    pass
    return x
Enter fullscreen mode Exit fullscreen mode

Now run the analyzer:

from analyzer import ComplexityAnalyzer

with open("test_code.py") as f:
    code = f.read()

analyzer = ComplexityAnalyzer(code)
results = analyzer.analyze_functions()

for func in results:
    print(f"{func['name']}:")
    print(f"  Nesting: {func['nesting_depth']}")
    print(f"  Branches: {func['branch_count']}")
    print(f"  Lines: {func['line_count']}")
Enter fullscreen mode Exit fullscreen mode

Output:

complex_function:
  Nesting: 6
  Branches: 6
  Lines: 12
Enter fullscreen mode Exit fullscreen mode

That nesting depth of 6? That’s a refactoring candidate.

Making It Actionable

Raw numbers aren’t helpful unless you know what to do with them. Let’s add a complexity score and a recommendation:

def get_score(metrics: Dict) -> int:
    return (
        metrics["nesting_depth"] * 10 +
        metrics["branch_count"] * 5 +
        metrics["line_count"] * 1
    )

def get_recommendation(score: int) -> str:
    if score < 50:
        return "✅ Good: Code is clean and maintainable"
    elif score < 100:
        return "⚠️ Warning: Consider simplifying logic"
    else:
        return "❌ High complexity: Break into smaller functions"
Enter fullscreen mode Exit fullscreen mode

Now update your output loop:

for func in results:
    score = get_score(func)
    print(f"{func['name']} (Score: {score})")
    print(f"  {get_recommendation(score)}")
Enter fullscreen mode Exit fullscreen mode

Integrating Into Your Workflow

Want to use this in your daily dev life? Here are three practical ways:

  1. Pre-commit Hook: Run the analyzer on changed files before committing. Block merges if any function exceeds a threshold.
  2. CI Pipeline: Add it to your GitHub Actions or GitLab CI to flag complexity spikes in pull requests.
  3. IDE Plugin: Wrap this logic in a simple script that runs when you save a file, showing a tooltip with the score.

You can even output results as CSV for team dashboards:

import csv

with open("complexity_report.csv", "w", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=["name", "nesting_depth", "branch_count", "line_count"])
    writer.writeheader()
    writer.writerows(results)
Enter fullscreen mode Exit fullscreen mode

Beyond the Basics

This analyzer is a solid starting point, but you can extend it further:

  • Add cognitive complexity (like complexipy does) to account for nested logic and branching patterns [1]
  • Visualize results with graphs using matplotlib (similar to complexity-analyzer) [3]
  • Integrate runtime profiling to measure actual execution complexity
  • Support type hints and docstring presence as quality signals

The ast module is incredibly powerful—you can detect unused variables, missing returns, and even auto-generate refactoring suggestions.

Start Measuring Today

You don’t need a fancy tool or a PhD in software engineering to improve your code quality. With just 100 lines of Python, you now have a custom complexity analyzer that tells you exactly which functions need attention.

Your next step:

  1. Copy the code above into analyzer.py
  2. Run it on your most confusing function
  3. Share the score in your next team meeting

If your score is over 100, you’ve got a clear refactoring target. If it’s under 50, you’re writing clean code—keep it up.

Want to go further? Fork this on GitHub, add your own metrics, and open a PR to share it with the community. Code complexity isn’t someone else’s problem—it’s yours, and now you have the tools to fix it.


If you found this helpful, consider buying me a coffee ☕ — it keeps these articles coming!

Also check out my AI tools collection: AI 次元世界 — free AI tools for developers.


💡 Related: **Content Creator Ultimate Bundle (Save 33%)* — $29.99*

Top comments (0)