feat(style): Add anti-OOP conventions and OOP refactoring tracker
- Add section 10 (Anti-OOP Conventions) to python.md with hard rules, class justification requirements, and Strangler Fig refactoring pattern - Create conductor/refactor_oop.md tracker with 4 phases for class elimination - Add ruff PLR rules (PLR0912, PLR6301, PLR0206) to pyproject.toml for OOP anti-patterns Addresses AI agent scope misinterpretation issues by enforcing flat function-call graphs over deep class hierarchies.
This commit is contained in:
@@ -68,11 +68,68 @@ is processed by AI agents, while preserving readability for human review.
|
||||
- **Minimal blank lines.** Token-efficient density is preferred over visual padding.
|
||||
- **Short variable names are acceptable** in tight scopes (loop vars, lambdas). Use descriptive names for module-level and class attributes.
|
||||
|
||||
## 9. Line Length
|
||||
## 10. Anti-OOP Conventions
|
||||
|
||||
- Soft limit: 120 characters.
|
||||
- Hard limit: None — let the formatter handle wrapping if needed.
|
||||
- Rationale: 80-char limits cause excessive line continuations that waste tokens.
|
||||
### Philosophy
|
||||
AI agents consistently misinterpret class hierarchies, method resolution, and inheritance. Flat function-call graphs are deterministic and traceable. OOP introduces scoping complexity that compounds with indentation.
|
||||
|
||||
### Hard Rules (Enforced by lint)
|
||||
- **Never write a class for a single method.** Use a function.
|
||||
- **Never use inheritance for code reuse.** Compose with standalone functions.
|
||||
- **Never use private methods (`_method`).** Module-level functions with clear names suffice.
|
||||
- **No nested classes.** Define helper types at module level.
|
||||
- **No decorator classes.** Use plain functions with decorators.
|
||||
|
||||
### Class Justification Required
|
||||
Every class definition MUST include a comment explaining WHY it is a class and not a function group or struct:
|
||||
|
||||
```python
|
||||
# JUSTIFIED: Holds mutable shared state across multiple async operations
|
||||
# Cannot be replaced by functions without passing state through every call
|
||||
class AsyncOperationScheduler:
|
||||
...
|
||||
|
||||
# NOT JUSTIFIED: Simply groups related functions
|
||||
# Should be module-level functions in operations.py
|
||||
class OperationHelper:
|
||||
def validate(self): ...
|
||||
def execute(self): ...
|
||||
```
|
||||
|
||||
### Acceptability Criteria
|
||||
A class is justified ONLY when ALL of:
|
||||
1. It holds mutable state that must be encapsulated
|
||||
2. It has 3+ related methods that share state
|
||||
3. It implements a behavioral interface used polymorphically (not just data grouping)
|
||||
|
||||
### Refactoring Existing Classes (Strangler Fig Pattern)
|
||||
When refactoring a class to functions:
|
||||
1. Write test validating current behavior (prevents regression)
|
||||
2. Extract one method at a time into module-level functions
|
||||
3. Create wrapper function that delegates to class until migration complete
|
||||
4. Delete class only when ALL callers migrated
|
||||
5. Commit with `refactor(oop):` prefix
|
||||
|
||||
### Data Structures
|
||||
- **Data-only containers:** Use `NamedTuple`, `dataclass(frozen=True)`, or plain `dict` — NOT classes
|
||||
- **State machines:** Use dict-based transitions, not class + inheritance
|
||||
- **Configuration:** Plain dict or `TypedDict`, not classes with defaults
|
||||
|
||||
### Anti-Patterns (Flagged by Ruff PLR rules)
|
||||
- `PLR0912`: Too many branches — extract to functions
|
||||
- `PLR6301`: No public methods — class is a namespace anti-pattern
|
||||
- `PLR0206`: Descriptors in class body — use simple attributes
|
||||
|
||||
### Enforcement
|
||||
```toml
|
||||
[tool.ruff.lint.select]
|
||||
select = ["E", "F", "W", "C90", "C4", "PLR0912", "PLR6301", "PLR0206"]
|
||||
|
||||
[tool.ruff.lint.plr]
|
||||
max-returns = 4
|
||||
max-locals = 8
|
||||
max-args = 5
|
||||
```
|
||||
|
||||
## 11. Structural Dependency Mapping (SDM)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user