What Is a Magic Number And How Do We Fix It?

What Is a Magic Number And How Do We Fix It? Featured Image

Magic numbers are one of those bad practices in programming that have somewhat of a weird name. After all, folks generally like magic, so it’s unclear what’s so bad about them. Fortunately, today we’ll talk about what a magic number is and how to remove them from your programs.

Table of Contents

Introducing Magic Numbers

Chances are you’ve found yourself here because a nice static analysis tool like a linter told you your code contains a magic number. Without context, that term is pretty weird. After all, nothing about programming is magic, though it can feel like it sometimes, so what’s the big deal?

In short, a magic number is a numerical value (typically excluding 0 and 1) that has an unclear purpose. For example, we might be computing the area of a circle with an approximation of pi as follows:

def area_of_circle(radius: float) -> float:
  return 3.14 * radius * radius

In this example, our approximation of pi is what would be considered a magic number. It’s a problem because it’s not exactly clear what purpose 3.14 serves in our calculation. In other words, it seemingly spawned from nothing.

Surely, a lot of folks know the area formula for a circle or the value of pi, so they could probably figure it out from context. That said, given how bad our brains are at holding information in short term memoryOpens in a new tab., we should really be trying to leave as little as possible to inference.

As a result, a magic number is considered bad practice because it makes code harder to reason about. Therefore, we should find ways to remove them from our code whenever possible.

Removing Magic Numbers From Code

In our previous example, we had a method which computed the area of a circle given some radius:

def area_of_circle(radius: float) -> float:
  return 3.14 * radius * radius

The problem as already discussed is that 3.14 is a magic number. To get rid of the magic number, we need to make a constant for it:

PI = 3.14

def area_of_circle(radius: float) -> float:
  return PI * radius * radius

The trick is to take the seemingly random value and give it some context by providing it a name. In this case, we stored the value of 3.14 in a constant named PI.

Benefits of Eliminating Magic Numbers

Given how trivial the examples were in this article, you might conclude that addressing magic numbers is a waste of time. Let me take a moment to try to convince you of the benefits.

First, as mentioned already, one of the major benefits of removing magic numbers from your code is readability. Numbers can have many meanings which can be cleared up with a simple name. This will save you time in the future when you inevitably have to make sense of your own code.

Second, another major benefit of removing magic numbers is inadvertently following the Don’t Repeat Yourself (DRY) principle. Under this principle, you try to limit duplication of code (e.g., by not using the same magic number multiple times). A nice consequence of following DRY is creating a single point of control where a magic number can be changed as needed. For example, imagine if we had both a circle area method and a circle circumference method:

def area_of_circle(radius: float) -> float:
  return 3.14 * radius * radius

def circumference_of_circle(radius: float) -> float:
  return 2 * 3.14 * radius

If for some reason we decided we wanted pi to a few more decimal places, we would have to update it twice, once for each method. Alternatively, we could use our constant to update it once in both places:

PI = 3.14159

def area_of_circle(radius: float) -> float:
  return PI * radius * radius

def circumference_of_circle(radius: float) -> float:
  return 2 * PI * radius

If neither of these benefits seem worth it to you, I recommend turning off that particular warning on your linter. What you absolutely should not do is find some creative workaround. For example, maybe you have a list where you know the exact indices of the contents:

cards = ["jack", "queen", "king", "ace"]
ace = cards[3]

One thing you shouldn’t do is something like this:

cards = ["jack", "queen", "king", "ace"]
ace = cards[1 + 1 + 1]

Sure, adding one three times sums to three, but I would argue this solution is even more confusing than using the magic number directly. Unless you’re trying to purposely obfuscate your code, this is moving in the wrong direction.

The Power of Best Practices

While best practices vary from language to language and team to team, they’re generally for the greater good. In this case, magic numbers are one of those warnings that actually hint at ways you can improve your code.

With all that said, I don’t recommend following rules for the sake of following rules. Very few rules in software development have been empirically studied, so use your best judgment and avoid falling prey to dogma.

Finally, I know many of the folks reading this will be my Java students, and I don’t want to leave you hanging, so here’s how you remove a magic number in Java:

public static double AreaOfCircle(double radius) {
  // Create a local constant using the final keyword
  final double pi = 3.14;  
  return pi * radius * radius;
}

Or alternatively:

// Create a global constant using the final keyword
private static final double PI = 3.14; 

public static double AreaOfCircle(double radius) {
  return PI * radius * radius;
}

With that said, that’s all I have for us today. If you found this article useful, I’d appreciate it if you gave it a share. Likewise, you can find other ways to support The Renegade Coder here.

If you’re looking for more to read, look no further than the following list:

Otherwise, thanks for stopping by! See you next time.

Coding Tangents (43 Articles)—Series Navigation

As a lifelong learner and aspiring teacher, I find that not all subjects carry the same weight. As a result, some topics can fall through the cracks due to time constraints or other commitments. Personally, I find these lost artifacts to be quite fun to discuss. That’s why I’ve decided to launch a whole series to do just that. Welcome to Coding Tangents, a collection of articles that tackle the edge case topics of software development.

In this series, I’ll be tackling topics that I feel many of my own students have been curious about but never really got the chance to explore. In many cases, these are subjects that I think deserve more exposure in the classroom. For instance, did you ever receive a formal explanation of access modifiers? How about package management? Version control?

In some cases, students are forced to learn these subjects on their own. Naturally, this forms a breeding ground for misconceptions which are made popular in online forums like Stack Overflow and Reddit. With this series, I’m hoping to get back to the basics where these subjects can be tackled in their entirety.

Jeremy Grifski

Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Then, he earned a master's in Computer Science and Engineering. Today, he pursues a PhD in Engineering Education in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch and Phantasy Star Online 2, practicing trombone, watching Penguins hockey, and traveling the world.

Recent Posts