DRY, YAGNI, KISS and SINE - 4 Most Important Software Development Principles

4 fundamental dev principles: DRY, YAGNI, KISS, SINE

4 fundamental dev principles: DRY, YAGNI, KISS, SINE

Introduction

For more than five years, I kept a large poster above my desk displaying the words: DRY, YAGNI, KISS, and SINE. These acronyms represent four essential principles in software development that help you create better code and shorten the timeline of your projects.

In the picture above, you can find a summary of each principle. In this article, we’ll look at each of them more closely and learn how you can use them in coding.

1. DRY - Don’t Repeat Yourself!

DRY principle is a kind of holy grail of easily maintainable codebase. The idea is simple: Write the same code only once. And no, you may not copy paste code either.

If you duplicate the same code, 99% of the time you are doing something that the future you will regret. It’ll make your codebase hard to maintain. For example, when you encounter a bug, you’ll have to remember to do the fix to all places where the same code is copy-pasted. And if you don’t, you’ll encounter the same bug again and you’ll need to fix it again.

“Every piece of knowledge must have a single, unambiguous, authoritative representation within a system” -Andy Hunt & Dave Thomas

If you need similar functionality in multiple locations, and you end up duplicating code, refactoring is necessary. Refactoring means improving the existing code without changing its functionality. The goal is to create a better design or structure for the code to make it easier to change it later.

Let’s consider the following code of an imaginary fantasy game:

const MANA_POTION_EFFECT = 10;
const ELVEN_BREAD_EFFECT = 5;

function eatElvenBread() {
	character.mana += ELVEN_BREAD_EFFECT;
}

function useManaPotion() {
	character.mana += MANA_POTION_EFFECT;
}

The both functions have basically the same code. Still, it doesn’t look too bad, does it?

Now, let’s think about a scenario where our game rules change. We get the following requirement:

Requirement:

The effects of the mana potion and elven bread must be relative
to the maximum mana of the hero so that they have meaningful 
impact even on higher levels.

For example: If the hero's current maximum mana is 50,
the mana potion restores 10% of it (instead of 10 units),
which is 5 units.

Now, how can we change our code for that?

Yes, we need to change the both functions. That is a code smell and we have actually two risks:

  1. In the real world, the functions can be far away from each other and it’s easy to miss one and make changes only to the other.
  2. If we notice both functions, it’s tempting to just implement the changes for both of them, because it’s quite simple and the schedule pressure can be tough.

This is often the point where things start to go wrong.

A best approach would have been to extract the common logic into a separate function originally, but let’s do the refactoring now:

const MANA_POTION_EFFECT = 10;
const ELVEN_BREAD_EFFECT = 5;

function applyManaEffect(effect) {
	character.mana += effect;
}

function eatElvenBread() {
	applyManaEffect(ELVEN_BREAD_EFFECT);
}

function useManaPotion() {
	applyManaEffect(MANA_POTION_EFFECT);
}

Now it’s easy to change the rules of the game as we need to change only one function:

const MANA_POTION_EFFECT = 10;
const ELVEN_BREAD_EFFECT = 5;

function calculateManaEffect(relativeEffect) {
	return (relativeEffect / 100) * character.maximumMana;
}

function applyManaEffect(relativeEffect) {
	const effect = calculateManaEffect(relativeEffect);
	applyManaEffect(effect);
}

function eatElvenBread() {
	applyManaEffect(ELVEN_BREAD_EFFECT);
}

function useManaPotion() {
	applyManaEffect(MANA_POTION_EFFECT);
}

This kind of refactoring is essential because code duplication leads to a spaghetti mess that is hard to maintain and increases technical debt. It slows you down tremendously in the long run.

DRY might be the most important programming principle.

So, the next time you’re going to hit Ctrl+C and Ctrl+V, stop for a second and think of a way to avoid it.

The idea of the DRY principle is simple, but it’s difficult to apply. You just have to do it! Don’t put a TODO comment; you already know that there will always be something more important. Do the refactoring immediately, and you’ll thank yourself later.

2. YAGNI - You Ain’t gonna need it!

The second principle is: Do not implement features or write functionality that you don’t need right now.

Let’s get back to our fantasy game. Our hero will face challenging adventures in Eldoria, so we might have an idea that in the future our hero will have an ability to transform into a werewolf. So we add this function:

WEREWOLF_STRENGTH_MODIFIER = 1.5;

function transformIntoWerefolf() {
	character.strength *= WEREWOLF_STRENGTH_MODIFIER;
	// ...
	// NOTE: This is for future needs. Not all parts of
	// transforming into a werefolf are implemented.
}

This is an anti-pattern that you should avoid! If you form such a habit, your codebase will become full of unused code and TODOs that you still must maintain.

As developers, we often think we know what features or functionality we’ll need in the future. We code them beforehand, thinking we’ll save time, when in reality we’re just wasting it. This kind of thinking was actually one of the biggest errors I kept doing in the early years of my career.

First: You can’t predict the future. It is as simple as that. What seems reasonable today may be totally different tomorrow. Everything changes, and very quickly.

Second: By trying to forecast the future, you end up writing code that is not used. There are two options: either you maintain that unused code, which requires time and effort and feels stupid, or you leave the code unmaintained. In my experience, leaving the code unmaintained means that it is not usable even if your prediction comes true and the day comes when you need it.

So, just write the code when you need it.

And I don’t mean that you don’t need to take the future into account at all. I mean you should design for change but not write code that you don’t need yet.

