When someone refers to a magic numbers in software, they probably have one of the following three scenarios in mind:
- A value that is used to determine the behavior of the code, but the value itself provides no meaning
- A value that has been hard coded in several locations in the program, that is unlikely to change, but might
- A value that is well known in some software and that will not be changing
Using a magic number directly in your code can lead to problems and it is recommended that you avoid or minimize the use of them. Let’s examine each of the three types of magic numbers and the risks associated with using them.
Example One with Magic Numbers
The first type of magic number in the above example is the value for the AccountType. When looking at the code, most programmers will not know what the values of 1, 2, and 3 represent. This type of magic number is common when the programmer is comparing the AccountType to a value that has come from a database or file and the value was persisted as a 1, 2, or 3. But many programming languages today support the concept of an enumeration, or enum, that can be used to make the code more readable:
Example Two Eliminating A Magic Number
Using an enumeration in place of a hard-coded magic number offers several advantages:
- The code is easier to understand for humans. The code is easier to write for humans because we don’t have to reference documentation elsewhere to know what an AccountType of 1, 2, or 3 means.
- It is easier to alter a value. If the source of the data decided that the value 2 should represent “No Taxes” and 3 should represent “No Federal Tax”, the code could be adjusted where the enumeration is defined. That is much easier than searching all of the code to replace 2s with 3s and 3s with 2s.
- It is easier to review the code where the enumeration containing all of the possible values are defined for accuracy and completeness.
The same block of code above also includes another type of magic number with the values of 1.1 and 1.03 to represent tax rates. I suspect you already realized that hard-coding the tax rate is risky because the values change so often. But some magic numbers are less obvious such as in this example:
Example Three with a Magic Number
In this example the magic number is the number 50. There are 50 states in the USA as I am writing the article, but that number could change. If you have coded the number 50 representing states in several places in your program, someone will need to change them all if that number of states changes.
Example Four Without Magic Numbers
Using constants to eliminate this type of magic number provides several advantages:
- The code is easier to understand for humans. We know right away that we are looping through the number of states and don’t have to question what the value of 50 represents.
- It is easier to alter the value. If the USA adds one more state we can change the constant defined in one place in the code and be done with our changes. A programmer could do a find and replace across all of the code on the value 50 easily enough, but the number 50 might be used in other places referring to something other than the number of states in the USA.
- A developer could accidentally type 60, 500, or even 49 instead of 50 and the compiler would allow the code to compile without warning. This problem is less likely when the important value is only coded once and a variable name is used throughout the program.
The third class of magic numbers are numbers that indicate types of files or the beginning and ending of streams of bits in protocols. These magic numbers are generally dependable, but you should still avoid coding them in the flow of your code logic primarily for readability. One example is the use of FFD8 and FFD9 at the beginning and ending of JPEG files. Instead of hardcoding this magic number inline in the code:
Example Five With Magic Numbers
Consider defining a constant to improve readability.
Example Six Without Magic Numbers
There are some drawbacks to eliminating magic numbers, but the benefits of eliminating them usually outweighs the drawbacks. Drawbacks include:
- The programmer might want to know the value of the constant or enumeration and would need to refer to where the value is defined to discover it. This could take a little time if the constant or enumeration is not defined closely to where it is being used.
- It makes the source code a little bit longer. In compiled languages, this is almost never a concern, but it non-compiled languages like javascript it might have a small impact on performance.
Go to Robert’s Rules of Coders for more.