DEV Community

loading...
Cover image for Avoid Magic Numbers to Increase Code Readability

Avoid Magic Numbers to Increase Code Readability

tbutterwith profile image Tom Butterwith ・2 min read

What are magic numbers?

Magic numbers are any numeric constant declared in your code. They could help you convert minutes to seconds, set limits for the number of all records in a database, or repeat a process a constant number of times. In any case, you'll see things like for(const i = 0; i < 10; i++) or return x * 60.

Why should you avoid them?

On their own, these numbers carry no explicit meaning, the developer has to infer what they are from the context they are used. This might be easy, everyone knows there are 60 seconds in a minute. Others, like the example for converting kilometres to miles below, are more obscure.

What should you do instead?

Consider this example. On its own, 0.6213712 carries very little meaning. We can establish from the method name that this might be the number of miles in a kilometre but without looking it up or a code comment, we can only assume.

By giving this value its own variable with a helpful name, we're writing self-documenting code that gives the next person to read this code all the information they need to know exactly what a value is.

Example one

// Before: large float in the middle of a function
const convertKilometersToMiles = (distanceInKm) => {
    return distanceInKm * 0.6213712;
}

// After: The function is easier to read at a glace. 
// Anyone reviewing the code can clearly see the intent of the function
const MILES_IN_KILOMETER = 0.6213712;

const convertKilometersToMiles = (distanceInKm) => {
    return distanceInKm * MILES_IN_KILOMETER;
}
Enter fullscreen mode Exit fullscreen mode

Example two

// Before
if (users > 10) {
    throw new Error("too many users");
}

// After
const CONCURRENT_USER_LIMIT = 10;

if (users > CONCURRENT_USER_LIMIT) {
    throw new Error("too many users");
}
Enter fullscreen mode Exit fullscreen mode

Discussion (3)

pic
Editor guide
Collapse
paddy3118 profile image
Paddy3118

I get what is meant, but remember, some constants have a meaning all their own, and other canstants gain meaning from context.
Examoles: 0, you could declare zero = 0 but it is less clear. Similarly for 1. 2 in 2**whatever expecially in a Boolean context. And leaving the most important to the end: 42.

Collapse
lexlohr profile image
Alex Lohr

A number is only "magic" if it has a meaning beyond its value, e.g. measurements, limitations, positions, etc.

Especially your example of 2**whatever can be extremely magic, if you use it to store flags inside an integer, like for example TOS inside IP headers:

const parseTos = (tos) => [tos & 7, !!(tos & 8), !!(tos & 16), !!(tos & 32)]

const PRECEDENCE = 7 // bit 0-2
const LOW_DELAY = 8
const HIGH_THROUGHPUT = 16
const HIGH_RELIABILITY = 32

const parseTosWithoutMagic = (tos) => [
  tos & PRECEDENCE,
  !!(tos & LOW_DELAY),
  !!(tos & HIGH_THROUGHPUT),
  !!(tos & HIGH_RELIABILITY),
]
Enter fullscreen mode Exit fullscreen mode

As you can see, the code is more verbose, but also far easier to understand.

Collapse
paddy3118 profile image
Paddy3118

A number is only "magic" if it has a meaning beyond its value, e.g. measurements, limitations, positions, etc.

I agree in your particular example, but take, for example indexing a Python list from the beginning - You see, and what is readable is to use integer 0. Want all odd members, use [1: - 1: 2]; all but the last, [:-2]. Some of these constants with meaning are baked into the Luton documentation.
These 'suggestions' should be known, but one needs to read a lot of Python to see when they are and are not applied to good effect. Definitely not backed into a linter for example, or taught as hard and fast rules.