Preferred Refactor Style
Migrate Callers, Then Delete Legacy APIs
Default posture: cutover refactors.
Update All Internal Callers
Update all internal callers in one wave; delete old APIs/schemas/tests for deprecated behavior.
Avoid Compatibility Wrappers
Do not add long-lived compatibility wrappers. If compatibility is absolutely required: keep it small, local, temporary, mark it clearly for removal (with a removal note).
Subtract Before You Add
Before introducing a new abstraction or subsystem, remove dead code, obsolete wrappers, and duplicate paths in the target area.Outcome-Oriented Execution
- It’s OK for intermediate steps to break if the breakage is scoped and planned
- Final state must be coherent, verified, and free of permanent temporary scaffolding
Redesign From First Principles
For major architectural or schema changes:- Define the ideal target state as if this requirement existed from day one
- Land incrementally toward that target
- Do not bolt on special cases that preserve avoidable complexity
Exhaust the Design Space
Only when not dictated by the original binary and there are multiple viable options:- Sketch 2–3 concrete alternatives
- Pick based on parity risk, maintainability, and testability
Code Smells to Avoid
Especially in gameplay/domain code:Defensive Runtime Checks
Defensive Runtime Checks
Deep in the domain:
isinstance,hasattr, “just in case”try/except ValueError.get()/getattr()on typed models to dodge typing (OK for truly dynamic dicts or narrow tooling)
Thin Duck Typing
Thin Duck Typing
Thin duck typing protocols that blur invariants
Hand-Written Mappers
Hand-Written Mappers
Hand-written field-by-field mappers just to satisfy style (prefer serializers/DTOs)
Long-Lived Compat Layers
Long-Lived Compat Layers
Long-lived “compat” layers when internal callers can be migrated
Encode Lessons in Structure
- Tests / snapshots / invariants
ast-greprulesimport-lintercontracts- Typed schemas and decoders
- Scripts/automation
Quick Playbooks
Parity Bug Investigation
Re-Run
Re-run the same capture/replay and confirm the mismatch moves/disappears for the right reason.
API/Schema Refactor (Cutover Wave)
Capture-Only Triage
Docs/Tooling Changes
Structural Search / Codemods
Preferast-grep over regex-only edits for structural transformations.
Python
Zig
Usesgconfig.local.yml to load the custom Zig parser. Zig metavariables use _VAR syntax (e.g. _EXPR):
Pull Requests (gh CLI Hygiene)
When usinggh:
Conventional Commit Format
PR title must be in the form of a conventional commit:
feat: add new weapon systemfix: correct RNG seed handlingrefactor: simplify perk selection logic
Pre-Commit Verification
Before committing, always run:CI-Equivalent Local Run
- Python/Docs/Tooling Changes
- Zig-Only Changes
- Mixed Changes
For changes to
src/, tests/, docs/, tools/:Reminder: When in Doubt
Choose Fidelity
Choose fidelity and determinism over cleanliness
Prove Behavior
Prove behavior with decompile/captures/replays/tests, not intuition
Fix Boundaries
Fix schemas/types/contracts at boundaries rather than weakening the domain
Delete Old Paths
Delete old paths rather than supporting two worlds