Skip to the content.
The Trap of Over-Abstraction: When Less Is More | AI Systems Design From Scratch

Connect with Amin Boulouma Official

🏠 Documentation Hub 📝 Engineering Blog 💻 GitHub Repository

Over-abstracted Data Piping

Amin Boulouma, Software Engineer

In software engineering, we are taught to avoid duplication and abstract logic into managers. However, there is a point where abstraction becomes a liability. A classic example is the DependenciesManager class that exists solely to return a 4-tuple, which is then immediately unpacked into a target function.

This is over-abstraction: it adds a layer of indirection that provides zero functional value while significantly increasing the cognitive load for anyone reading your code.

The Problem: Obscured Data Flow

When a class is nothing more than a wrapper for a data structure (the 4-tuple), it creates a “hidden” dependency. The reader must jump between files to see what the DependenciesManager returns, only to find that it was just a simple grouping of variables that could have been handled explicitly.

The Over-Abstracted Pattern

# Unnecessary complexity
class DependenciesManager:
    def get_deps(self):
        return (config, db, logger, cache)

# In the main flow:
# The reader has to trace back to DependenciesManager to know what these 4 items are.
config, db, logger, cache = manager.get_deps()
Jekyll.generate_site(*args) 

The Solution: Explicit Data Handling

If your “manager” isn’t managing state or logic, delete it. Replacing the over-abstraction with explicit variable initialization or a simple dataclass makes your code self-documenting and immediately readable.

The Simplified Approach

from dataclasses import dataclass

@dataclass
class SiteDependencies:
    config: Config
    db: Database
    logger: Logger
    cache: Cache

# Now the flow is explicit:
deps = SiteDependencies(config, db, logger, cache)
Jekyll.generate_site(deps)

Why Simplicity Wins

  1. Reduced Cognitive Load: When you look at the SiteDependencies dataclass, you know exactly what data is being passed without needing to jump to a “Manager” class.
  2. Type Safety: By using a dataclass or a NamedTuple, you get IDE autocompletion and static type checking that a generic tuple unpacking (*) completely hides.
  3. Readability: The code now documents its own intent. You aren’t “piping” data; you are passing a well-defined object.

Best Practices for Evaluating Abstractions

By stripping away the unnecessary wrappers, you make your codebase more approachable and easier to debug. Sometimes, the best code is the code you decide not to write.

Connect with Amin Boulouma Official