Software Design/Don't reuse a variable

Checklist question:

  • Each variable is used for a single purpose and is not "recycled"?

This practice corresponds to rule Don’t use a variable for two unrelated purposes from C++ Core Guidelines.

Reusing a variable for multiple purposes may be seen as a violation of the Single responsibility principle on the level of variables.

Why edit

Reusing local variables contributes to cognitive load by creating an implicit dependency within a function: readers have to mentally disambiguate between the different meanings behind the variable name when they see a use of the variable.

Using a single variable for multiple purposes misses an opportunity to reflect the semantics behind each purpose of the variable's usage more accurately. See practice Name a variable semantically.

Reusing a local variable might be puzzling[1] unless there are code comments explaining why the variable is reused. For example, in complicated loops, reusing local variables makes readers question themselves whether this is actually an "unmotivated" variable reuse or, rather, there are some execution paths where the variable is not reassigned and therefore is not reused. Or, whether the latter used to be the case but after some changes in the code the variable reuse has become pointless.

Why not edit

Reusing variables in complicated functions edit

Sometimes, in long and complicated functions, it might be actually better to reuse a variable to refer to similar things when the variable is used several times to refer to each thing, especially when these uses span more than a few lines of code. This is because when there are more different variables declared in the fuction there is a higher risk to mistakenly use a wrong variable. Each such reuse should be commented. For example:

var item = itemOne()
doSomething(item)
if (...) {
  ...
  doSomethingElse(item)
  ...

  // Reusing variable `item`. The previous value (itemOne) is not used below.
  item = itemTwo()
  doSomething(item)
  for (...) {
    ...
    doSomethingThird(item)
  }
}

In languages that support variable shadowing (such as C++ and Rust) shadowing should be preferred to reuse in such cases and the variables are preferably made unmodifiable.

Functions so long and complicated that they warrant such variable reuse are problematic themselves: see practice Break up too large and complex functions.

Note that in the example above, it's not possible to make separate scopes for both different uses of item variable because the scope of the second use belongs to an if block, and the first use of item begins outside of this if block. Extracting the section of code with the second use into a separate function might reduce the runtime efficiency if this part of the code has multiple logical outputs and the programming language doesn't support efficient multivalued returns from functions.

A long and complex function may not be necessary to justify using of variable shadowing: for example, John A De Goes advocates for always shadowing variables whenever possible, so that there is at most one value of any type in the scope at any given time.[2]

Reusing variables in repetitive snippets edit

If a function includes multiple repetitive parts that in separation require some additional variables it might be better to reuse these auxiliary variables for the same reason of avoiding the risk of mistakes as explained in the previous section regarding reuse of variables in complicated functions. It might not be possible to extract such snippets as functions if the programming language doesn't support multivalued returns from functions. The typical example is swapping two variables:

// Swap x and y
var tmp = x
x = y
y = x
... later within the same function:
// Swap foo and bar
tmp = foo // Reusing variable `tmp`
foo = bar
bar = tmp

If the programming language supports naked scoping blocks such snippets could be put each into a separate scope to avoid reusing tmp variable:

// Swap x and y
{
  var tmp = x
  x = y
  y = tmp
}
... later within the same function:
// Swap foo and bar
{
  var tmp = foo
  foo = bar
  bar = tmp
}

However, this approach increases the size of the function and to the overall noisiness, especially if the repetitive snippets are short and there are many of them in the function.

Related edit

References edit

  1. Refactoring: Improving the Design of Existing Code (2 ed.). 2018. ISBN 978-0134757599. https://martinfowler.com/books/refactoring.html.  Chapter 9, "Split Variable" section
  2. A De Goes, John (October 26, 2019). "12 Steps to Better Scala". Hamburg, Germany: Scala Hamburg. "Prefer One Type Per Scope"