Python Code Quality Tools: Stop Technical Debt Before It Starts
Technical debt sneaks in through sloppy code: vulnerabilities that bite later, tangled imports that slow onboarding, or functions so complex they become black boxes. This guide cuts through the noise with nine battle-tested Python tools. Each one targets a specific pain point—security, complexity, cohesion, coupling, and more—helping you enforce standards early. We'll cover what it does, quick install/usage, and why it pays off. No fluff; just actionable steps.
Focus on integration: Hook these into pre-commit, CI/CD (e.g., GitHub Actions), or your IDE. Start small—pick 2-3 for your workflow—and scale.
1. Safety CLI: Vulnerability Scanning
What it does: Scans Python dependencies for known vulnerabilities and malicious packages using a massive DB (3x more coverage than free alternatives). Outputs fixes, JSON/SBOM/HTML reports, and integrates with CI/CD.
Install: pip install safety
Quick usage:
- Authenticate:
safety auth - Scan project:
safety scan - Fix deps:
safety scan --apply-fixes - System-wide:
safety system-scan
Prevents debt by: Catching supply-chain risks before deployment. Legacy deps? It auto-updates to safe versions per your policy. Free tier for solos; paid ($25/seat/mo) for teams.
Pro tip: GitHub Action: Add to .github/workflows for PR scans.
2. Bandit: Security Issue Detector
What it does: Analyzes Python AST for common security flaws (e.g., hardcoded secrets, unsafe eval). Plugin-based; generates reports in text, JSON, HTML, or XML.
Install: pip install bandit
Quick usage:
- Basic scan:
bandit -r . - Config file:
bandit -c .bandit.yml -r . - CI output:
bandit -f json -r . > security.json
Prevents debt by: Flags injection risks and weak crypto early, reducing breach costs. Extensible for custom rules; integrates with pre-commit or Jenkins.
Pro tip: Ignore false positives via config; pair with Safety for full dep+code coverage.
3. LCOM: Lack of Cohesion of Methods
What it does: Measures class cohesion (LCOM4 metric). Low scores mean classes juggle unrelated responsibilities—split 'em. Ignores constructors/inherited methods.
Install: pip install lcom
Quick usage:
- Scan module:
lcom src - Single file:
lcom src/command.py - Output: Table of LCOM scores per class (0-1; aim for 1).
Prevents debt by: Exposes god classes that resist refactoring. High cohesion = easier tests/maintenance; refactor >2 scores.
Pro tip: Use in CI: Fail builds if average >1.5. Simple CLI; no heavy deps.
4. Cohesion: Class Cohesion Metrics
What it does: Tracks variable usage across class methods (e.g., 66% cohesion if 2/3 vars used). Verbose mode shows per-function breakdowns.
Install: pip install cohesion
Quick usage:
- Scan file:
cohesion --files example.py --verbose - Threshold:
cohesion --files . --below 50(shows low-cohesion classes) - Flake8 integration:
flake8 .(flags H601 low cohesion)
Prevents debt by: Highlights underused vars/methods, curbing "kitchen sink" classes. Boosts reusability; clean code stays clean.
Pro tip: Pre-commit hook: hooks: - id: cohesion args: [--below=70]. Poetry for dev setup.
5. Module Coupling Metrics: Instability & Abstractness
What it does: Computes SDP/SAP metrics (Instability I = Fan-out/(Fan-in+out); Abstractness A = abstract classes/total). Plots A/I graph in PNG/CSV.
Install: pip install module_coupling_metrics
Quick usage:
- Scan package:
module_coupling_metrics /path/to/root - Output: CSV per module + PNG graph (ideal zone: high A, low I).
Prevents debt by: Enforces stable abstractions; unstable modules drag down scalability. Duck-typing friendly—treats inherited classes as abstract.
Pro tip: For monorepos; run post-refactor to validate splits. AGPL license; PyPI publish via GitHub Actions.
6. Complexipy: Cognitive Complexity
What it does: Rust-powered analyzer for human-readability (e.g., nested loops/if-else). Unlike CCN, it forgives simple nests. Thresholds, snapshots for legacy code.
Install: pip install complexipy (or uv add complexipy)
Quick usage:
- Scan dir:
complexipy . --max-complexity-allowed 10 - API:
from complexipy import file_complexity; result = file_complexity("app.py") - Snapshot:
complexipy . --snapshot-create(baseline violations)
Prevents debt by: Spots "brain-melting" code before reviews. Inline ignores (# noqa: complexipy); VS Code extension for real-time feedback.
Pro tip: TOML config in pyproject.toml; pre-commit for zero regressions. Blazing fast—handles large codebases.
7. Lizard: Cyclomatic Complexity & Clones
What it does: Multi-language CCN (McCabe), param counts, NLOC, duplicates. Warnings for thresholds; XML/CSV/HTML output. Whitelists generated code.
Install: pip install lizard
Quick usage:
- Scan:
lizard . -C 10(CCN >10 warns) - Duplicates:
lizard -Eduplicate . - Exclude:
lizard . -x "tests/*" - Gitignore auto-use.
Prevents debt by: Quantifies branching hell; clone detection cuts copy-paste bugs. 20+ languages; no headers/imports needed.
Pro tip: -T nloc=50 for length limits; Jenkins Checkstyle XML. Python 3.8+; Nix/Flake for reproducible envs.
8. Import Linter: Dependency Rules
What it does: Enforces import contracts (e.g., no cycles, forbidden deps). Custom types for acyclic graphs; checks intra/inter-package flows.
Install: pip install import-linter
Quick usage:
- Config
.importlinter: Define contracts (e.g., forbidden: foo → bar). - Lint:
lint-imports - Custom: Extend via plugins.
Prevents debt by: Locks in layered architectures; breaks circular imports that hide bugs. Team-scale: Fail CI on violations.
Pro tip: Acyclic contract for DAGs; screenshot errors for docs. BSD license; Justfile for tasks.
9. Xenon: Complexity Monitoring
What it does: Radon-based thresholds for average/module/block complexity (A-F ranks). Fails builds on breaches; pre-commit friendly.
Install: pip install xenon
Quick usage:
- Scan:
xenon -a A -m B -b C .(fail if avg >A, etc.) - Exclude:
xenon -e "*.test.py" . - Pre-commit: Args like
['--max-absolute=B'].
Prevents debt by: Commits never introduce complexity spikes; tracks trends in CI. Simple ranks align with "maintainable" intuition.
Pro tip: Travis/GitHub examples in repo; Python 3.6-3.12. Pair with Lizard for deeper dives.
Comparison Table: At a Glance
Comparison Table: At a Glance
| Tool | Focus | Metrics/Output | CI Ease | Free Tier? | Best For |
|---|---|---|---|---|---|
| Safety | Vulnerabilities | Fixes, JSON/SBOM | High | Yes (limited) | Dep scanning |
| Bandit | Security flaws | AST plugins, XML | High | Yes | Code security |
| LCOM | Class cohesion | LCOM4 scores (table) | Med | Yes | OOP refactoring |
| Cohesion | Var/method usage | % cohesion, Flake8 | High | Yes | Class design |
| Module Coupling Metrics | Coupling/Abstractness | I/A graph, CSV | Med | Yes | Architecture stability |
| Complexipy | Cognitive complexity | Thresholds, snapshots | High | Yes | Readability reviews |
| Lizard | CCN & clones | Warnings, duplicates | High | Yes | Multi-lang complexity |
| Import Linter | Import rules | Contracts, errors | High | Yes | Dependency hygiene |
| Xenon | Overall complexity | A-F ranks, thresholds | High | Yes | Commit/CI gates |
Next Steps: Build Your Stack
- Audit: Run Lizard + Complexipy on your repo—fix top offenders.
- Secure: Add Safety/Bandit to CI.
- Architect: Use Import Linter + Module Metrics for layers.
- Maintain: Cohesion/LCOM/Xenon in pre-commit.
- Scale: Snapshots (Complexipy) for legacy; whitelists (Lizard) for quick wins.
These tools aren't silver bullets, but they enforce discipline where humans falter. Track metrics over time—debt drops, velocity rises. Questions? Dive into docs (linked in each section) or ping the repos. Code clean, ship fast.
Top comments (0)