25

So every number in the code that we are sending to a method as an argument is considered as a Magic Number? To me, it shouldn't. I think if some number is let's say it is for minimum length of user name and we start using "6" in the code...then yeah we have a maintenance problem and here "6" is a magic number....but if we are calling a method that one of its arguments accept an integer for example as the ith member of a collection and then we pass "0" to that method call, in this case I don't see that "0" as a magic number. What do you think?

3
  • 5
    In your example, what does the 0 represent? Commented Jan 8, 2013 at 18:31
  • 3
    In the case you illustrate, that "0" has no magical properties whatsoever. Commented Jan 8, 2013 at 20:21
  • 6
    Everything except 0,1 and 42 is magic
    – Mawg
    Commented Dec 18, 2014 at 14:02

8 Answers 8

50

If the meaning of the number is very clear in the context, I don't think it's a "magic number" problem.

Example: Let's say you're trying to get the substring of a string, from the beginning to some token and the code looks like this (imaginary language and library):

s := substring(big_string, 0, findFirstOccurence(SOME_TOKEN, big_string));

In this context, the meaning of the number 0 is clear enough. I suppose you could define START_OF_SUBSTRING and set it to 0, but in this case I think it would be overkill (although it would be the correct approach if you knew that the start of your substring might not be 0, but that depends on the specifics of your situation).

Another example might be if you are trying to determine if a number is even or odd. Writing:

isEven := x % 2;

is not as strange as:

TWO := 2;
isEven := x % TWO;

Testing negative numbers as

MINUS_ONE := -1;
isNegativeInt := i <= MINUS_ONE;

also feels weird to me, I'd much rather see

