The engineers behind famous OSS are elite. No question.
But even well-maintained codebases grow — and growth has a cost.
Does "famous" still mean "high quality" after years of evolution?
I pointed a static analyzer at Flask and FastAPI to find out.
How I Measured
I ran pyscn — an open-source Python static analyzer — on both frameworks. Unlike traditional linters that focus on style, pyscn measures structural quality: how complex the functions are, how tangled the classes are, how much code is duplicated, whether modules import each other in circles, whether any code is never called, and an overall health score.
What makes pyscn useful for this kind of comparison is that it outputs everything in a single run. No need to combine multiple tools or normalize different scoring systems. Same metrics, same scale, same conditions.
ludo-technologies
/
pyscn
An Intelligent Python Code Quality Analyzer
pyscn - Python Code Quality Analyzer
pyscn is a code quality analyzer for Python vibe coders.
Building with Cursor, Claude, or ChatGPT? pyscn performs structural analysis to keep your codebase maintainable.
Working with JavaScript/TypeScript? Check out jscan
Quick Start
# Run analysis without installation
uvx pyscn analyze .
# or
pipx run pyscn analyze .
Demo
pyscn_20251005.mov
Features
- 🔍 CFG-based dead code detection – Find unreachable code after exhaustive if-elif-else chains
- 📋 Clone detection with APTED + LSH – Identify refactoring opportunities with tree edit distance
- 🔗 Coupling metrics (CBO) – Track architecture quality and module dependencies
- 📊 Cyclomatic complexity analysis – Spot functions that need breaking down
100,000+ lines/sec • Built with Go + tree-sitter
MCP Integration
Run pyscn analyses straight from AI coding assistants via the Model Context Protocol (MCP). The bundled pyscn-mcp server exposes the same tools used in the CLI to Claude Code, Cursor, ChatGPT, and…
I grabbed the latest releases (Flask 3.1.1, FastAPI 0.115.12) and analyzed only the core source. No tests or docs — just the real code.
Here's what 71 files told me:
| Metric | FastAPI | Flask |
|---|---|---|
| Health Score | 68 (C) | 77 (B) |
| Avg Complexity | 10.07 | 7.57 |
| Code Duplication | 2.2% | 0% |
| Coupling (CBO) | 2.23 | 1.39 |
| Circular Deps | 0 | 1 |
| Architecture | 94.3% | 100% |
These numbers are raw output from a general-purpose tool — not tuned for each framework's specific goals or context.
That said, the architecture scores speak for themselves: both above 94%. The health scores look modest (C and B), but that's what happens with large, mature codebases. Look at the individual metrics — low coupling, minimal duplication, almost no dead code — and you can tell these projects are well-maintained.
Let's look at what's behind each number.
What the Numbers Actually Mean
Complexity: The Cost of Features
FastAPI's most complex function scores 28 on cyclomatic complexity. It's responsible for automatic OpenAPI schema generation — a feature Flask doesn't include in core.
Flask has its own complex spots, like find_app_by_string at 22. But the complexity serves different purposes: one builds API specifications automatically, the other handles CLI app discovery.
Coupling: The OpenAPI Tax
FastAPI has 3 classes with high coupling (CBO >= 8). The highest is 12 — a class that needs to know about schemas, references, responses, headers, and more. That's what happens when you build OpenAPI specs programmatically.
Flask's maximum coupling is 6, from the core Flask class itself.
This isn't a design flaw. OpenAPI-first architecture requires components to be aware of each other. FastAPI chose that trade-off deliberately.
The Circular Dependency Surprise
Here's where FastAPI actually wins. Zero circular dependencies.
Flask has one cycle involving 6 modules:
blueprints ←→ sansio.blueprints
blueprints → sansio.scaffold
debughelpers → blueprints
debughelpers → sansio.app
sansio.app → templating
This is 14 years of organic growth. It works, but it makes deep refactoring harder. Newer codebases benefit from hindsight. FastAPI's clean dependency graph reflects lessons learned from predecessors.
Duplication: Compatibility Costs
FastAPI's 2.2% duplication lives mostly in _compat modules — supporting both Pydantic v1 and v2.
Flask: 0%. Minimal surface area means less to duplicate.
Supporting multiple library versions costs code cleanliness. Sometimes that's worth it.
The Real Lesson
These numbers don't pick a winner. They reveal trade-offs.
FastAPI's higher complexity and coupling aren't accidents. They're the cost of automatic OpenAPI docs and type-driven validation baked into the core. The duplication exists to support multiple Pydantic versions. Every metric tells a story of deliberate choices.
Flask takes the opposite path. Lower complexity because the core stays minimal. Lower coupling because features live in extensions. The circular dependency is 14 years of organic growth. Not ideal, but stable.
Both frameworks have zero dead code. Both are maintained by teams who know exactly what they're doing.
But that's the point. Even codebases this well-maintained have spots worth revisiting. A circular dependency here, gradual coupling there. Tools like pyscn make these patterns visible, giving even experienced engineers a fresh perspective on code they see every day.
What About Your Code?
Want to see where your project lands? No installation needed. Just use the following command
uvx pyscn@latest analyze .
Example:
You might find circular dependencies you didn't know about. Or coupling that's grown out of control. Or hopefully confirmation that your architecture is solid.
The goal isn't perfection. It's awareness.
Built with pyscn. Analysis performed January 2026.


Top comments (0)