CRITICAL RULE: When refactoring or modularizing code:
- NO backward compatibility shims
- NO deprecation warnings
- NO legacy method wrappers
- Clean break approach only
- Backward compatibility adds technical debt
- Legacy code paths increase complexity and maintenance burden
- Clean breaks force updates and prevent fragmented codebases
- All code should use the current, best structure
Package __init__.py exports are legitimate APIs when their declared
__all__ exposes the current implementation. Migrate callers to implementation
modules before deleting redundant facades. Publishing implementations and
their supported exports are documented in
infrastructure/publishing/AGENTS.md.
Retired interfaces remain documented in release history, rather than current
usage guidance.
When modularizing or restructuring:
- Create new modular structure with clear separation of concerns
- Delete old files completely - no leaving "deprecated" files around
- Update ALL imports across the entire codebase immediately
- Update ALL tests to use new imports
- Update ALL documentation (AGENTS.md, README.md, docstrings)
- Run full test suite to ensure nothing breaks
- Commit as single atomic change with clear migration notes in commit message
Maximum file length: aim for ≤600 lines as a soft target. The enforced
module_line_count gate (infrastructure/validation/line_count.py,
DEFAULT_INFRA_THRESHOLDS) warns at 800 lines and fails at 950 lines for
infrastructure code (project scripts: warn 150, fail 250).
When to split a file:
- Multiple independent classes/functions that could be separate modules
- Mixed concerns that would benefit from separation
- File approaches the gate thresholds (800 warn / 950 fail for infra code; exceeding the fail threshold blocks the module-line-count gate) — negative control:
tests/infra_tests/validation/test_line_count.py::test_scan_line_counts_warn_and_failfeeds a 960-line infra module and asserts it lands in the failures list Exceeding the fail threshold fails the gate outright; the warn threshold only invites refactoring review. These are enforcement numbers: a module over 950 lines fails the gate, while anything between the 800-line warn level and the ceiling emits warnings without blocking.
How to split:
flowchart LR
OLD[old_module.py<br/>835 lines] --> NEW[new_structure/]
NEW --> INIT[__init__.py<br/>exports all public API]
NEW --> BASE[base.py<br/>base classes · ~100 lines]
NEW --> CA[component_a.py<br/>~250 lines]
NEW --> CB[component_b.py<br/>~200 lines]
NEW --> CC[component_c.py<br/>~200 lines]
classDef before fill:#7c2d12,stroke:#0f172a,color:#fff
classDef d fill:#0f172a,stroke:#0f172a,color:#fff
classDef after fill:#0f766e,stroke:#0f172a,color:#fff
class OLD before
class NEW d
class INIT,BASE,CA,CB,CC after
Before refactoring, search for all imports:
rg "from old_module import" .
rg "import old_module" .After refactoring, update all imports:
- Infrastructure modules
- Test files
- Scripts
- Documentation examples
Update in this order:
- Module docstrings in files
__init__.pyexports with clear APIAGENTS.mdwith new architectureREADME.mdwith new import examples- Any inline code examples in documentation
- Before: Capture current behavior with characterization, edge-case, and negative-control tests; meet the applicable project/infrastructure coverage gate without treating a percentage as proof of semantic completeness
- During: Update tests to import from new locations
- After: Run full test suite with coverage report
- Verify: No broken imports, all tests pass
Bad (backward compatibility approach):
# old_api.py
import warnings
from new_module import *
warnings.warn("Use new_module instead", DeprecationWarning)Good (clean break approach):
# Delete old_api.py completely
# Update all imports:
# OLD: from old_api import Thing
# NEW: from new_module.things import Thingrefactor: modularize [module_name] into focused submodules
BREAKING CHANGE: Split [old_file.py] into [new_structure/]
- Created new modular structure with clear separation
- Moved [Component A] to [new_location]
- Moved [Component B] to [new_location]
- Updated all imports across codebase
- Updated all tests
- Updated documentation (AGENTS.md, README.md)
- All tests passing with maintained coverage
Migration guide:
- OLD: from module.old import Thing
- NEW: from module.new.things import Thing
Before marking refactoring:
- New modular structure created
- Old file(s) deleted completely
- All infrastructure imports updated
- All test imports updated
- All script imports updated
- All documentation examples updated
-
__init__.pyexports public API clearly - AGENTS.md reflects new structure
- README.md has updated examples
- Full test suite passes
- Test coverage maintained or improved
- No linter errors introduced
- Commit message documents migration
- Clarity: Only one way to import things
- Maintainability: No legacy code paths to maintain
- Performance: No deprecation warnings at runtime
- Quality: Forces updates
- Documentation: Documentation stays current
- Testing: Tests reflect actual usage
- Simplicity: Codebase easier to understand
- Development Standards — Rule index
- Rules AGENTS — Rules for AI agents