I learned abstraction through examples of rectangles and lines, like probably many of you did. I spent time thinking abstractly about abstraction based on class names and story titles.
This was a mistake. It’s the same kind of mistake we make when we copy solutions from other companies without understanding the problems they were meant to solve.
The Pedagogy Problem
Computer science education teaches abstraction through geometric shapes and animals:
Now your axe can implement Damaging
and Durable
, while your pickaxe implements Damaging
and Upgradeable
, without forcing them into an artificial hierarchy.
Composition Over Inheritance
This is why “composition over inheritance” is such powerful advice.
Inheritance says: “This IS A that” Composition says: “This HAS A that” or “This CAN DO that”
Composition is more flexible because:
- You can combine behaviors independently
- You can change implementations without affecting interfaces
- You avoid deep inheritance hierarchies
- You make dependencies explicit
The Abstraction Test
Before creating an abstraction, ask:
The Behavior Test
- Do these things actually behave the same way?
- Will they evolve in the same direction?
- Do they have the same lifecycle?
The Change Test
- When requirements change, will these things change together?
- Or will changes affect them differently?
The Coupling Test
- Does this abstraction create useful decoupling?
- Or does it create artificial coupling?
Red Flags for Bad Abstractions
Method names that don’t make sense for all implementations:
Lots of empty or default implementations:
Frequently checking types at runtime:
When to Create Abstractions
Good times:
- Multiple implementations that truly behave the same way
- You need to swap implementations at runtime
- You’re building a plugin system or framework
- The abstraction eliminates meaningful code duplication
Bad times:
- You have only one implementation (YAGNI)
- Implementations have fundamentally different behaviors
- You’re just trying to make the code “more object-oriented”
- You’re modeling real-world relationships instead of software behaviors
The Evolution Strategy
- Start with concrete implementations
- Let duplication emerge naturally
- When you have 3+ similar implementations, consider abstraction
- Extract based on actual shared behavior, not theoretical similarity
- Be willing to break apart abstractions that become unwieldy
Remember: Abstractions Are for Humans
The computer doesn’t care about your class hierarchy. Abstractions exist to help humans understand and modify code.
If your abstraction makes the code harder to understand or change, it’s not helping — regardless of how theoretically “correct” it might be.
Sometimes the best abstraction is no abstraction at all.
Focus on making your code clear, flexible, and maintainable. Good abstractions will emerge naturally from these goals.