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.
KISS
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.
DRY
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.
Resources:
YAGNI
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.
Resources:
SLAP
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.
Resources:
SRP
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 DevicesController
, 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.
OCP
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.
LSP
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 width
and 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 width
and height
at the same time (say setSide
).
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 Rectangle
s. This might then result in a wrong setHeight
and 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 setHeight
/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 setHeight
/setWidth
methods, but now they wouldn't be compatible with the original class, resulting, again, in breaking the LSP.
ISP
Interface Segregation Principle (ISP) is yet another principle that says how to organize your code. As it's mainly focused on interfaces and statically-typed programming languages, those who write in e.g. JavaScript won't use it very often. However, the sole knowledge that the ISP brings to the table can still be used to improve your code in other ways.
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).
DIP
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.
SOLID
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.
Resources:
- SOLID Principles: Explanation and examples
- SOLID Principles made easy
- S.O.L.I.D: The First 5 Principles of Object Oriented Design
The Only Principle
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!
Top comments (16)
Pretty good. One major correction, however...
Close, but not quite. Your code can be overly DRY as well, wherein you abstract too much out into functions and macros and lambdas and...heaven help us. That's when the code becomes a big pile of DRY spaghetti. GCC's
libstdc++
is a great (terrible) example of this.Instead, I recommend applying DRY with what I call the "rule of threes": If you repeat something twice, it is not an automatic candidate for DRY. If you repeat something three or more times, seriously consider abstracting!
To correct the correction ;)
DRY isn't about code duplication - it's about knowledge duplication. Sometimes two pieces of code that 'do' the same thing are actually 'about' different things. For instance (a very simple instance):
!!! LET'S DRY IT !!!
but now I ❤️ four!
oh noes!
This is when coupling goes wrong. It seems like a really stupid example, but when DRY goes wrong it's the same thing, but usually just a few steps removed (classes instead of variables, for instance).
When Sandi Metz talks about the 'wrong' abstraction, she means that it's bad when we couple concepts together in code that are actually independent of each other even though the code 'looks' the same. We couple together pieces of knowledege that are actually independent. My favourite number and the second prime number are the same... but they're really nothing to do with each other. One expresses an eternal fact about three, and the other is much more subject to change...
The final kicker: you won't be able to spot knowledge duplication as easily as you can spot code duplication. It becomes apparent much, much later as you build your program.
Honestly best explanation of DRY gone wrong I've seen.
Yes, exactly. Good insight. It's not really much different from what I was trying to say, although you've beautifully expanded the point!
This. So much this.
What about WET?
Write Expressive Tests • the principle to guide unit tests to be independent, because having common set-up and tear-down code makes unit tests harder to maintain, a much high cognitive load, and more brittle.
Haven't heard about this. Thanks for info!
Forgot to mention that! Not really a principle, but an opposite of DRY - Write Everything Twice. 😉
Allow your code to be WET , then make it DRY.
Cool!
I thought it stood for 'We Enjoy Typing' :)
Maybe that dependable on the context. 😀
Don't forget GRASP (General Responsability
Assignment Software Patterns) and GoF (Gang of Four)
medium.com/@ReganKoopmans/understa...
google.com/amp/s/hub.packtpub.com/...
Great article! Keep up the good work!
I would also add STUPID williamdurand.fr/2013/07/30/from-s...