Learn to write code that you can easily edit. For example, avoid hard coding values, have a good architecture, use design patterns, keep your code clean, and keep functions short. Don’t try to predict the future, but create a system that you can easily change.

3. KISS - Keep it stupid simple!

The KISS principle states that: Keep things super simple and avoid unnecessary complexity.

Software development is very challenging, so it’s essential to have simple solutions. Simple solutions might work. Complex ones won’t.

Consider the manufacturing of boats (which I know nothing about, but I guess it’s very difficult too). For boats, it doesn’t matter if the color pigment of the rudder is slightly wrong. However, if you make a similar level of mistake in code, the whole system might crash with no warning.

Even the slightest error in code can cause critical issues. In my career, I’ve made many single-line changes that resulted in major bugs. Therefore, it’s important to keep the building blocks of the system as simple as possible. After implementing the first version of the code, think how you can make it simpler and refactor it.

Always create the simplest possible solution you can think of.

Have:

  • Simple classes that follow the single-responsibility principle (SRP)
  • Short methods
  • Easy-to-understand algorithms
  • Clear directory structure
  • Simple architecture that is easy to understand.

If you can’t explain how your code works to a colleague in 30 seconds, it’s probably too complicated or you don’t understand it yourself.

Note that a simple solution doesn’t necessarily mean an easy solution. I’ll explain this concept in the next section with an example that illustrates keeping things simple.

4. SINE - Simple is not easy!

SINE is my extension of the KISS principle because the KISS principle is not always understood correctly: Simple is often erroneously mistaken for easy, and in software development, it’s the opposite.

  • Easy means something that can be done quickly, without needing much learning, research, thinking, or work.
  • Simple is the opposite of complex. It is something that has only a small amount of moving parts.

For example, let’s consider the famous formula of Albert Einstein:

E = mc²

It is simple: very generic and not too many “moving parts”. But it definitely wasn’t easy to invent, and it’s not too easy to understand either.

In a similar way, writing code, implementing features and solving problems are relatively easy. Most developers can implement the required features. However, only a few are competent enough to implement those in a simple way because simplicity is difficult to achieve.

“Anyone can make the simple complicated. Creativity is making the complicated simple.” -Charles Mingus

It’s much easier to implement a feature in the first thinkable way by writing a long function with 500 lines, than to design small, reusable functions or methods or create a good class hierarchy.

I don’t mean you need to invent a new theory of relativity every time you write a code. However, you need to think things through and do the refactoring work to make things simple.

Let’s get back to the world of Eldoria. Look at the following example of simple versus easy. This code is relatively easy to write:

const HEALTH_POTION_NAME = 'Health Potion';
const MANA_POTION_NAME = 'Mana Potion';
const STRENGTH_POTION_NAME = 'Strength Potion';

const HEALTH_POTION_DESCRIPTION = 'Restores a portion of health';
const MANA_POTION_DESCRIPTION = 'Restores a portion of mana';
const STRENGTH_POTION_DESCRIPTION = 'Increases physical strength';

function getHealthPotionDetails() {
  console.log(`Name: ${HEALTH_POTION_NAME}`);
  console.log(`Description: ${HEALTH_POTION_DESCRIPTION}`);
}

function getManaPotionDetails() {
  console.log(`Name: ${MANA_POTION_NAME}`);
  console.log(`Description: ${MANA_POTION_DESCRIPTION}`);
}

function getStrengthPotionDetails() {
  console.log(`Name: ${STRENGTH_POTION_NAME}`);
  console.log(`Description: ${STRENGTH_POTION_DESCRIPTION}`);
}

It’s not very maintainable though and clearly it doesn’t follow the DRY principle either. Consider what would happen if we add 10 more items to our game.

Now, let’s follow the KISS principle and write a simpler version of it using JavaScript objects as dictionaries:

const POTION_DETAILS = {
  HEALTH: {
    name: 'Health Potion',
    description: 'Restores a portion of health',
  },
  MANA: {
    name: 'Mana Potion',
    description: 'Restores a portion of mana',
  },
  STRENGTH: {
    name: 'Strength Potion',
    description: 'Increases physical strength',
	},
};

function getPotionDetails(potionType) {
  console.log(`Name: ${POTION_DETAILS[potionType].name}`);
  console.log(
		`Description: ${POTION_DETAILS[potionType].description}`
	);
}

I consider this code being simpler, but also harder to write. It is simpler because it has only one function instead of three. It is harder to write because you must understand the concept of dictionaries and how to use JS objects as ones. The example is quite simplified, but I believe you got the point of simple vs. easy.

Creating simple solutions may sometimes be challenging, but it’s worth the effort to refactor the code to simpler after having the first working version. Although simplicity requires work, it pays off in the long run when you have a code base that is easy to understand and you can quickly make changes to it. Besides you, this will also benefit other developers in your team.

Conclusion

The four essential software development principles are:

  • DRY: Don’t repeat yourself (”Do not copy-paste”)
  • YAGNI: You ain’t gonna need it (”Don’t write the code if you don’t need it now”)
  • KISS: Keep it simple stupid (”Write the code in the simplest way you can think. Refactor.”)
  • SINE: Simple is not easy (”Simplicity requires work. Do it.”)

By following these four fundamental principles, you can create better code and make better decisions. Simplicity takes work, but it pays off in the long run with a code base that is easy to understand and quick to modify. So, use these guidelines to write better code and to become a better developer.

Explore more

Your subscription could not be saved. Please try again.
Your subscription has been successful.

Get notified!

Be the first to hear when something new is out.

View the privacy policy.