ReviseAlgo Logo

LLD Interview Framework

Clean Code & Refactoring

Master clean code principles (DRY, KISS, YAGNI), identify common code smells, and apply refactoring techniques to write highly maintainable software.

Last Updated: June 26, 2026 24 min read

Writing working code is only 50% of the battle. In the software industry, code is read far more often than it is written. LLD interviewers grade you heavily on code hygiene, readability, testability, and how easily your design can adapt to changing requirements.

1. Learning Objectives

  • Identify common code smells like Primitive Obsession, Feature Envy, and Monolithic Classes.
  • Apply clean coding principles: DRY (Don't Repeat Yourself), KISS (Keep It Simple, Stupid), and YAGNI (You Aren't Gonna Need It).
  • Perform core refactoring techniques: Extract Method, Introduce Parameter Object, and Dependency Injection.
  • Use polymorphism (Strategy/State patterns) to eliminate nested if-else and switch blocks.
  • Refactor tightly coupled, hardcoded classes into loosely coupled, testable modules.

2. Problem & Naive Solution

Suppose you are building a billing system. When an invoice is processed, the system must calculate tax, print details, and save the invoice data to a local database.

The Naive Solution (Monolithic & Smelly Code)

A developer writes a single class that manages database connections, prints invoices, and calculates taxes directly:

3. Issues with the Naive Approach

  • Violates Single Responsibility Principle (SRP): The Invoice class handles business rules (tax calculation), presentation logic (printing), and data persistence (database queries). Any change to the print format or database schema forces modifications to this class, risking regressions.
  • Tightly Coupled Dependencies: The database connection logic is hardcoded inside the processInvoice() method. This makes it impossible to unit test the class without a live database connection or to mock database calls.
  • Violates Open/Closed Principle: If you need to add support for a different database (e.g., PostgreSQL) or print format, you must modify the core Invoice class.

4. Real-world Analogy

Think of a restaurant kitchen:

* Smelly Kitchen: A single chef does everything: takes customer orders, cooks the meals, washes the dishes, and processes payments. If the restaurant gets busy, the chef becomes a bottleneck, dishes pile up, and orders are delayed.
* Clean Kitchen: The kitchen is divided into separate, specialized roles: a host takes orders, a line cook prepares the food, a dishwasher cleans, and a cashier handles payments. Each person works independently, allowing the restaurant to scale smoothly.

5. Theory: Code Smells, Principles & Refactoring

Let's explore the core concepts of clean code and refactoring:

A. Common Code Smells

  • Primitive Obsession: Using basic data types (like integers or strings) to represent complex concepts (e.g., using a raw string for phone numbers or email addresses). *Solution: Create dedicated value objects (PhoneNumber, Email).*
  • Feature Envy: A method in Class A that constantly calls getter methods in Class B to perform calculations. This indicates the method should be moved to Class B.
  • Long Parameter List: Passing more than 3-4 arguments to a method. *Solution: Wrap the parameters in a single parameter object.*

