Python Code Health: Patterns Across 37 Open-Source Repositories

An aggregate analysis of code health patterns across 37 open-source Python repositories, revealing that complex branching appears in 100% of projects studied.

Stephen Collins ·

Key Points

What are the most common antipatterns in Python open-source codebases?

Complex branching appears in 100% of the 37 Python repositories analyzed. Exit-heavy functions, god functions, long functions, and deeply nested code each appear in 97% of repos (36 of 37).

Which open-source Python projects have the highest code complexity risk?

BerriAI/litellm and odoo/odoo both score 24.8, the highest activity risk in the dataset. pandas-dev/pandas and jax-ml/jax follow closely at 22.1 and 22.4 respectively.

How does Python compare to other languages in code health?

Python repositories in this study show a median top risk score of 19.8 and an average of 19.7, with complex branching as a universal problem likely driven by Python's reliance on explicit conditionals over pattern matching.

Every Python repository I analyzed has complex branching problems. Not most of them—all 37. The language that prides itself on readability produces codebases where cyclomatic complexity spirals past maintainable thresholds with near-perfect consistency.

Methodology

I scanned 37 open-source Python repositories using static analysis combined with git history from the past 90 days. The activity risk score multiplies structural complexity (cyclomatic complexity × nesting depth × fan-out) by recent commit frequency. Functions that are both complex and actively changing score highest, surfacing the code most likely to cause problems during your next release.

The Most Common Antipatterns

Complex branching appeared in all 37 repositories. This pattern triggers when cyclomatic complexity exceeds 15—meaning a function has enough conditional paths that reasoning about behavior becomes error-prone. Python’s lack of native pattern matching (until 3.10, and still underutilized) pushes developers toward lengthy if/elif chains that accumulate over time.

Exit-heavy functions showed up in 36 of 37 repos. Multiple return statements scatter responsibility across a function body, making it harder to trace what conditions lead to which outputs. Python’s permissive syntax makes early returns feel natural, but the cumulative effect is functions with five, ten, or more exit points.

God functions also appeared in 36 repositories. These combine high complexity with high fan-out and excessive length—functions trying to do everything. Python’s dynamic typing removes some of the friction that might otherwise encourage decomposition in statically typed languages, letting functions grow unchecked until they become unmaintainable.

The Highest-Risk Repositories

BerriAI/litellm and odoo/odoo tied for the highest activity risk at 24.8. Both show the same antipattern trifecta: complex branching, deep nesting, and exit-heavy functions. For litellm, this likely stems from handling multiple LLM provider APIs with slightly different behaviors. Odoo’s score reflects years of accumulated business logic in an ERP system where edge cases multiply.

safishamsi/graphify scores 22.5, exhibiting the same three patterns. Graph processing code tends toward deep recursion and complex state management, which surfaces as nesting and branching problems.

jax-ml/jax at 22.4 and pandas-dev/pandas at 22.1 round out the top five. Both are numerical computing libraries where performance-critical code paths often resist refactoring—developers hesitate to break apart functions that have been carefully optimized, even when complexity becomes unwieldy.

What This Means for Python Developers

The universality of complex branching suggests this isn’t a problem of individual discipline—it’s a language-level tendency. Python’s syntax makes conditionals cheap to write and extend. A function that starts with two branches easily becomes one with twelve as requirements evolve. The fix isn’t vigilance; it’s tooling that flags functions before they cross the complexity threshold.

The 97% prevalence of god functions tells a similar story. Python’s flexibility works against modularity unless teams actively enforce decomposition. If your codebase has been growing for more than a year, you almost certainly have functions that need to be split. The question is whether you find them during a calm refactoring sprint or during an incident.

Analyze Your Own Repository

You can run the same analysis on any local repository. Install the CLI:

brew install Stephen-Collins-tech/tap/hotspots

Or on any platform with Rust:

cargo install hotspots-cli

Then run:

hotspots analyze .

The output ranks functions by activity risk, showing exactly where complexity and churn intersect in your codebase.

Was this useful? Let me know →

Want to see analysis like this for your own codebase? Try hotspots — free & open source →