Why Your Code is a “Control Freak”
How to Fix It
As developers, we usually optimize for one thing:
making the feature work today.
But in real systems, the real question is different:
How easy is this feature to change tomorrow?
That’s where most designs start to break.
Over time, we accidentally build systems that look solid on the surface, but are actually fragile underneath, small changes ripple everywhere, and testing becomes painful.
A lot of this starts from one simple keyword we use all the time:
new
It looks harmless. But in many cases, it quietly pushes your architecture toward something rigid and hard to change.
Your Code Might Be a “Control Freak”
In clean architecture terms, a “Control Freak” class is one that doesn’t just use its dependencies, it creates and controls them completely.
For example:
var db = new SqlServerDatabase();
At first glance, nothing special.
But now this class:
decides what database to use
controls how it is created
controls how long it lives
That creates two real problems:
1. Rigidity
You are now locked into one implementation.
Switching database or changing behavior means modifying the class itself.
2. Lifetime Control Issues
The class decides object lifetime instead of the system.
This leads to inefficient resource usage and harder state management.
A simple way to think about it:
You didn’t just use a database, you glued it into your class.
The Lamp Problem
Imagine a desk lamp where the bulb is permanently glued inside.
It works fine… until it breaks.
Then you can’t replace just the bulb, you replace the entire lamp.
That’s exactly what happens in tightly coupled code.
Now compare that with a normal lamp:
You can swap bulbs
You can upgrade them
You can experiment freely
That flexibility is what we call loose coupling.
The Fix: Dependency Injection
Instead of creating dependencies inside a class, you pass them in from the outside.
So instead of:
var service = new SqlDatabase();
You do:
public Lamp(ILightBulb bulb)
Now the class:
doesn’t care about implementation
only depends on a contract (interface)
This is what enables Inversion of Control.
A central system (container) decides what gets injected, not your class.
Why This Matters in Practice
Once you do this, a few important things become easier:
Testing becomes simple
You can replace real dependencies with fake ones.Changes become local
You don’t break unrelated parts of the system.Flexibility increases
You can swap implementations without rewriting logic.
Not Every “new” is Bad
The goal is not to avoid new completely.
Some things are perfectly fine to create directly:
DTOs (simple data objects)
String, DateTime, List
Internal utility objects
Because they are stable and predictable, not external systems.
The Real Rule
Design patterns are not rules.
They are responses to pain.
If something becomes hard to test, hard to change, or hard to reason about, that’s when you introduce structure.
Otherwise, you’re just over-engineering.
Final Thought
Good architecture is not about adding more patterns.
It’s about delaying decisions until the right moment.
So next time you type:
new
pause for a second and ask:
Am I keeping this flexible… or am I gluing it in?



