0
0
LLDsystem_design~7 mins

SOLID violations and fixes in LLD - System Design Guide

Choose your learning style9 modes available
Problem Statement
When code mixes responsibilities, changes in one part cause unexpected bugs in others. Classes become hard to understand and maintain, slowing down development and increasing errors.
Solution
Apply SOLID principles to separate concerns, making each class responsible for a single task. This reduces dependencies and makes code easier to change, test, and extend without breaking existing functionality.
Architecture
BadDesign
(ManyRoles)
Problems
SOLIDApplied
(SingleRole)
Benefits

This diagram shows how a design with many roles in one class leads to problems, while applying SOLID principles separates roles and brings benefits.

Trade-offs
✓ Pros
Improves code readability by separating concerns clearly.
Makes testing easier by isolating functionality.
Facilitates adding new features without breaking existing code.
Reduces bugs caused by unintended side effects.
✗ Cons
Initial design takes more time to plan and implement.
May increase number of classes, which can feel complex at first.
Requires developers to understand SOLID principles well.
Use when codebase grows beyond a few hundred lines or when multiple developers work on the same code. Essential for projects expected to evolve over time.
Avoid in very small scripts or prototypes where speed of writing is more important than maintainability.
Real World Examples
Amazon
Amazon applies SOLID principles in their microservices to keep services focused and independently deployable, reducing downtime during updates.
Netflix
Netflix uses SOLID to design modular components in their streaming platform, enabling quick feature additions without affecting core playback.
Uber
Uber structures their ride matching logic using SOLID to isolate responsibilities, making it easier to scale and maintain complex business rules.
Code Example
The before code mixes validation, saving, and emailing in one class, violating the Single Responsibility Principle. The after code separates these concerns into distinct classes, making each easier to maintain and test.
LLD
### Before (Violating SOLID - Single Responsibility Principle)
class UserManager:
    def __init__(self, user_data):
        self.user_data = user_data

    def save_user(self):
        # code to save user to database
        pass

    def send_welcome_email(self):
        # code to send email
        pass

    def validate_user(self):
        # code to validate user data
        pass


### After (Applying SOLID - Single Responsibility Principle)
class UserValidator:
    def validate(self, user_data):
        # code to validate user data
        pass

class UserRepository:
    def save(self, user_data):
        # code to save user to database
        pass

class EmailService:
    def send_welcome_email(self, user_data):
        # code to send email
        pass


# Usage
user_data = {...}
validator = UserValidator()
if validator.validate(user_data):
    repo = UserRepository()
    repo.save(user_data)
    email_service = EmailService()
    email_service.send_welcome_email(user_data)
OutputSuccess
Alternatives
Monolithic Design
Combines all responsibilities in fewer classes or modules without strict separation.
Use when: Choose when building very small or short-lived applications where simplicity and speed are priorities.
Procedural Programming
Focuses on functions and procedures rather than objects and classes.
Use when: Choose when working on simple scripts or tasks that do not require object-oriented design.
Summary
Violating SOLID principles causes tightly coupled code that is hard to maintain and extend.
Applying SOLID separates responsibilities, improving code clarity, testability, and flexibility.
SOLID is essential for scalable projects but should be balanced with simplicity in small scripts.