DiPeO separate-monolithic-python
Break large Python files (>500 LOC) into smaller, well-organized modules with proper package structure. Use when a Python file is too large, monolithic, or needs refactoring. Triggered by requests mentioning "too large", "separate", "split", "break up", or "refactor" for Python files.
git clone https://github.com/sorryhyun/DiPeO
T=$(mktemp -d) && git clone --depth=1 https://github.com/sorryhyun/DiPeO "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/separate-monolithic-python" ~/.claude/skills/sorryhyun-dipeo-separate-monolithic-python && rm -rf "$T"
.claude/skills/separate-monolithic-python/SKILL.mdSeparate Monolithic Python Code
Break large Python files into maintainable modules following Python best practices.
Workflow
Step 1: Analyze
- Read entire file to understand structure
- Identify components (classes, function groups, constants)
- Count lines (>500 LOC needs separation)
- Map dependencies (what depends on what)
Step 2: Plan Structure
Choose a separation pattern:
By Responsibility (Recommended):
mypackage/ ├── __init__.py # Public API exports ├── models.py # Data models/classes ├── services.py # Business logic ├── utils.py # Helper functions └── constants.py # Configuration
By Feature:
mypackage/ ├── __init__.py ├── feature_a/ │ ├── __init__.py │ ├── models.py │ └── logic.py └── feature_b/
By Layer (Domain-driven):
mypackage/ ├── __init__.py ├── domain/ # Core models ├── application/ # Use cases └── infrastructure/ # External deps
Present plan to user before proceeding.
Step 3: Create Structure
mkdir mypackage touch mypackage/__init__.py mypackage/models.py mypackage/services.py
Step 4: Extract Code
Extract in dependency order:
- Constants (no dependencies)
- Models (minimal dependencies)
- Utilities (depend on constants/models)
- Services (depend on everything)
- Main (orchestrate all)
Step 5: Update Imports
In new modules:
# models.py from .constants import DEFAULT_ROLE from .utils import validate_email
In
(public API):__init__.py
from .models import User, Product from .services import create_user __all__ = ['User', 'Product', 'create_user']
In external files:
# Before: from monolith import User # After: from mypackage import User
Step 6: Validate
ruff check mypackage/ mypy mypackage/ python -c "from mypackage import User" pytest tests/
Key Principles
High Cohesion: Keep related code together
- Group by purpose, not type
- Example:
notuser_service.pyall_services.py
Low Coupling: Minimize dependencies
- Avoid circular imports
- Use dependency injection
Single Responsibility: One clear purpose per module
Clear API: Use
__init__.py to expose public interface
Handling Circular Dependencies
Option 1: Move shared code
# Create shared.py for common code
Option 2: TYPE_CHECKING
from typing import TYPE_CHECKING if TYPE_CHECKING: from .services import UserService # Only for type hints
Option 3: Late import
def process_user(): from .services import create_user # Import inside function create_user()
File Size Guidelines
- ✅ Ideal: 100-300 lines
- ⚠️ Warning: 300-500 lines (consider splitting)
- ❌ Too large: >500 lines (should split)
Quick Example
Before (monolith.py - 800 lines):
DATABASE_URL = "sqlite:///./test.db" class User: def __init__(self, name): self.name = name def create_user(name): return User(name) app = FastAPI() @app.get("/users") def get_users(): return []
After:
api/ ├── __init__.py ├── config.py # DATABASE_URL ├── models.py # User class ├── services.py # create_user └── routes.py # FastAPI routes
Troubleshooting
Import errors: Check
__init__.py exports, verify relative imports (.module)
Circular imports: Use TYPE_CHECKING or late imports, or extract shared code
Tests failing: Update test imports to new package structure
For detailed examples, patterns, and troubleshooting, see references/detailed-guide.md.