5 Steps to Clean Code

Make software designs more understandable, flexible, and maintainable.

Ajesh Kalayil
4 min readSep 1, 2021
Photo by Danial Igdery on Unsplash

Technical debt is one of the biggest challenges facing by most of the teams in the software industry. Today here, I’m going to discuss how to make our software architecture clean by applying the SOLID design principle to manage technical debt.

What is SOLID?

SOLID is a mnemonic acronym for five software design principles to develop understandable, flexible, and maintainable software. First introduced by Robert C. Martin also know as Uncle Bob in his 2000 paper Design Principles and Design Patterns

The 5 pillars of SOLID design principles are

  • Single Responsibility
  • Open closed
  • Liskov substitution
  • Interface segregation
  • Dependency inversion

Let’s understand the problems in software architecture and how the problems will be solved by the above principle.

Single Responsibility principle

In a nutshell, Single responsibility states to do one thing

For example, if class A handles both data access logic and business logic. Split the class into A and B. Let class A handle data access logic and class B handle business logic.

Similarly, function X does file reading and validating. Split the function into X and Y. Let function X read and function Y validate the files.

The core of this principle is there should be only one reason to change a module or class or function.

Open Closed Principle

Closed for modifications and open for extensions

A Developer X implemented mail sending functionality with cc in class A. A few years later developer Y wants to send mails with bcc also. At that time developer Y depends on the implementation details of class A, as well as he, would force to make changes in class A that may cause functionality breaking for clients who are using this class A in their application.

To avoid this developer X should create an interface and implements that interface for mail sending with cc at class A. A few years later developer Y can implement the same interface for mail sending with bcc without breaking the existing functionality.

Earlier in 1988’s Bertrand Mayer proposes to use inheritance to achieve this goal in his book Object-Oriented Software Construction. But inheritance introduces tight coupling if the subclasses depend on implementation details of their parent class. That’s why Robert C. Martin and others redefined the Open/Closed Principle to the Polymorphic Open/Closed Principle.

Liskov Substitution principle

Objects of superclass shall be replaced by the object of base class without breaking the application

For example, If Crow is the base class of Bird. Then the object of the superclass Bird shall be replaced by the object of base class Crow.

Interface segregation principle

No client should be forced to depends on the methods it does not use

For example, Developer X has an interface A that has methods for reading and writing to the file. Developer Y wants to implement interface A for writing to a PDF file, but he would force to implement the read method even if he doesn’t need it, this sounds rude, right?

To avoid this, according to ISP, split the interface into A and B. A for reading the file and B for writing to the file. So that developer X can implement both the A and B interfaces for reading and writing to the file. At the same time, developer Y can implement interface B for writing to the file without depends on the unused read method.

Dependency inversion principle

Depending on interfaces is less risky than depending on concrete implementations

High-level modules should not depend on low-level modules, both should depend on abstraction. Abstractions should not depend on details (concrete implementation). Details should depend on abstractions

Let’s understand what problem it solves through an example.

An Owner has a Doberman dog, he feeds the Doberman dog as needed. Let's tell the same story through java code.

One day owner plans to adopt a Husky dog, but the owner's feeding knowledge is only limited to Doberman due to the dependency on the Doberman breed. So the owner determined to expand his knowledge in dog feeding rather than sticking on a particular breed to adopt Huskey shown below.

Now the owner depends on the abstraction Dog rather than any concrete implementation like Doberman.

Quick Recap

  • There should be one and only one reason to change a class or function.
  • Classes and functions should close for modifications and open for extensions.
  • Objects of the superclass should be replaced by the object of the base class without breaking the application.
  • No client should be forced to implement the method it doesn't use at all.
  • Depending on the abstraction is less risky than depending on the concrete implementation.

Final Thoughts

If we are managed to follow SOLID design principles throughout our code we can reduce the technical debt.

If you have any doubts or corrections please feel free to drop a text on the comment box.
If you enjoyed this blog post, share it with a friend!

Thanks, Good day everyone.