isNegativeInt := i <= -1;
11
  • 9
    To throw another example on there, in code where you're explicitly working with degrees on a circle, it would be fair to use a number such as 360 to mark a full rotation with the understanding that most people will know what that means (though this is a case where it wouldn't hurt to provide a constant)
    – KChaloux
    Commented Jan 8, 2013 at 19:00
  • 14
    KChaloux: If I could, I would -1 your comment. 360 is a magic number. If 360 happens to be a value for another constant, then you have 2 sets for 360 that are unrelated and indistinguishable. Junior comes along, go "That is a magic number", global search and replace 360 with "Degrees_in_Circle", run all unit and regression tests, all pass - delivers code fix. Code now a dogs breakfast, and we all know what happens to that after a short time.......
    – mattnz
    Commented Jan 8, 2013 at 20:17
  • 8
    @mattnz: Hopefully that kind of large-scale code change would be quickly caught (hopefully during code review, if they're that junior) long before it ever got into production. I think someone who would do that in that context would probably also replace 0 in the context of the my substring example. In which case, this might be the least amount of damage they can cause. It's been a long time since I did any coding that did geometric calculations, but generally, the values 15, 30, 45, 60, 90, 180, 360 were constants that were accepted. I've never seen anyone define FIFTEEN_DEGREES,... Commented Jan 8, 2013 at 20:29
  • 5
    @KChaloux The example may actually fall apart if there's a shift from Degrees to Radians. By 360, you are expressing 1 complete rotation. Since there are multiple representations for the same value, it should be pulled out. Especially considering 360PI could look the same as 2PI (180 rotations but still pointing the same direction in the end), or 360 rotations the same as 1 rotation, but side effects may be different.
    – CLo
    Commented Jan 8, 2013 at 21:36
  • 19
    A bit of a straw man there, TWO and MINUS_ONE are utterly bad because replacing a magic number with its rendering in text is OF COURSE idiotic. The name of the constant has to convey its meaning. Except your examples are about fundamental facts about numbers, closely tied to just those specific numbers, so there isn't really any meaning beyond that. Commented Jan 10, 2013 at 16:15
20
bool hasApples = apples > 0;

It is obvious zero means absence. I find 0 easier to understand than a variable named "absenceValue".


for(int i=0; i < arr.length; i++)

It's obvious 0 is the starting position. I would be confused by a variable named "firstPosition". Such a variable would make me wonder if the starting position could change.

17

I would suggest three key factors in deciding whether something should be a constant declaration:

  1. Is the number something that is precisely and concisely representable
  2. Are there any plausible scenarios under which the value would have to change, but the code would not have to be rewritten
  3. Would someone who sees the number be apt to recognize it more quickly or less quickly than someone who sees a named constant

Something like pi should probably be written as a named constant, rather than as a numeric literal, since a numeric literal is apt to be needlessly verbose, needlessly imprecise, or both. Something like the number of slots in a cache should likely be a named constant (though see note below) to allow for the possibility of expanding the cache without having to modify all the code that uses it. Things like the numbers "4", "28", and "29" in the statement if ((year % 4)==0) FebruaryDays = 29; else FebruaryDays = 28; should probably not be named constants, since the expression is almost certainly more readable than if ((year % YearsBetweenLeapYears)==0) FebruaryDays = FebruaryDaysInLeapYear; else FebruaryDays = FebruaryDaysInNonLeapYear;. Note that the maintainers of standards have indicated that the length of February 2100 in that year will not match the above formula, but I would recommend not handling that case unless it would be the only impediment to correctly handling such dates (i.e. the code won't get tripped by integer overflow or other such problems).

An important caveat with rule #2 is that in some cases code may rely upon hard-coded numbers in a way which cannot readily be represented by a named constant. For example, a method which computes a cross product of two vectors passed as discrete parameters will only be meaningful when used on three-dimensional vectors. The required number of dimensions is not a value that could meaningfully be changed without completely rewriting the routine. Even if one foresaw a possible need for computing the cross product of three 4-dimensional vectors, using a named constant for the value "3" would do little to make it easier to satisfy that need.

4
  • I call people not handling leap years correctly for all years “halfwits”. People who do it intentionally - I have no name for that. Saboteur maybe?
    – gnasher729
    Commented Aug 14, 2021 at 12:17
  • @gnasher729: Most real-time clock chips have a decimal year counter that only goes from 00 to 99, and will advance from 00-02-28 23:59:59 to 00-02-29 00:00:00, and the same continues to be true of many real-time clock modules that are being incorporated into new designs. The only way dates will possibly be able to work correctly in 2100 will be if either (1) a system is modified to use a different clock chip, which would likely require reworking the system's time/date logic; (2) the system is modified so that after 2100-02-28 it expects all RTC dates to be off by one, ...
    – supercat
    Commented Aug 14, 2021 at 17:56
  • ...which would require reworking the time/date logic, or (3) governments decide to make 2100 be a leap year. I suppose that having a separate function to handle conversion between linear time and RTC date-time, versus conversion between linear time and human-readable time might be better than using one function for both purposes, but my point is that any of three incompatible approaches may end up being needed to fix the problem, and I have no way of knowing which one would actually end up being correct.
    – supercat
    Commented Aug 14, 2021 at 17:59
  • @gnasher729: If I were to change a program today so that the date would default to 21-01-01, and years from 00 to 20 would be interpreted as 2100-2120, that would at best increase by about 20% the number of years the software could run without needing a change to its date-handling logic, but if on the first reboot after the upgrade the year read 20 or less the system would have to determine whether the system had previously been using old software (in which case the date should probably be set to a default 21-01-01) or new software (meaning the date should be interpreted as 2100+).
    – supercat
    Commented Aug 14, 2021 at 18:24
5

This, as all principles, is a matter of degree. Generally speaking, number literals in source code are more suspect the larger they are. A maximum length like 10 or a memory address like 0x587FB0 are obviously bad practice - it is almost certain that sooner or later you will have to repeat these values more than once, creating a risk of incompatibility and subtle errors introduced in places that weren't changed.

0 is at the other end of the scale; it is still suspect but not quite as much. Are you using 0 as a sentinel value? Then you should probably use a symbolic constant instead, just because the constant can explain what it means. Is it an extremely entrenched cultural agreement such as "0 means successful completion"? That's probably OK. Does it mean "the first item in a collection"? That may be harmless, but if there is an alternate method such as first() I'd probably prefer that.

2
  • 2
    "Are you using 0 as a sentinel value?" <-- Can you explain what you mean by "sentinel" here? I can't find a definition that seems to match.
    – rory.ap
    Commented Nov 17, 2015 at 15:08
  • @rory.ap "Sentinel" in this context means "a thing in its own right", independent of the numeric value "0". COBOL programmers often used the character literal "1", assembler programmers frequently used 0xff, C programmers often followed the Standard Library string model of 0, etc. Commented Nov 23, 2020 at 22:17
5

Every unnamed number that's not immediately obvious from context is a magic number. Its a little silly to define numbers that have meaning that is immediately obvious from context.

In django (python web framework), I may define some database field with a raw number like:

firstname = models.CharField(max_length=40)
middlename = models.CharField(max_length=40)
lastname =  models.CharField(max_length=40) 

which is clearer (and the recommended practice ) than say

MAX_LENGTH_NAME = 40
...
firstname = models.CharField(max_length=MAX_LENGTH_NAME)
middlename = models.CharField(max_length=MAX_LENGTH_NAME)
lastname =  models.CharField(max_length=MAX_LENGTH_NAME) 

as I'm unlikely to ever need to change the length (and can always compare to the max_length of the field). If I do need to change the length of the field after initially deploying the application, I need to change it in exactly one location per field in my django code, and then additionally write a migration to change the schema of the DB. If I ever need to reference max_length of a defined field of a type of object, I can do it directly -- if those fields were defining a Person class, I can use Person._meta.get_field('firstname').max_length to obtain the max_length being used (which is defined in one place). The fact that the same 40 was used for multiple fields is irrelevant as I may want to change them independently. The length of firstname should never depend on the length of middlename or lastname; they are separate values and can change independently.

Often array indices can use unnamed numbers; like if I have a CSV file of data that I want to put in a python dictionary, with the first element in the row as the dictionary key I would write:

mydict = {}
for row in csv.reader(f):
    mydict[row[0]] = row[1:]

Sure I could name index_column = 0 and do something like:

index_col = 0
mydict = {}
for row in csv.reader(f):
    mydict[row[index_col]] = row[:index_col] + row[index_col+1:]

or worse define after_index_col = index_col + 1 to get rid of the index_col+1, but that doesn't make the code clearer in my view. Also, if I give the index_col a name, I better make the code work even if the column is not 0 (hence the row[:index_col] + part).

9
  • 9
    Actually, max_lngth=40 vs. max_length=MAX_LENGTH_NAME is a classic examlple of a magic number that screams to be a symbol. The day will come when you want to support 45 character names, and now every use of "40" is suspect and must be carefully examined. Commented Jan 9, 2013 at 0:21
  • 3
    @RossPatterson - This is not C where we constantly compare against a global var MAX_ARRAY_SIZE, but a decent web framework. The only place that the magic number comes up is where you declare the database model; everything else is compared against this value (e.g., 40 appears nowhere else in the code). Also note, you can't change this variable easily without doing schema migrations as its tied to a DB. If I wanted to change to say 1 character middle names its immediately obvious the one place to change in the code 40 to 1. You have to think of context.
    – dr jimbob
    Commented Jan 9, 2013 at 7:33
  • 2
    Sorry, you're wrong on two points. First, the OP asked a "programming practices" question that doesn't specify any language. They said "method", not "function", so let's presume something object-oriented, but that doesn't take us out of the realm of magic-numbered data. Second, if the magic number is baked into the database (e.g., the schema), then it's even worse to have it in code. The right thing to do is to get the magic almost-a-constant from its source - either the database itself or a schema module that centralizes all those constants-that-will-vary-over-the-code's-lifetime. Commented Jan 9, 2013 at 11:58
  • 1
    @RossPatterson, the main reason we avoid magic numbers is to make code more legible (That's why we call them "magic" - we don't know what they do in their context). Turning 40 into a constant purely to DRY up the literal is a different, but often overlapping concept, and comes with all of the benefits and pitfalls of DRY code. Commented Nov 23, 2020 at 17:31
  • 1
    @RossPatterson I've done some more research, and have found entire articles talking about magic numbers the way I've defined them, and different articles talking about them the way you've defined them. Apparently we have one phrase in the community to portray two separate but similar ideas - that never ends well. This means 40 in max_length=40 is magical depending on who we're talking too, and it's unfair to say half of the community is wrong because our co-workers use a certain definition (so sorry for calling you wrong). Commented Nov 24, 2020 at 17:58
2

Sometimes numbers in code will never be changed. With integer values, using symbolic names would obscure the code. For example:

   radians= (degrees * M_PI) / 180;

and

   degrees_fahrenheit= 32 + (degrees_celsius * 9) / 5;

Having code reflect the conversion equation maximizes readability.

1

I've been reading the answers raising my eyebrows. I think all of them, including the Wikipedia page, are wrong.

To me a magic number is something very specific that has nothing to do with whether a constant has been defined for it or not. And I did not make this up myself.

A magic number is the computer science equivalent of in-band signaling. It means there is a number in a domain (say, integers) that gets assigned (semantically) a different meaning than its value. This is always problematic and often a hack to overcome some shortcoming in the programming language, it is an optimization or it's just a lazy shortcut.

Example: You have a variable Age and you want to abuse it to tell whether the person is still alive or not. This is where you go wrong already but you may be living in the 1960s and be short on memory. You come up with the idea of using 200 to signal someone is dead. That would make 200 a magic number.

In this case it may be obvious there is something about 200 because nobody makes 200 but the potential problems are obvious. You have to be aware of this hack every time you do something with ages. You can no longer just calculate an average age without being aware of it for example.

The classic example of this being a problem is an old AT&T phone system that used a particular frequency not often used in speech to terminate the call. This did not work out well, people whistling over the phone was enough to drop the call.

2
  • 1
    "I've been reading the answers raising my eyebrows. I think all of them, including the Wikipedia page, are wrong." That's a very strong claim, you should definitely back up your claims with references which could support them. Commented Aug 14, 2021 at 21:52
  • @VincentSavard My understanding of the term and the anecdote come from one of Andrew Tanenbaum's books, probably Computer Networks. I did some searches.and found a wikipedia page that links magic numbers to in-band signaling: en.m.wikipedia.org/wiki/In-band_signaling This does however only name file identifiers as an example,, not their usage in code. Commented Aug 14, 2021 at 23:59
0

As in everything, context is everything. A couple of simple rules of thumb should be kept in mind: 1) the more frequently it is used, the more likely it is to deserve a name, 2) if the reason why it is that number instead of another number isn’t clear without knowing more than is in the function/method, it deserves a name to explain it.

0 seldom needs a name, because it’s generally clear from context, but there can certainly be circumstances where it’s unclear, where a name or a comment is necessary to explain why it was used. Determining even/odd with a modulo 2 is clear, giving a 50% discount might not be clear whether it is done with a multiplication or a division. Context is everything.

In your specific example, zero would represent the first element of the collection, but you might want to have a name that explains why the first and not the last…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.