The DRY Talk


author: Madan

date:

read time: ~3 min

tags: [DRY] [JS] [Coding]


When we speak about the essence of good code, the first and foremost topic pops up is DRY, which actually means DON’T REPEAT YOURSELF. Following this makes somewhat sure that other developers won’t bang their head understanding what code is doing.

My initial coding journey was all to get it work and it didn't make much sense to implement all these. Later when I started my professional career in programming, people started speaking about DRY, YAGNI and all those cool acronyms. Let’s talk about what this DRY actually means and why it is so important to not misunderstand by its own acronym.

NOTE: The topic essence of good code goes by many other techniques and i will be only talking about DRY. More reference at the end of the blog

Don’t Go by the Acronym

Don’t Repeat Yourself, as the name goes it makes it kinda obvious that we need to avoid writing repetitive code. This is not what DRY means, not completely false but a small trivial part of it is right.

DRY actually is about duplication of knowledge, It’s about expressing the same thing in two different places.

Let’s look at some example

Before: DRY version

function sendWelcomeNotification() {
  const payload = {
    channel: "email",
    priority: "normal",
    retries: 3,
    message: "Welcome to the platform"
  };

  fetch(NOTIFICATION_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload)
  });
}

function sendPasswordResetNotification() {
  const payload = {
    channel: "email",
    priority: "normal",
    retries: 3,
    message: "Reset your password"
  };

  fetch(NOTIFICATION_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload)
  });
}

Same structure. Same config. Different message.

That’s not two features. That’s one idea copy-pasted. Why this is a DRY problem. If tomorrow: retries change to 5 ,channel changes to sms,headers change

You now need to change multiple functions for the same knowledge. Enjoy missing one.

After: DRY version

function sendNotification(message) {
  const payload = {
    channel: "email",
    priority: "normal",
    retries: 3,
    message
  };

  return fetch(NOTIFICATION_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload)
  });
}
function sendWelcomeNotification() {
  return sendNotification("Welcome to the platform");
}

function sendPasswordResetNotification() {
  return sendNotification("Reset your password");
}

This is better that above because the intent of the function is clear and has slightly lesser abstraction of code. Changes are boring and safe, need to worry about stepping on something else.

Not all Code Duplication is Knowledge Duplication.

function validte_age(value) {
	if typeof(value) === "number" && validate_min(value, 0)

function validate_quantity(value){
	if typeof(value) === "number" && validate_min(value, 0)


If we look at this piece of code , it has exact same code for both function, but if we look at this carefully we can see that the knowledge is not duplicated and each function has single source of truth. In future even if we want to change min value for age , we can easily.

Duplication in Documentation

It’s good that developers actually document the code and tell what each function is doing, until we overdo it. Let’s take an example

/**
 * Transfers money from sender to receiver.
 *
 * First, it checks if the sender has enough balance.
 * Then it deducts the amount from the sender’s balance.
 * After that, it adds the amount to the receiver’s balance.
 * Finally, it applies a transaction fee to the sender.
 * It returns the updated balances for both sender and receiver.
 */
function transferMoney(sender, receiver, amount) {
  if (sender.balance < amount) {
    throw new Error("Insufficient balance");
  }

  sender.balance -= amount;
  receiver.balance += amount;

  applyTransactionFee(sender, amount);

  return {
    senderBalance: sender.balance,
    receiverBalance: receiver.balance
  };
}

What’s wrong here??

We are telling the exact same thing twice. The logic exactly tells what the function is doing. We need to go out of way and explain the same thing using comments. In future if plan to remove transaction fees or update the function with new quantum particle, I need to updated It at two places which violates DRY principal.

My perspective on this principle

  1. Code duplication is not always duplication of knowledge
  2. A single, overly generic reusable function is not always better than small, explicit duplication.
  3. Blindly applying DRY can introduce unnecessary complexity. Sometimes a small duplication of code or knowledge is better than a complex abstraction of code which no one can understand after 6 months.

Conclusion

Essence of good code has lot more techniques involved and DRY is just a part of it. When codebase grows and where functions are reused multiple times, it’s better to follow this so we don't end up with PR that solves one and break two features.

--- end of article ---