Don't turn collaborators into permanent roommates
TL;DR: You should avoid storing stateless utility classes as instance variables initialized with new.
Problems
Hardcoded dependencies
Testing difficulties
High coupling
Hidden side effects
Rigid design
Misleading intent
Premature Optimization
Stack clutter
Solutions
Use dependency injection
Pass as parameter
Use static methods
Inline the logic
Use local variables
Inline object creation
Refactorings ⚙️
https://maximilianocontieri.com/refactoring-024-replace-global-variables-with-dependency-injection
https://maximilianocontieri.com/refactoring-030-inline-attributes
https://coderlegion.com/13328/refactoring-007-extract-class
Context
Hardcoding a stateless class in the constructor creates permanent coupling.
Even if the class is cheap to instantiate, you lose the ability to swap it.
Stateless objects shouldn't be part of the object's internal state.
You confuse readers by making a tool look essential to the object's identity.
It makes testing harder because you can't mock the hardcoded dependency.
Sample Code
Wrong
class UserProcessor {
private provider: MockDataProvider;
constructor() {
// You hardcode the dependency here.
// This makes the class harder to test.
this.provider = new MockDataProvider();
}
process(data: any) {
return this.provider.format(data);
}
}
Right
interface DataProvider {
format(data: any): any;
}
class UserProcessor {
// You inject the dependency via constructor.
// Now you can swap it or mock it easily.
constructor(private readonly provider: DataProvider) {}
process(data: any) {
return this.provider.format(data);
}
}
// Simpler but coupled
class UserProcessor {
constructor() {
// Empty
}
process(data: any) {
return new MockDataProvider().format(data);
}
}
Detection
Look for the new keyword inside constructors.
Watch for private properties instantiated directly in the constructor rather than passed as parameters.
Most linters flag this pattern automatically when you create instances and assign them to private fields.
Level
[X] Beginner
Why the Bijection Is Important ️
Software should mimic a MAPPER of the real world.
In reality, a worker might use a tool to complete a task.
The tool is not a permanent physical attachment to the worker.
When you refactor to use dependency injection, you respect the bijection by treating collaborators as external entities, not internal state.
This keeps your simulation flexible and accurate.
AI Generation
AI generators frequently create this smell.
They often suggest code that just works by instancing dependencies directly in the constructor to save time.
AI Detection
AI can easily detect this smell without explicit instructions.
When you show AI a class with new keywords in the constructor, it recognizes the pattern as hardcoded coupling.
AI identifies that stateless utility classes should be injected rather than instantiated internally.
The detection is straightforward because the pattern is syntactically obvious and semantically harmful.
Try Them!
Remember: AI Assistants make lots of mistakes
Suggested Prompt: remove the cached attribute
Conclusion
Storing stateless dependencies as instance variables makes your code rigid.
When you inject these dependencies instead, you improve testability and keep your objects focused on their true purpose.
Relations ❤️
https://coderlegion.com/8216/code-smell-06-too-clever-programmer
https://maximilianocontieri.com/code-smell-20-premature-optimization
https://coderlegion.com/6634/coupling-the-one-and-only-software-design-problem
Disclaimer
Code Smells are my opinion.
Credits
Photo by Possessed Photography on Unsplash
Coupling is the enemy of change
Rich Hickey
https://coderlegion.com/13786/software-engineering-great-quotes
This article is part of the CodeSmell Series.
https://coderlegion.com/10942/how-to-find-the-stinky-parts-of-your-code