Antipattern #1 — Bigger scope than needed

Why defining variables upfront is a bad idea.

Luís Soares
2 min readOct 16, 2024
Photo by Wesley Ford on Unsplash

🦄 The myth

I remember one of my university teachers telling us to declare variables at the beginning of functions in C. This was due to language limitations. However, the C99 release (in 1999, before my degree) allowed declarations to be mixed within the code. It’s not the 90s anymore, but the myth persists, even in higher-level languages.

func prepareData() {
var (
name string
age int
height float64
isAdult bool
)
// ... code
// ... more code
// using the variables defined way above, which could have been mutated meanwhile
}

Go has var blocks (JavaScript and Swift also do), encouraging defining all the variables upfront. Indeed, you’re not obligated to use var blocks, but I’ve seen it dozens of times in practice. The usage patterns that a programming language promotes are more relevant than its feature set. Even using var blocks to define sentinel errors is not worth it because it hinders moving them around. On a related note, Go’s named return values also increase variable scope unnecessarily.

🧯 Lowering the scope

Global variables are never needed and are easy to remove, but var blocks in functions also have a broader scope than required. Remove them by declaring/initializing variables as late as possible. This clamps down their scope, reducing the likelihood of bugs (probably, shared mutable state is the most common source of bugs); fewer things can mess with them without you noticing.

Even better than low scope is no scope: inline variables when invoking other functions/constructors. This is easy in languages with named parameters (e.g., Python, Kotlin, Ruby, C#) but also feasible using DTOs (e.g., Go structs, JavaScript objects).

Variables' scope

🚨 Maximum

Global
Package
Function - beginning of function
Function - beginning of a block
Function - right before usage
Inline (no variable)
✅ Minimum

Defining variables upfront has no benefits. Limiting the scope to the bare minimum is always good, including in testing.

🧶 Code cohesion

Reducing scope increases code cohesion because declaration, initialization, and usage are close; code that changes together stays together (look at “Locality of behavior”).

In physics, the principle of locality states that an object is influenced directly only by its immediate surroundings. Principle of locality

📖 Code as documentation

Reducing scope enhances code self-documentation and readability. Since context provides meaning, you know immediately what the variable is serving and what it is not just by looking at its surroundings. By defining variables as late as possible, you’re helping readers understand a story that respects the natural flow of code, introducing characters only when they enter the scene. For example, a variable may not even be used because of some early return, so you’re just confusing the reader.

--

--

Luís Soares
Luís Soares

Written by Luís Soares

I write about automated testing, Lean, TDD, CI/CD, trunk-based dev., user-centric dev, domain-centric arch, ...