B. Clean Code Principles

  • DRY (Don't Repeat Yourself): Extract duplicate logic into helper methods or utility classes.
  • KISS (Keep It Simple, Stupid): Avoid over-engineering. Do not apply complex design patterns where a simple conditional statement is sufficient.
  • YAGNI (You Aren't Gonna Need It): Do not write code or features based on future requirements that might never happen. Focus on delivering the current scope cleanly.

C. Dependency Injection (DI)

Instead of hardcoding dependencies inside a class, inject them via the class constructor:

This decouples the classes, allowing you to pass mock implementations (e.g., MockInvoiceRepository) during unit testing without needing a database connection.

6. The Refactoring Loop

This diagram shows the iterative cycle of refactoring code safely.

7. Syntax Explanation

Languages provide features to enforce clean coding interfaces:

  • Java: Declares fields as final to guarantee immutability, and uses Interfaces to establish clean, pluggable abstraction boundaries.
  • Python: Leverages dynamic typing and duck typing. Interfaces are established using Abstract Base Classes (ABCs) via the abc library.
  • C++: Uses pure virtual classes (interfaces) and manages resource lifecycles using smart pointers (std::shared_ptr, std::unique_ptr).

8. Step-by-Step Implementation

  1. Identify Class Responsibilities: Analyze monolithic classes and separate business logic from presentation and persistence details.
  2. Extract Interfaces: Define clean interfaces for external dependencies (e.g., DatabaseConnection, NotificationSender).
  3. Refactor to Constructor Injection: Modify the main class constructor to accept these interfaces as dependencies.
  4. Split Methods: Break down long methods into smaller, single-purpose helper functions.
  5. Verify with Mocks: Write unit tests passing mock implementations to verify the design is testable and decoupled.

9. Complete Code (Invoice Billing System Refactoring Case Study)

Below is the refactored, clean, and testable version of the invoice billing system, decoupled into interfaces and single-responsibility classes.

10. Code Walkthrough

  • Single Responsibility Alignment: We extracted printing and database operations out of the Invoice class. The Invoice class now only manages invoice state and calculates taxes, which is its core business rule.
  • Constructor Injection Decoupling: The InvoiceBillingService accepts InvoiceRepository and InvoicePrinter interfaces in its constructor. The class does not instantiate MySqlInvoiceRepository directly, making it decoupled and testable.

11. Best Practices

  • Write Self-Documenting Code: Use meaningful class, method, and variable names. Readable code should not require extensive comments to explain *what* it does, only *why* it does it.
  • Prefer Composition Over Inheritance: Build classes using composition (has-a) rather than inheritance (is-a) to prevent tight coupling and deep class hierarchies.
  • Write Small Methods: Keep methods short (ideally under 15 lines). If a method gets too long, extract logic into smaller helper functions.

12. Common Mistakes

  • Primitive Obsession: Using basic data types for complex domain concepts (e.g., passing a double array to represent geolocation coordinates, instead of a dedicated Location class).
  • Viscous/Rigid Code: Writing code where making a change in one class requires modifying multiple other classes, indicating tight coupling.

13. Interview Discussion

Q: What is the difference between DRY and WET code?
Answer: - DRY (Don't Repeat Yourself): A design principle that aims to reduce duplication of code by extracting common logic into reusable methods or modules. - WET (Write Everything Twice / We Enjoy Typing): Smelly code containing duplicate copy-paste logic, which increases maintenance costs.
Q: How does Dependency Injection improve code testability?
Answer: By injecting dependencies via constructors rather than hardcoding them, you can pass mock objects (e.g., MockRepository) during testing. This allows you to test class behavior in isolation without needing external systems like database connections or network sockets.
Q: What is Feature Envy?
Answer: Feature Envy is a code smell where a method in Class A excessively accesses the fields and methods of Class B to perform calculations, indicating the method should be moved to Class B.

14. Practice Exercises

  • Easy: Identify and refactor a method with a long parameter list (e.g., 6 primitive variables) using a parameter object.
  • Medium: Refactor a program containing deeply nested if-else blocks (representing payment type fee calculations) using the Strategy Pattern.
  • Hard: Take a legacy, tightly coupled e-commerce order system and refactor it into clean, testable classes using dependency injection and interfaces.

15. Challenge Problem

Refactor a Legacy Transaction Processor. The legacy class reads transaction logs, validates details using hardcoded rules, updates a database, and sends email alerts. It contains nested loops, raw database connection strings, and is completely un-testable. Break it down into single-responsibility classes, define clean interfaces, and implement the refactored, testable version in Java, Python, or C++.

16. Summary & Cheat Sheet

  • Clean Code is readable, self-documenting, and easy to maintain.
  • Single Responsibility ensures that each class has exactly one reason to change.
  • Dependency Injection enables testability by decoupling classes from their dependencies.
  • Follow DRY, KISS, and YAGNI to avoid duplication and over-engineering.

17. Quiz

1. Which code smell describes using basic data types for complex domain concepts?

A) Feature Envy
B) Primitive Obsession (Correct)
C) Large Class

2. What does the DRY principle stand for?

A) Do Repetitive Yields
B) Don't Repeat Yourself (Correct)
C) Design Robust Yields

3. How does Dependency Injection improve class testability?

A) It compiles code faster
B) It allows passing mock objects during testing, decoupling the class from live systems (Correct)
C) It reduces class memory footprint

4. What is "Feature Envy"?

A) A class with too many methods
B) A method that excessively accesses another class's fields to perform calculations (Correct)
C) A type of compiler warning

5. Which principle is violated by having a single class print reports, calculate taxes, and save data?

A) Interface Segregation Principle
B) Single Responsibility Principle (Correct)
C) Dependency Inversion Principle

6. What does YAGNI stand for?

A) You Aren't Gonna Need It (Correct)
B) Yield All Good Node Indices
C) Yet Another Garbage Node Index

7. What is the benefit of keeping method lengths under 15 lines?

A) It speeds up the CPU
B) It improves readability and makes the method easier to test and maintain (Correct)
C) It uses fewer registers

8. Which refactoring technique extracts common code blocks into a new method?

A) Introduce Parameter Object
B) Extract Method (Correct)
C) Inline Method

9. In C++, what smart pointer is best suited to manage exclusive ownership of a dependency?

A) std::shared_ptr
B) std::unique_ptr (Correct)
C) std::weak_ptr

10. What is "Refactoring"?

A) Rewriting the entire application from scratch
B) Restructuring existing code without changing its external behavior to improve readability and maintainability (Correct)
C) Adding new features to the code

18. Next Lesson Preview

This completes the LLD Interview Framework module. In the next module, we will explore LLD Case Studies, where we will apply these frameworks and clean-coding principles to design complete real-world systems like a Parking Lot, a Vending Machine, and a Chess Game!