Swizec Teller - a geek with a hatswizec.com

Senior Mindset Book

Get promoted, earn a bigger salary, work for top companies

Senior Engineer Mindset cover
Learn more

    DRY is a footgun, remember to YAGNI

    DRY is the first of the big rules engineers learn. Do not Repeat Yourself.

    It's a good rule. You'll figure it out on your own even if nobody teaches you.

    "Make a button"

    Okay, you can write a component like this:

    const Button = () => <button>Button</button>;
    

    "I want to have a button that says Button and a button that says Click Me"

    You got it!

    const Button = () => <button>Button</button>;
    
    const ClickMe = () => <button>ClickMe</button>;
    

    "Now I want Button to open the shopping cart and I want ClickMe to close the page."

    Okay, we can do that.

    const Button = () => <button onclick={openCart}>Button</button>;
    
    const ClickMe = () => <button onclick={closePage}>ClickMe</button>;
    

    "Wonderful. Beautiful buttons. I want a third button, and this one should close the currently open modal."

    Ah, this is getting tedious. Are you going to want many more buttons?

    "Yeah, maybe?"

    Okay. Let me DRY this up.

    const GenericButton = ({ onClick, label }) => (
        <button onclick={onClick}>{label}</button>
    )
    
    const Button = () => (
        <genericbutton onclick={openCart} label="Button">
    )
    
    const ClickMe = () => (
        <genericbutton onclick={closePage} label="ClickMe">
    )
    
    const CloseModal = () => (
        <genericbutton onclick={closeModal} label="Close Modal">
    )
    </genericbutton></genericbutton></genericbutton>
    

    "Wonderful. Can we make all those buttons 20px font?"

    Yes, of course!

    You've just DRY'd up your code, and here's your first victory. Buttons use a common underlying component. Giving them all a big font is easy. You got dis.

    const GenericButton = ({ onClick, label }) => (
        <button 20="" onclick={onClick} style={{ fontsize: }}>
            {label}
        </button>
    )
    

    All your buttons now have a bigger font. DRY Magic.

    A true victory for engineering. You pat yourself on the back for an architecture well designed, code cleaned up. You followed some great engineering principles! 👏

    But you just shot yourself in the foot.

    Next week, your PM comes back and says, "Button must be green, ClickMe should be blue, and CloseModal is red"

    Okay, that's annoying, why 3 different buttons, but you can extend GenericButton to accept styling. You've still got dis.

    const GenericButton = ({ onClick, label, style }) => (
        <button onclick={onClick} style={{ fontsize: 20, ...style }}>
            {label}
        </button>
    )
    
    const Button = () => (
        <genericbutton onclick={openCart} label="Button" style={{ background: 'green' }}>
    )
    
    const ClickMe = () => (
        <genericbutton onclick={closePage} label="ClickMe" style={{ background: 'blue' }}>
    )
    
    const CloseModal = () => (
        <genericbutton onclick={closeModal} label="Close Modal" style={{ background: 'green' }}>
    )
    </genericbutton></genericbutton></genericbutton>
    

    You had to add styles to every call of GenericButton, but that's okay. At least you have components so you didn't have to find every single invocation of any button and change them.

    That's the power of DRY. Change once, fix everywhere.

    Then your PM says "The font on CloseModal is too big"

    A-ha! You were smart. You wrote GenericButton so styles provided through props can overwrite default styles.

    {{ fontSize: 20, ...style }} versus {{ ... style, fontSize: 20 }}.

    "CloseModal and Button should be disabled when they're inactive"

    Hmm… 2 of your 3 buttons need new behavior. Best add it to the base implementation.

    const GenericButton = ({ onClick, label, active, style }) => (
      <button onclick={onClick} disabled style={{ fontsize: 20, ...style }}>
        {label}
      </button>
    );
    

    You change Button and ClickMe to set the active prop based on some state.

    And ClickMe became disabled too? That's weird.

    Guess you have to change that one so it's always passing active=true. That's weird.

    Why would a button always proclaim itself as active? Buttons should be active by default, and this feature should exist only for buttons that use it.

    You adapt the code.

    const GenericButton = ({ onClick, label, active, style }) => {
      active = active === undefined ? true : active;
    
      return (
        <button onclick={onClick} disabled style={{ fontsize: 20, ...style }}>
          {label}
        </button>
      );
    };
    

    Now active is true when undefined, and whatever you set otherwise.

    The DRY footgun

    Your GenericButton is starting to swell with edge cases. As time passes, it's going to get worse and worse.

    You'll add a loading state. But only some buttons will use it. You'll add hover/unhover behavior. But not all buttons want that.

    Eventually, you'll have a button with so many features it's impossible to use. Every render requires so many props that you might as well write a whole button from scratch.

    Happens a lot.

    YAGNI to the rescue

    YAGNI, ya ain't gonna need it, is a programming philosophy that engineers learn a little later in life.

    Because it takes a while for those DRY footguns to appear, perhaps. Or because many projects don't live long enough to see it. Maybe it's just a matter of thinking about why your code is suffering.

    YAGNI often comes in 2 shapes 👇

    The first is like the above. You spotted an opportunity to optimize, but you were too early. You should avoid generalizing code until it is absolutely obvious that you should.

    The more use-cases you have, the easier to know what to generalize. I mean, how can you know which functionality your components share if you don't even have those components yet?

    You can't.

    And that brings us to the other YAGNI: Building stuff you think you'll need before you need it.

    You think of a wonderful abstraction. A wonderful little feature that's gonna help oh so much in the future.

    You build it.

    Months pass. You tweak the code here and there. Nobody's using it, but you have to maintain otherwise it's gonna break. Whenever you implement a feature, you have to think about how it impacts this code you've got.

    It's slowing you down. Making you think. And you aren't even using it.

    But one day! One glorious day, your PM gives you a task. A task from the gods. You are about to use this code you predicted 6 months ago!

    You are a god of prediction! You knew it all along!

    You delete your code and start from scratch. The feature is so different that you can't use any of it.

    Published on January 7th, 2019 in Front End, Learning, Technical

    Did you enjoy this article?

    Continue reading about DRY is a footgun, remember to YAGNI

    Semantically similar articles hand-picked by GPT-4

    Senior Mindset Book

    Get promoted, earn a bigger salary, work for top companies

    Learn more

    Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.

    Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.

    Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.

    Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev

    Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization

    Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections

    Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog

    Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️

    Created by Swizec with ❤️