SOLID principles of programming, but you’re 5 years old: Episode 1

Adham Saheb
6 min readMar 4, 2023

--

You see, I’ve always wanted people to explain things to me like I was completely illiterate, I love diagrams, black boxes and arrows moving around from one component to the other, as if I was 5 years old if you may.

But experienced engineers and technical tutors seem to enjoy demonstrating their complicated technical comprehension when asked to explain something 🥲 which is a sport I can’t play, and definitely not a fan of, with that said, over the course of the next few articles, I will try to explain SOLID principles of programming, as if you were a 5-year-old (Who knows how to code a bit).

Back when my goal was to get accepted into google, it seemed very easy to figure out what I needed to do to achieve this, and that’s excelling problem solving, and given that I was aiming for an entry level job, I didn’t have to study any system design interview questions, but even if I was aiming for a higher level job, I would have known that I needed to study system design as well. Now, hundreds of leetcode problems later, I am a Google engineer, and I’ve been so for about a year now, through which I’ve realized that it’s not as easy anymore to figure out what I need to do next, whether to develop as a software engineer, or get promoted, choose a speciality, etc …, all of this is still unknown to me.

What is for sure known though, is that no matter what objective to pursue next, any software engineer needs to know how to write clean, scalable and maintainable code and feature designs. The ABC of that would be to try to adhere as much as possible to SOLID principles, a set of five design principles that can help you achieve exactly that.

Before I talk about the first principle, I would like to leave you with a caveat, SOLID principles are not something you can learn and apply next day at work, this is not a tutorial, each principle takes some good amount of time to adhere to, and outstanding engineers still fail to never break any of them while designing something, the closest you are for a 5/5, the better the design, and the more considerate of applying SOLID to your designs, the more intuitive they become.

Single Responsibility (SRP)

Book definition

The single responsibility principle states that every component or module should have only one job to do or must be responsible for doing only one functionality. The other (and more important) definition would be that every component or module should only have one and only one reason to change.

5 year old definition

Let’s start with “component or module”, what is a module or a component in programming ? Basically it’s any type of building blocks of your code, regardless of its size, a helper method is a component, a class is a component, in simple backend for instance, an end point is a type of a component, in simple frontend, a component is a component …

Now focusing on “one reason to change”, which is the criteria you can use to detect violations of this principle, what does it mean ? Imagine if were the CEO of coca cola, and you have a person that is both your personal assistant and accountant, there would be 2 reasons why you might want to replace this person, they either failed doing their job as a PA, or failed doing their job as an accountant, this breaks SRP, if there you accountant only, failing their job as an accountant would be their only reason you would want to change them.

With that said, SRP is a principle that suggests making your components do one thing only, and encapsulating that one thing it does within its scope, so that when you either want to change how that thing is done, or if you discover a bug in that functionality, you know exactly where to look.

Example of violation

I will give an example that may not be as similar to the ones you’d see in other places explaining SRP, such as classes calculating both area and circumference of a shape, or a class the both calculates the price of an order and stores it in the database, but this one I can explain better since I went through in my previous job.

import org.apache.log4j.Logger;

public Class SomeService implements SomeAbstractService {

static Logger log4jLogger = Logger.getLogger(SomeService.class.getName());
// More dependencies here ...

public void doSomeLogic() {
// Business logic here ...
_privateBusinessLogicMethod();

log4jLogger.info("Something has just finished executing");
}

// More methods here ...
}

You can clearly tell, we loved logging things, which is not necessarily a bad practice, logs can be lifesavers in post mortems*, but can you tell what the issue is ? This service was responsible for doing business logic, as well as logging the operation. Which makes this service have 2 reasons to change:

1- Business logic requirements have changed.

2- Logging mechanism has changed, maybe we need to write the logs to the database instead ? Or use a logger that’s different from log4j, which is exactly what we needed to do.

At some point in 2021, **CVE-2021–44228 emerged, a security vulnerability that required the team to change all occurrences of log4j in our codebase immediately, you would anticipate this was an easy change, oh, it was ANYTHING BUT. We Iterated over every occurrence of log4j loggers in our codebase, changed the import path, the definition of the logger, and refactored the method we call to log, having hundreds of thousands lines of codes for multiple applications deployed over multiple remote repositories.

How would have this been easier ? If we had a dedicated service for logging operations and logging operations only, that would have been the only place we needed to look, and our business logic service would’ve looked like this:

public Class SomeService implements SomeAbstractService {

private final LoggingService logService;

SomeService(LoggingService logService) {
this.logService = logService;
}

public void doSomeLogic() {
// Business logic here ...
_privateBusinessLogicMethod();

logService.logInfo("Something has just finished executing");
}

// More methods here ...
}

Having the logging separate from this service makes it more hermetic and immune against outer changes, logging requirements have changed ? Logging service is the only place we need to change, and SomeService has no idea how logging works internally (and should never have). What was a multiple day effort, is now a 5 minute pull request.

In fact, this article you’re reading, is sort of applying SRP itself by discussing one principle only, if scientists decide to change the definition of SRP, I would know exactly what to change.

An Episode after the other, you can start inferring how SOLID principles complement each other, dependency inversion (coming in episode 5) helped us achieve SRP. SOLID and design patterns also complement each other, the logging issue would have also been solved using a creational design pattern, but that’s a story for another day I guess ?

Next episode of SOLID Principles, but you’re 5 years old, we’ll be discussing the Open/Closed principle, stay tuned, I will be announcing on my instagram once it’s published.

* A software post-mortem is an analysis conducted after a system failure. The goal is to understand why an incident / error happened, and to learn from the experience. In doing so, future software becomes more robust.

** The original Apache Log4j vulnerability (CVE-2021–44228), also known as Log4Shell, is a cybersecurity vulnerability on the Apache Log4j 2 Java library. This security flaw is a Remote Code Execution vulnerability (RCE) — one of the most critical security exposures.

--

--

Adham Saheb
Adham Saheb

Responses (3)