DEV Community 👩‍💻👨‍💻

DEV Community 👩‍💻👨‍💻 is a community of 963,864 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Ayron Wohletz
Ayron Wohletz

Posted on • Originally published at funtoimagine.com

Favor values over variables

Favoring values over variables makes code easier to read, write, and maintain.

A variable is a container for a value. Over time, you put new values in the container, overwriting the old. For example in TypeScript:

interface Todo {
    title: string;
    completed: boolean;
    importance: number;
}

function getRankedIncompleteTodos(allTodos: Todo[], pinnedTodos: Todo[]): Todo[] {
    const result = []; // 1. result = Empty array

    for (let todo of allTodos) {
        if (!todo.completed) {
            result.push(todo);
        }
    }
    // 2. result = An array of incomplete todos

    // sort by importance
    result.sort((a, b) => {
        if (a.importance < b.importance) {
            return 1;
        }
        if (a.importance > b.importance) {
            return -1;
        }
        return 0;
    });
    // 3. result = A sorted array of incomplete todos

    // pinned TODOs always at top
    for (let todo of pinnedTodos) {
        result.unshift(todo);
    }
    // 4. result = An array of pinned todos followed by sorted incomplete todos

    return result;
}
Enter fullscreen mode Exit fullscreen mode

Here result contains different values at different points in time. The sequence of values goes like this:

  1. Empty array
  2. An array of incomplete todos
  3. A sorted array of incomplete todos
  4. An array of pinned todos followed by sorted incomplete todos

For this sequence of values we have given a single label, result. To understand what result contains at a line of code, we have to trace through the code up to that point. In other words, we have to include the dimension of time in our understanding.

A variable acts like a sequence of values through time. That sequence can get complex and hard to predict, modulated by conditionals, loops, side effects, and other nested contexts.

Here's the code refactored to favor values over variables:

import {orderBy} from "lodash";

function getRankedIncompleteTodos(allTodos: Todo[], pinnedTodos: Todo[]): Todo[] {
    const incompleteMatchingTodos = allTodos.filter(todo => !todo.completed);
    const sortedTodos = orderBy(incompleteMatchingTodos, todo => todo.importance, "desc");
    return [...pinnedTodos, ...sortedTodos];
}
Enter fullscreen mode Exit fullscreen mode

In this code, I've given intermediate values their own descriptive labels. Each label stands for a single value and doesn't change. So to understand what incompleteMatchingTodos or sortedTodos contains at a line, we need only look at their declarations. We don't need to take time into account. It reduces cognitive burden.

Which would you rather read, write, and maintain? Take some code written with variables and convert it to values. See the difference for yourself. Then multiply the difference over a whole codebase.

This rule of thumb applies across many (imperative) programming languages. And some languages have ways of (partially) enforcing it. In JavaScript we would say "favor const over let." In Java, "use final for local variables and parameters". In functional languages, immutability is enforced by default 😎.

As usual, a rule of thumb has exceptions. In my experience, the majority of code in mainstream, high-level languages benefits from favoring values over variables. But I don't argue for trying to shoehorn it. Sometimes performance-sensitive/low-level/library code fits variables better, or if you're writing some unusual imperative algorithm. Most of the time though, especially in web apps and CRUD, we're just taking some data, map/filter/reducing it in some way, and shuffling it on to the next step.

Top comments (0)

Take a look at this:

Settings

Go to your customization settings to nudge your home feed to show content more relevant to your developer experience level. 🛠