Reference taxonomy of refactoring hole types including current state holes, architecture holes, and implementation holes. Use as reference during typed holes refactoring for identifying and categorizing unknowns.
Complete taxonomy of refactoring holes with validation patterns.
These holes represent unknowns about the existing system. Resolve before refactoring.
Type: Dict[str, FileMetadata]
Question: What files exist and what do they do?
Resolution:
inventory = {
"src/module.py": {
"purpose": "Core business logic",
"dependencies": ["config.py", "utils.py"],
"public_api": ["process", "validate"],
"dead_code": ["old_process"]
}
}
Validation:
Type: DirectedGraph
Question: What depends on what?
Resolution: Generate dependency graph with cycle detection
Validation:
Type: TestCoverage
Question: What's already tested?
Resolution:
coverage = {
"line_coverage": 0.65,
"branch_coverage": 0.42,
"uncovered_critical": ["auth.py", "payment.py"]
}
Validation:
Type: ArchitectureSpec
Question: What should the ideal structure be?
Resolution:
architecture = {
"layers": ["presentation", "domain", "data"],
"modules": {
"presentation": ["api", "cli"],
"domain": ["core", "services"],
"data": ["repositories", "models"]
},
"rules": [
"presentation can call domain",
"domain cannot call presentation",
"data can only be called by domain"
]
}
Validation Tests:
def test_no_layer_violations():
violations = check_architecture_rules()
assert len(violations) == 0
def test_dependency_direction():
for module in domain_modules:
deps = get_dependencies(module)
assert not any(d in presentation_modules for d in deps)
Type: Dict[str, ModuleDef]
Question: How should modules be organized?
Resolution:
modules = {
"auth": {
"responsibility": "Authentication & authorization",
"public_interface": ["authenticate", "authorize"],
"private": ["hash_password", "validate_token"]
}
}
Validation:
Type: List[Layer]
Question: What interfaces/protocols are needed?
Resolution:
class Repository(Protocol):
def get(self, id: str) -> Entity: ...
def save(self, entity: Entity) -> None: ...
class Service(Protocol):
def execute(self, command: Command) -> Result: ...
Validation:
Type: List[ConsolidationPlan]
Question: What duplicate code should merge?
Resolution:
consolidations = [
{
"targets": ["parse_v1.py", "parse_v2.py", "parse_v3.py"],
"unified": "parser.py",
"reason": "Same logic with minor variations",
"strategy": "Single function with mode parameter"
}
]
Validation Tests:
def test_consolidated_equivalence():
"""New unified function equals all old functions"""
for old_func, test_cases in [(parse_v1, cases1), ...]:
for case in test_cases:
old_result = old_func(case)
new_result = unified_parse(case)
assert old_result == new_result
def test_no_remaining_duplicates():
clones = find_code_clones(threshold=0.8)
assert len(clones) == 0
Type: List[ExtractionPlan]
Question: What code should split out?
Resolution:
extractions = [
{
"source": "monolith.py:process_and_send",
"extract": ["process", "send"],
"reason": "Two responsibilities",
"new_modules": ["processor.py", "sender.py"]
}
]
Validation:
Type: List[str]
Question: What dead code should be removed?
Resolution:
dead_code = [
"old_api.py", # Replaced by new_api.py
"legacy_parser.py", # No callers
"deprecated_utils.py" # Unused for 6 months
]
Validation:
def test_no_callers():
"""Ensure code marked dead has no callers"""
for dead_file in dead_code:
callers = find_callers(dead_file)
assert len(callers) == 0, f"{dead_file} still called by {callers}"
Type: TestStrategy
Question: How do we validate equivalence?
Resolution:
strategy = {
"characterization": {
"coverage": "All public APIs",
"location": "tests/characterization/",
"approach": "Capture current behavior as baselines"
},
"refactor_validation": {
"coverage": "Each hole resolution",
"location": "tests/refactor/",
"approach": "TDD - write tests before refactoring"
},
"integration": {
"coverage": "End-to-end workflows",
"location": "tests/integration/",
"approach": "Compare old vs new on real scenarios"
}
}
Validation:
Type: MigrationPlan
Question: How do we safely transition?
Resolution:
plan = {
"phase1": "Deploy refactored code behind feature flag",
"phase2": "Enable for 10% traffic, monitor",
"phase3": "Ramp to 100% over 2 weeks",
"rollback": "Disable feature flag, revert deployment"
}
Validation:
Type: RollbackStrategy
Question: How do we undo if needed?
Resolution:
rollback = {
"code": "Keep old branch, feature flag controls routing",
"data": "No schema changes during refactor",
"config": "Feature flag toggle takes effect immediately"
}
Validation:
def test_rollback():
"""Ensure can revert to old version"""
enable_refactor()
assert uses_new_code()
disable_refactor()
assert uses_old_code()
# Both versions produce same results
assert old_results == new_results
Type: FeatureFlagSchema
Question: How to control rollout?
Resolution:
flags = {
"use_refactored_parser": {
"type": "boolean",
"default": False,
"rollout_strategy": "percentage",
"monitoring": ["error_rate", "latency"]
}
}
Type: CompatibilityMatrix
Question: What must remain compatible?
Resolution:
compatibility = {
"api": "All endpoints preserve signatures",
"data": "No schema changes",
"config": "All existing configs supported",
"behavior": "Same outputs for same inputs"
}
Validation:
def test_api_compatibility():
"""All old API calls work identically"""
for endpoint, test_cases in api_tests.items():
for case in test_cases:
old = call_old_api(endpoint, case)
new = call_new_api(endpoint, case)
assert old == new
Type: MonitoringPlan
Question: How do we detect regressions?
Resolution:
monitoring = {
"metrics": [
"request_latency_p50",
"request_latency_p99",
"error_rate",
"throughput"
],
"alerts": [
"error_rate > baseline * 1.1",
"latency_p99 > baseline * 1.2"
]
}
For any hole: