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.