Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Class interfaces present one cohesive abstraction - don't mix domain logic with serialization, persistence, or unrelated concerns
when_to_use
When designing any class interface. When class has mixed responsibilities. When class groups unrelated functions. When domain object knows about JSON/XML/database. When class description has multiple purposes. When interface mixes high and low level operations. When temporal cohesion exists (grouped by when, not what). When reviewing classes for abstraction quality. When creating grab-bag utility classes. When mixing serialization with domain logic. When persistence mixed with business logic. When class difficult to name clearly. When cohesion is weak. When single responsibility violated at class level. When Abstract Data Type unclear.
version
1.0.0
languages
all
Maintaining Consistent Abstractions
Overview
A class interface should present ONE cohesive abstraction. All methods should work toward a consistent purpose at a consistent level.
Core principle: Each class implements one Abstract Data Type (ADT). If you can't identify what ADT the class implements, it has poor abstraction.
Goal: Anyone using the class should see a clear, consistent set of related operations, not a miscellaneous grab-bag.
When to Use
Apply when designing any class:
New class design
Reviewing existing classes
Refactoring
API design
Warning signs of poor abstraction:
Class groups unrelated functions
Methods at different abstraction levels (high-level + low-level mixed)
Domain object with serialization methods (to_json, to_xml)
Business logic mixed with persistence (SQL in domain class)
Temporal cohesion (things done at same time, not related by purpose)
Can't clearly state what abstraction the class represents
Class description has "and" connecting unrelated purposes
classProgram:
"""Initialize application components."""def_init_database(self): # Database concernpassdef_setup_web_server(self): # Web concernpassdef_start_background_jobs(self): # Jobs concernpassdef_init_command_stack(self): # Command concernpassdef_init_report_formatter(self): # Reports concernpass
Problem: These are unrelated functions grouped because they happen at startup (temporal cohesion). The class has no consistent abstraction - it's a miscellaneous collection.
Code Complete specifically calls this out as poor abstraction.
✅ Each subsystem initializes itself:
classDatabaseSystem:
"""Abstraction: Database operations."""definitialize(self):
# Database-specific initializationpassclassWebServer:
"""Abstraction: Web serving."""defstart(self):
# Web server initializationpassclassBackgroundJobManager:
"""Abstraction: Job processing."""defstart(self):
# Job system initializationpass# Coordinator stays high-levelclassApplication:
def__init__(self):
self.database = DatabaseSystem()
self.web_server = WebServer()
self.jobs = BackgroundJobManager()
defstart(self):
# High-level orchestrationself.database.initialize()
self.web_server.start()
self.jobs.start()
Now: Each class has consistent abstraction. Program no longer a grab-bag.
Anti-Pattern 3: Business Logic + Persistence Mixed
Baseline violation:
classDataProcessor:
"""Mixes data access with statistics."""defprocess_dataset(self, dataset_id):
# Loads from PostgreSQL (persistence concern)
values = self._load_dataset(dataset_id)
# Calculates statistics (business logic concern)
mean = statistics.mean(values)
# Two concerns in one class
Problem: Class does two things - data access AND statistics. If you switch from PostgreSQL to MongoDB, you must modify this class. If you change statistical algorithm, you modify same class.
✅ Separate concerns:
classDatasetRepository:
"""Abstraction: Dataset storage/retrieval."""defget_dataset_values(self, dataset_id: str) -> list[float]:
# PostgreSQL details hidden here# Can switch to MongoDB without affecting calculatorpassclassStatisticsCalculator:
"""Abstraction: Statistical computations."""defcalculate_metrics(self, values: list[float]) -> dict:
# Pure calculation, no database knowledge
mean = statistics.mean(values)
median = statistics.median(values)
# Returns statistics onlypassclassDataProcessor:
"""Abstraction: Orchestration."""def__init__(self, repository, calculator):
self._repository = repository
self._calculator = calculator
defprocess_dataset(self, dataset_id: str) -> dict:
# High-level only - delegates to focused abstractions
values = self._repository.get_dataset_values(dataset_id)
returnself._calculator.calculate_metrics(values)
Now: Each class has single, consistent abstraction. Database changes don't affect calculator. Algorithm changes don't affect repository.
The Abstraction Levels Test
For any class, ask:
What abstraction does this class represent?
Can state it in one sentence?
All methods work toward that one purpose?
Are all methods at same abstraction level?
All high-level (orchestration)?
All low-level (implementation)?
Or mixed (violation)?
Do methods belong together?
Related by purpose (functional cohesion)?
Or just coincidentally grouped (temporal/coincidental)?
If answers reveal inconsistency → poor abstraction.