Swizec Teller - a geek with a hatswizec.com

    Why null checks are bad

    Last week we talked about how small decisions can lead to big changes in your codebase. We used null-checks as an example but didn't explain why those are bad.

    It's about exploding complexity.

    Starts simple

    Take a basic React component for example. It takes a user and renders their info. Like for a page header.

    type User = {
    name: string;
    };
    function UserInfo(props: { user: User }) {
    return <div>{props.user.name}</div>;
    }

    We have a User type that defines properties and a component that takes user as a prop. It then renders the user's name.

    How would you test this?

    There is 1 important state

    In theory, this component has an infinite number of states. The name string can be anything.

    But you can rely on React and browser rendering to work. You only need to test that when you pass a user name, it renders.

    test('renders username', () => {
    let component = renderer.create(
    <UserInfo user={{ name: 'Swiz' }}> />
    )
    expect(component.toJSON()).toMatch(
    '<div>Swiz</div>'
    )
    })

    A null check doubles your important states

    Let's see what happens when a user's name may be undefined and we want to show a default.

    type User = {
    name?: string | null;
    };
    function UserInfo(props: { user: User }) {
    return <div>{props.user.name || "Unknown"}</div>;
    }

    The name property may be undefined or null. We can treat those the same because there is a special circle in hell for people who use the difference between undefined and null for program logic.

    Altho it can be useful sometimes. I've done it πŸ˜‡

    When the username is undefined, our component now shows Unknown instead.

    How many tests do you need now?

    There are 2 important states

    You now need to write 2 tests to verify this component works.

    test('renders username', () => {
    let user = { name: 'Swiz' }
    let component = renderer.create(
    <UserInfo user={user}> />
    )
    expect(component.toJSON()).toMatch(
    '<div>Swiz</div>'
    )
    })
    test('renders Unknown', () => {
    let user = {}
    let component = renderer.create(
    <UserInfo user={user}> />
    )
    expect(component.toJSON()).toMatch(
    '<div>Unknown</div>'
    )
    })

    The first test verifies rendering the username, the second test verifies the default state.

    If you don't trust JavaScript's || operator, you could add another test for an explicit name: null value.

    And you might notice I wrote a bug. Empty usernames render as Unknown. πŸ’©

    That's where fuzzing is useful. Finding little edge cases like that.

    Every null check doubles your states

    Now here's where it gets crazy. Every null check doubles the number of tests you need.

    type User = {
    name?: string;
    avatar?: string;
    karma?: number;
    };
    function UserInfo(props: { user: User }) {
    const { user } = props;
    return (
    <div>
    {user.avatar ? <img src={user.avatar} /> : <DefaultAvatar />}
    <label>{user.name || "Unknown"}</label>
    <p>{typeof user.karma === "undefined" ? "🐣" : user.karma}</p>
    </div>
    );
    }

    Show user avatar or a default. Show their name or 'Unknown'. Show their karma or 🐣.

    How many test cases does this component need?

    That's right! 8.

    Add one more and you get 16. At 5 nullable properties, you're looking at 32 combinations. 2^N(nulls) adds up fast πŸ™ƒ

    And if you're treating undefined and null as different, that formula becomes 3^N(nulls).

    How to solve exploding complexity

    Truth tables are my favorite tool in this case. Write out all your nullable variables, iterate their states, cross out what's impossible.

    Here's an example from an article about How tests uncover hidden complexity in your code

    An example truth table
    An example truth table

    You take the rows that aren't crossed out and write tests. Or decide that's way too many states and simplify your code.

    When you have lots of transitions between states, I like to use XState. Helps when your brain is breaking ☺️

    Cheers,
    ~Swizec

    PS: I'm calling it a null check because that's the standard phrasing. JavaScript just happens to have 2 different null-ish values – undefined and null

    Did you enjoy this article?

    Published on December 10th, 2021 in TypeScript, JavaScript, Lessons,

    Learned something new?
    Read more Software Engineering Lessons from Production

    I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.

    Subscribe below πŸ‘‡

    Software Engineering Lessons from Production

    Join Swizec's Newsletter and get insightful emails πŸ’Œ on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.

    "Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. πŸ‘Œ"

    ~ Ashish Kumar

    Join 15,883+ engineers learning lessons from my "raw and honest from the heart" emails.

    ⭐️⭐️⭐️⭐️✨
    4.5 stars average rating

    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

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

    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 ❀️