skies.dev

Make Your Code DRY: Don't Repeat Yourself!

4 min read

DRY means “Don’t Repeat Yourself,” but that slogan is easy to misuse. The goal is not to remove every repeated line. The goal is to keep important knowledge in one place when duplication would make the system harder to change.

What DRY Is Actually About

When two parts of the codebase must change together, that is a duplication problem. The repeated thing might be:

  • business rules
  • validation logic
  • styling decisions
  • data transformations
  • configuration

The key question is not “do I see repetition?” It is “will I have to edit this in multiple places every time the rule changes?”

The Pragmatic Programmer is still a good reference for the original idea behind DRY.

A Clear Example

Suppose a User object has to answer whether certain actions are allowed. Several methods repeat the same inactive-user logic:

class User
  def can_bookmark_post?(post)
    true unless @disabled || @suspended || post.deleted?
  end

  def can_like_post?(post)
    true unless @disabled || @suspended || post.deleted? || post.author_id == @id
  end

  def can_follow_user?(user)
    true unless @disabled || @suspended || following?(user) || user.id == @id
  end
end

If the definition of “inactive” changes, you have to update every method that copied the rule. That is the kind of duplication DRY wants you to remove.

A better version moves the shared rule into one method:

class User
  def inactive?
    @disabled || @suspended
  end

  def can_bookmark_post?(post)
    true unless inactive? || post.deleted?
  end

  def can_like_post?(post)
    true unless inactive? || post.deleted? || post.author_id == @id
  end

  def can_follow_user?(user)
    true unless inactive? || following?(user) || user.id == @id
  end
end

Now the rule has one owner. That is DRY done well.

A Frontend Example

Duplication shows up in UI code too. If three forms all use the same primary button classes, copy-pasting the class string everywhere makes style changes annoying.

Instead, extract the shared button styling:

export function PrimaryButton(props: React.ButtonHTMLAttributes<HTMLButtonElement>) {
  return (
    <button
      className="bg-blue-500 px-2 py-1 font-bold text-white shadow hover:bg-blue-600"
      {...props}
    />
  );
}

Now the form components can focus on their own job, while the button style lives in one place.

That is a good abstraction because the styling rule and the component change together.

When DRY Goes Too Far

The mistake is treating every repeated line as a bug. Sometimes repetition is the right tradeoff.

For example, you might see type="submit" repeated on several buttons and decide to hide it inside PrimaryButton. That sounds DRY, but it also makes the component less flexible. If one caller needs a button that is not a submit button, the abstraction starts fighting the real use case.

This is the real cost of premature abstraction:

  • more indirection
  • harder debugging
  • more coupling than the original duplication had

A useful test is whether the abstraction captures a stable concept. If it does not, leave the repetition alone for now.

Duplication is not always the problem. Sometimes it is a useful clue that two things only look similar on the surface.

A Simple Rule For Choosing

DRY the code when the repeated logic is truly the same rule. Do not DRY it when the repetition is accidental but the use cases are still meaningfully different.

Ask these questions before extracting:

  • Will these pieces change together?
  • Does the abstraction remove real maintenance work?
  • Does the new name describe a stable concept?
  • Will the code be easier to read after the extraction?

If the answer to most of those is no, keep the duplication for now.

Practical Signals

Good candidates for DRY:

  • validation rules that appear in multiple places
  • permissions logic shared across screens
  • repeated API payload shaping
  • design tokens or reusable UI patterns

Bad candidates for DRY:

  • one-off convenience wrappers
  • abstractions created from a single example
  • logic that only repeats because the feature is still evolving
  • helpers that hide important differences between call sites

Summary

  • DRY is about shared knowledge, not about deleting repeated characters.
  • Extract abstractions when they reduce real maintenance cost.
  • Keep duplication when the code paths only look similar and still need different behavior.
  • Prefer a small amount of repeated code over a premature abstraction that is harder to undo.

Hey, you! 🫵

Did you know I created a YouTube channel? I'll be putting out a lot of new content on web development and software engineering so make sure to subscribe.

(clap if you liked the article)

You might also like