This post was taken from my blog, so be sure to check it out for more up-to-date content.
The programming industry has a lot to offer when it comes to acronyms. KISS this, SLAP that - there are a lot of these intriguing, but meaningful abbreviations out there. If you're only getting started, seeing them pop up from left and right might be a bit stressful. Especially if you don't know what they mean! Anyway, if that's the case, here's a blog post for you!
In this article, we'll explore 10 different coding principles that come with some pretty cryptic acronyms. Some are well-known, while others - less so. The difficulty in understanding and applying them to your code also varies. With that said, I'll try to explain the whole theory behind each of these terms in detail. The fun part - their implementation - is left to you.
Let's start with some more popular principles. Keep It Stupid Simple (KISS) is one of the most well-known ones. It also has a pretty clear, but very broad meaning.
Basically, this principle dictates that you should keep your code very simple, which is a no-brainer. The simpler the code, the easier it is to understand for you and other people maintaining it. The simplicity mostly refers to not utilizing sneaky tricks (like these), and not overcomplicating things that don't require that.
The basic examples of breaking this rule would be writing a separate function only to conduct addition operation, or using a bitwise operator (right shift
>>1) to divide integers by 2. The latter is surely more performant than its usual counterpart (
/2), but greatly reduced the "understandability" of the code. By doing this, you're committing what's called clever coding and over-optimization. Both of which aren't very good for the long-time "health" of your code.
Don't Repeat Yourself (DRY) principle in its nature is very similar to KISS. It's quite simple and yet has a broad meaning at the same time.
Copy-pasting and duplicating fragments of own code happens to many programmers. There's nothing wrong about doing that. Everybody sometimes needs to quickly check something (expected behavior or whatever) to later determine if it's worth the hassle to write it properly. But it's surely unacceptable to ship such code to production.
DRY reminds us that every repetitive behavior in the code can and should be extracted (e.g. within a function) for later reuse. Having two fragments of the same code in your codebase isn't good. It can often lead to desynchronization and other bugs happening in your code, not even mentioning increment in the program's size.
YAGNI is actually the longest acronym on this list. You Aren't Gonna Need It (YAGNI) is a principle that might conflict with some programmers' perspectives.
Being prepared for the future is usually a good thing, but not in programming. Leaving any code that's meant only for future extendability isn't good. But, if it conflicts with your beliefs, let's discuss it a bit further.
Coding projects aren't things that have a clear ending. Unless the creator abandons the idea (and don't pass it to someone else), the project is, in fact, going to end. But, otherwise, there's pretty much no point at which the code is "good enough". There's always some room for improvement. It's good to look into the future, and think about what you want your code to look like. But, in production, leaving "extension points" (places meant to easily allow for new functionalities), unless intelligently utilized or a required feature, isn't desired. It adds unnecessary complexity and increases the size of your codebase. If you think about it, it even conflicts with the previously-discussed KISS principle.
Guess what! You can not only KISS or DRY but also SLAP your code! Single Level of Abstraction Principle (SLAP) dictates the way you should organize your code (functions to be specific) to keep it maintainable.
Long and complex functions are hard to live with. They're difficult to understand for others, are hard to test and often require scrolling to see all of their content! If you come across such an abomination, you should restructure it to a few smaller functions immediately! Remember that:
Functions should do just one thing, and they should do it well. Robert Martin
But how exactly should you organize your smaller functions? What is this one thing they should do? Well, as you get more experience in the programming, you'll start to feel where certain things should go, and the SLAP will help you.
Your functions should do only one thing, or, with the SLAP in mind, should have only a single level of abstraction. Basically, a function that, for example, reads the user input, shouldn't also process it. Instead, it'll use a separate function, which is on the other, lower level of abstraction. The more general the function is and the more other functions it utilizes, the higher it is in the abstraction hierarchy.
Single Responsibility Principle (SRP) is somewhat similar to the SLAP but directed towards Object-Oriented Programming (OOP). It says that you should organize your objects and classes (but also functions and methods), for them to have only one responsibility each.
Responsibilities of objects and classes are easy to organize when they reflect more life-like objects. However, when we're dealing with entities that have e.g. "controller" or "service" in their names, then the situation starts to complicate. These high-level units are hard to organize, as in theory, you could put pretty much everything inside them and call it a day. In such a case, the number of responsibilities of such an entity sky-rockets, making the whole code increasingly harder to understand.
How to fix this issue? Let's say that our controller is responsible e.g. a computer. It has to control and store the CPU temperature, fan speed, disk space, external devices and all that sorts of stuff. Mind you that it means not only properties but also methods that we're dealing with. Instead of keeping everything directly in one class, how about splitting it into multiple classes? Now we'd have
DiskSpaceController, etc. Then we'd use all these classes to form the high-level
Controller class, which now is way easier to maintain. Of course, in reality, such a code would need a lot more organization, but I hope you get the idea.
We've already talked about code extendability when discussing YAGNI. Open-Closed Principle (OCP) is somewhat related to that previous rule but features a different perspective.
The OCP requires your code to be open to new, future additions, without having to modify the already-written code. It's more about the overall architecture of your code rather than the code itself.
Does the OCP conflicts with YAGNI? After all, we're talking about the future of the code from two different perspectives here, right? Well, no. As I said earlier, YAGNI prevents you from adding code that you aren't currently using. On the other hand, the OCP goes deeper - into your code architecture - to make it future-proof right from the core. You aren't meant to write any currently-unused code, rather than design the whole codebase in such a manner, that it supports easy extendability.
Make your "core" extendable, built the current functionality upon that, and have a good, future-proof architecture down the road, without having to write any dead code.
Liskov Substitution Principle (LSP) named after its creator - Barbara Liskov - is an OOP principle, related to classes, interfaces, types, and subtypes.
The rule itself is pretty simple and logical, but might be hard to grasp at first. It indicates that any subtype must be substitutable for its base type. I think an example is needed to illustrate this better.
Let's take the infamous rectangle and square problem, that's usually used to illustrate this principle. We've got a
Rectangle class (a base type), which has properties like
height and methods to set them and calculate the area. By inheritance, we create a
Square class (a subtype), which has an additional method to set both
height at the same time (say
If we use these classes separately - nothing would happen. But, as the
Square class is a subtype of a Rectangle class, it can be assigned to the variable which accepts
Rectangles. This might then result in a wrong
setWidth calls, causing our
Square instance to have incorrect dimensions and miscalculating the area (or throwing an error if a check is implemented).
We could easily resolve the issue by checking whether the entity is of a
Square subtype, directly in the
setWidth methods of the
Rectangle class. Sadly, in the process, we would acknowledge the existence of a
Square subtype and thus break the LSP. You could also override the
setWidth methods, but now they wouldn't be compatible with the original class, resulting, again, in breaking the LSP.
Interfaces serve as a way to work with the form of the data, rather than the data itself. Writing and organizing them properly gives you a great way to improve the maintainability of your code, without much performance loss.
That's pretty much what the ISP is all about - using the interfaces to segregate your code, while also keep the interfaces themselves organized as well. Take a class inheritance for example. Maybe you don't care about certain methods or properties from the base class and want to "skip" them? A simple interface can help you do that! In compiled and statically-typed languages, it also gives you advantages like cleaner scope and faster compilation time (subclasses don't have to recompile when its parent properties, aside from the interface-specified ones, change).
Like OCP, the Dependency Inversion Principle (DIP) also refers to the more general architecture of your code. In fact, it's one of the most important principles in the code architecture design.
The DIP is a bit complex, but there are only two things you need to understand to follow it correctly. Firstly, your code should be written in a way, where implementation details (e.g. User Interface (UI), database) should be dependent on the main logic (aka business rules) - not otherwise.
Secondly, all these dependencies shouldn't be direct. You should abstract them through e.g. interfaces, so that your main logic would work with anything you'll throw at it, only requiring some simple "bridge" code to be implemented.
5 of the previously-discussed principles - SRP, OCP, LSP, ISP, DIP - come together to form SOLID - a set of principles that instructs you on how to write good, object-oriented (but not only) code, created by Robert C. Martin. With the descriptions from this blog post, I hope I provided "logical-enough" explanations for starters in this full of principles programming world. However, if you're interested in the topic, I recommend you search the web or visit the linked resources to learn more.
- SOLID Principles: Explanation and examples
- SOLID Principles made easy
- S.O.L.I.D: The First 5 Principles of Object Oriented Design
Lastly, we've got The Only Principle (TOP). OK, I'm just kidding! But really, all the coding principles discussed in this article (and even all the others) have only one goal - to help you write good, maintainable code. That's their TOP priority. And while knowing them certainly helps, it's only you who has control over your code and how it'll look like.
I hope you've enjoyed this blog post, and learn something new in the process! If so, consider sharing it, and following me on Twitter, Facebook, or checking out my personal blog for more! Also, I've got a YouTube channel if you're interested. As always, thank you so much for reading this piece, and have a nice day!