Small changes yield unexpected problems.
TL;DR: If small changes have big impact, you need to decouple your system.
Problems
High Coupling
Low maintainability
Side effects
High risk
Testing difficulty
Solutions
- Decouple your components.
- Cover with tests.
- Refactor and isolate what is changing.
- Depend on interfaces.
https://maximilianocontieri.com/how-to-decouple-a-legacy-system
Refactorings ⚙️
https://maximilianocontieri.com/refactoring-007-extract-class
https://maximilianocontieri.com/refactoring-024-replace-global-variables-with-dependency-injection
Examples
Context
The ripple effect happens when you design a system where objects know too much about each other.
When you modify a specific behavior, the impact spreads through the codebase like a stone thrown into a pond.
You feel this pain when a simple requirement change requires you to touch dozens of files.
Your classes have direct dependencies on concrete implementations rather than abstractions.
Sample Code
Wrong
class Time {
constructor(hour, minute, seconds) {
this.hour = hour;
this.minute = minute;
this.seconds = seconds;
}
now() {
// call operating system
}
}
// Adding a TimeZone will have a big Ripple Effect
// Changing now() to consider timezone will also bring the effect
Right
class Time {
constructor(hour, minute, seconds, timezone) {
this.hour = hour;
this.minute = minute;
this.seconds = seconds;
this.timezone = timezone;
}
// Removed now() since is invalid without context
}
class RelativeClock {
constructor(timezone) {
this.timezone = timezone;
}
now(timezone) {
var localSystemTime = this.localSystemTime();
var localSystemTimezone = this.localSystemTimezone();
// Do some math translating timezones
// ...
return new Time(..., timezone);
}
}
Detection
It is not easy to detect problems before they happen.
Mutation Testing and root cause analysis of single points of failures may help.
Level
[x] Intermediate
Why the Bijection Is Important ️
In a proper bijection, a change in a single real-world concept should only lead to a change in a single program component.
When you break the MAPPER , one concept spreads across your code.
This creates the ripple effect because you didn't represent the original idea as a single, isolated unit.
AI Generation
AI generators often create this smell because they suggest "quick fixes" that access global states or direct dependencies.
They focus on making the local code work without seeing the architectural ripple they cause elsewhere.
AI Detection
AI can fix this if you provide the context of the related classes.
When you ask an AI to "decouple these two classes using dependency injection," it usually does a great job of breaking the link.
Try Them!
Remember: AI Assistants make lots of mistakes
Suggested Prompt: Refactor this class to remove direct dependencies on global objects. Use constructor-based dependency injection and depend on interfaces or abstractions instead of concrete implementations.
Conclusion
There are multiple strategies to deal with Legacy and coupled systems.
You should deal with this problem before it explodes under your eyes.
Relations ❤️
https://maximilianocontieri.com/code-smell-08-long-chains-of-collaborations
https://maximilianocontieri.com/code-smell-176-changes-in-essence
https://maximilianocontieri.com/how-to-decouple-a-legacy-system
Credits
Photo by Jack Tindall on Unsplash
Architecture is the tension between coupling and cohesion.
Neal Ford
https://maximilianocontieri.com/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