In the previous article, we answered an important question:
What does SOLID actually solve?
We saw that SOLID helps us design software that is easier to understand, maintain, test, and extend by improving the quality of individual classes and their relationships.
But software engineering doesn't stop there.
Even when every class follows SOLID principles, experienced engineers continue to encounter a different category of problems.
These problems don't indicate that SOLID has failed.
They simply belong to a different level of design.
Understanding this distinction is one of the biggest mindset shifts in becoming a better software engineer.
Good Design Doesn't Mean Every Problem Disappears
Imagine you've built an e-commerce application.
You've carefully applied SOLID.
- Every class has a single responsibility.
- Dependencies are injected through interfaces.
- Interfaces are small and focused.
- The code is easy to test.
Everything looks clean.
Then the product team starts introducing new features.
- Customers can pay using different payment providers.
- Notifications can be sent through Email, SMS, Push, or WhatsApp.
- Shipping partners vary by country.
- Premium customers receive different pricing.
- Recommendations use different algorithms.
Your classes are still well designed.
But new questions begin to appear.
Notice that these questions aren't about making a class smaller or splitting responsibilities.
They're about how multiple well-designed classes should work together.
SOLID Doesn't Decide How Objects Should Be Created
Suppose your application supports several payment gateways.
Stripe
PayPal
Razorpay
Apple Pay
A business rule says:
"Choose the appropriate payment provider based on the customer's selection."
Where should that decision live?
Should every service contain a long if-else statement?
Should each class create payment objects itself?
Should object creation be centralized?
These are important design questions.
SOLID doesn't prescribe the answer.
That's because object creation is a different design concern.
SOLID Doesn't Decide How Behavior Should Change
Imagine a ride-sharing application.
Pricing depends on many factors:
- Standard ride
- Premium ride
- Airport ride
- Peak hours
- Festival pricing
Initially, the pricing logic is simple.
As new pricing rules arrive, the calculation becomes increasingly complex.
Now ask yourself:
How should the system switch between different pricing algorithms without constantly modifying existing code?
SOLID encourages extensibility.
But it doesn't define the mechanism for organizing interchangeable behaviors.
SOLID Doesn't Decide How Independent Components Communicate
Think about an online shopping platform.
When an order is placed, many things happen.
Order Created
│
├── Inventory Updated
├── Payment Confirmed
├── Loyalty Points Added
├── Notification Sent
└── Analytics Recorded
Should the OrderService directly call every other service?
If another feature is introduced tomorrow, should it be modified again?
Soon one service starts knowing too much about the rest of the system.
The issue isn't responsibility anymore.
The issue is communication.
SOLID doesn't define a general strategy for organizing interactions between many collaborating objects.
SOLID Doesn't Solve Integration Problems
Suppose your company integrates with a shipping partner.
Your system expects:
calculateShippingCost()
The shipping partner provides:
getFreightEstimate()
Both systems are correct.
Both work perfectly.
The challenge is simply that they speak different "languages."
No SOLID principle explains how to bridge incompatible interfaces.
That requires a different kind of design thinking.
SOLID Doesn't Tell You How to Add Features Dynamically
Imagine a coffee ordering application.
Customers can customize drinks.
Coffee
Later they add:
Coffee
+ Milk
Then:
Coffee
+ Milk
+ Caramel
Then:
Coffee
+ Milk
+ Caramel
+ Whipped Cream
Should you create a new class for every possible combination?
What happens when the menu grows?
The challenge is no longer about responsibility.
It's about extending behavior in a flexible way.
A Pattern Begins to Emerge
Look back at all the previous examples.
Although they belong to different domains, they're asking surprisingly similar questions.
- Who should create objects?
- How should behavior change dynamically?
- How should components communicate?
- How should incompatible systems work together?
- How should features be added without creating an explosion of subclasses?
These questions appear in:
- banking systems
- e-commerce platforms
- ride-sharing applications
- hospital management systems
- inventory systems
- content platforms
The business domains are different.
The design problems are remarkably similar.
That observation changed the way experienced software engineers approached design.
Weak Thinking vs Strong Thinking
Weak Thinking
"Every new project has completely unique design challenges."
Strong Thinking
"Business requirements differ, but many underlying design problems repeat across projects."
Once you start recognizing recurring problems instead of isolated situations, you're thinking much more like an experienced engineer.
Common Beginner Mistake
Many developers believe that when their design starts becoming difficult, they simply haven't applied SOLID well enough.
Sometimes that's true.
But often, the responsibilities are already clear.
The real challenge lies elsewhere:
- coordinating objects,
- creating them,
- extending them,
- or allowing them to collaborate cleanly.
Trying to solve every problem with SOLID alone is like trying to fix every construction problem with better bricks.
Sometimes the challenge isn't the bricks.
It's the blueprint.
Interview Perspective
Interviewers often present scenarios that sound like business problems but are actually testing whether you recognize recurring design challenges.
For example:
- "We need to support multiple payment gateways."
- "Users can choose different notification channels."
- "The pricing algorithm changes frequently."
- "We're integrating with a third-party API."
They're not looking for pattern names immediately.
They're looking for whether you recognize the type of problem before proposing a solution.
Strong candidates identify the recurring design concern first.
Only then do they choose an appropriate pattern.
Key Insight
SOLID gives us principles for designing maintainable components.
As software grows, recurring challenges appear around object creation, behavior, communication, integration, and extension.
These aren't failures of SOLID.
They're simply different categories of design problems.
Understanding those recurring problems is what eventually led to the development of Design Patterns.
In This Article, You Learned
- Why well-designed software still encounters new design challenges.
- The categories of problems SOLID wasn't intended to solve.
- Why recurring design problems appear across completely different domains.
- Why recognizing the problem is more important than memorizing the solution.
One-Line Takeaway
SOLID builds strong components; Design Patterns help those components solve recurring collaboration and evolution problems.
Top comments (0)