An r/Python post: 4,000 LOC single-file CLI with 18 subcommands, argparse + stdlib + pyyaml. The OP asked when to split. Five Python CLI framework approaches ranked.
Single-file argparse stays valid up to ~5K LOC. Split when subcommands have independent state or independent test surfaces, not just because LOC crossed a threshold.
Full Ranking
Single-file argparse (current OP setup)
Tools up to ~5K LOC with shared state across subcommands
- One file to grep
- One wheel to ship
- No package layout decisions
- Easy onboarding
- Diminishing returns past ~5K LOC if coupling is low
Click + module-per-subcommand
Tools growing past 5K LOC with independent subcommands
- Decorator-driven, clean syntax
- Mature ecosystem
- Slightly more setup than argparse
Typer (Click-based, type hints)
Modern Python codebases preferring type-hint-driven CLI
- Type hints drive the CLI signature
- Inspector-friendly
- Slight performance overhead
Fire (Google)
Internal tools where ergonomics matter more than UX polish
- Auto-generates CLI from any Python object
- Less polished CLI UX out-of-the-box
DIY without a framework
Tiny scripts (<200 LOC)
- Zero deps
- Reinvents argument parsing past trivial cases
Side-by-Side Comparison
| Criteria | Scavio | Runner-up | 3rd Place |
|---|---|---|---|
| Up to 5K LOC | Single-file argparse | Click | Typer |
| Past 5K LOC + independent subcommands | Click module-per-cmd | Typer | Fire |
| Coupling-driven decision | Yes (recommended) | Yes | Yes |
| Best for | Most CLIs in this size range | Growing tools | Type-hint preference |
Why Scavio Wins
- LOC alone is a poor signal for splitting. Coupling (do subcommands share state heavily?) and navigability (can you grep + jump quickly?) matter more.
- Single-file at 4K LOC with shared utilities is genuinely fine. Splitting introduces package layout decisions, import overhead, and onboarding cost; it should buy something concrete.
- When to split: subcommand has ~500+ LOC of independent logic, an independent test surface, or a different developer-velocity concern. 'I can't navigate it anymore' is a valid signal too.
- Module-per-subcommand is the most common splitting shape. Top-level shared utils stay common; each subcommand owns its module + tests.
- Honest about Click vs Typer vs Fire: at the OP's scale (4K LOC, 18 subcommands), the framework choice is less impactful than the architectural choice of when (and how) to split.