28

During development phase, there are certain variables which need to be the fixed in the same run, but may need be modified over time. For example a boolean to signal debug mode, so we do things in the program we normally wouldn't.

Is it bad style to contain these values in a constant, i.e. final static int CONSTANT = 0 in Java? I know that a constant stays the same during run time, but is it also supposed to be the same during the whole development, except for unplanned changes, of course?

I searched for similar questions, but did not find anything that matched mine exactly.

GregT
  • 407
  • 1
  • 4
  • 7
  • 11
    I'm curious, why would you believe it to be bad style to change these? – Vincent Savard Jan 08 '18 at 13:58
  • 1
    As long as it is a constant for the entire run-time of your application, I see no harm. – Berin Loritsch Jan 08 '18 at 13:59
  • @VincentSavard A senior colleague told me that if something may be changed, it should not be `final static`. – GregT Jan 08 '18 at 14:00
  • 36
    Unless you are modeling physical properties with constants that have known mathematical values, **everything** can change at some time. – Berin Loritsch Jan 08 '18 at 14:04
  • @GregT, after giving it some thought, there are cases where your senior colleague would be correct. You can see the end of my answer for more clarification. – Berin Loritsch Jan 08 '18 at 14:30
  • 19
    software is *soft*. – Erik Eidt Jan 08 '18 at 15:58
  • 2
    In Java, be sure to recompile all classes where the constant itself or dependent constants are used, hopefully done by your IDE / toolchain. – Ralf Kleberhoff Jan 08 '18 at 17:56
  • 1
    @BerinLoritsch I think that even some physical constants can (theoretically) change over time. Is nothing sacred? –  Jan 08 '18 at 18:08
  • 10
    @GregT I would disagree. `final` gives you a compiler-enforced guarantee that the program won't modify the value. I wouldn't dispense with that just because the programmer might want to modify the value assigned in the source code. – Alexander Jan 08 '18 at 18:24
  • 10
    Not much time to articulate a full answer, but I suspect your colleagues concern isn't so much with constants, but with the code-inlining of configuration values, which might tend to manifest as constants. ... Constants protect you against stupid mistakes, like accidentally assigning over `gravity` mid-game/run. They don't necessarily mean, `gravity` is the same on every planet ... That said, the healthy solution is to make `gravity` a constant, but pull it from a `planet` file or database at the start of the relevant scope. – svidgen Jan 08 '18 at 21:02
  • 4
    Wait, your program has `final static int NUMBER_OF_RUNS = 5;` but mine has `static final int NUMBER_OF_RUNS = 50;`! Are constants supposed to be the same in different programs? – user253751 Jan 09 '18 at 02:52
  • @immibis different programs wouldn't even see those names in each other. If the code from each was linked together into a 3rd program you'd need some kind of namespace to make them unique. That's an entirely different issue then dealing with multiple users of a public constant that have different expectations of what it's value should represent. – candied_orange Jan 09 '18 at 13:59
  • @BerinLoritsch even our approximations of those constants change over time as experimental observations refine thier precision. (Thinking physical constants, not pure mathematical constants) – casey Jan 09 '18 at 16:02
  • 3
    @CandiedOrange Nor do different versions of the "same" program see those names in each other. – user253751 Jan 09 '18 at 21:59

7 Answers7

85

Anything in your source code, including const declared global constants, might be subject to change with a new release of your software.

The keywords const (or final in Java) are there to signal to the compiler that this variable will not change while this instance of the program is running. Nothing more. If you want to send messages to the next maintainer, use a comment in source, that's what they are there for.

// DO NOT CHANGE without consulting with the legal department!
// Get written consent form from them before release!
public const int LegalLimitInSeconds = ...

Is a way better way to communicate with your future self.

nvoigt
  • 7,271
  • 3
  • 22
  • 26
  • 12
    I really liked this comment to the future self. – GregT Jan 08 '18 at 14:15
  • Note that constants are not necessarily global. E.g., [JavaScript constants](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const) ... Also, why wouldn't the "constant" in your example be pulled from a config or a database? – svidgen Jan 08 '18 at 20:41
  • 4
    `TaxRate` being `public` makes me nervous. I'd like to know for sure that only the sales department in impacted by this change and not also our suppliers who charge us a tax. Who knows what's happened in the code base since that comment was written. – candied_orange Jan 08 '18 at 20:41
  • @CandiedOrange In that case, you get the other advantage of constants - you can easily search for all of their uses in a good IDE. – IllusiveBrian Jan 08 '18 at 21:24
  • 3
    @IllusiveBrian wasn't criticizing use of constants. Was warning against trusting a comment to be up to date. Always be sure of how something is used before you change it. – candied_orange Jan 08 '18 at 21:40
  • In a case like this, I would prefer config files. No notes to future selves, just config files for different environments. – Seiyria Jan 09 '18 at 04:05
  • 8
    This is good advice *for Java*. It may be different in other languages. For example, because of the way const values are bound to the call site in C#, `public const` fields should *only* be used for things that will never-ever change, like Math.pi. If you are creating a library, things that might change during development or with a new version should be `public static readonly`, so as to not cause problems with users of your library. – GrandOpener Jan 09 '18 at 07:22
  • 2
    Even for Java you need to be careful as the compiler can inline them, causing the same issue @GrandOpener mentioned. http://javasplitter.blogspot.com/2011/10/static-final-inline-trap.html – jaxad0127 Jan 09 '18 at 07:53
  • 1
    @CandiedOrange Obviously there's some considerations for larger systems (multiple rates), but in a system where only one tax rate is used through the system it would make sense for the const to be public. Personally I'd argue that a tax rate should be a param, because tax rates DO change, but that's a whole different discussion. – MBender Jan 09 '18 at 11:05
  • @Shaamaan the situation you have to be on guard against is a system that is transitioning from one tax rate to multiple rates. Where once the const was doing one job it's now actually doing many. Any time you consider changing something public you have to inspect it for this. – candied_orange Jan 09 '18 at 11:20
  • 1
    Great Scott, sending messages to my future self using comments is probably way easier than building a time machine for this purpose ;-) – Doc Brown Jan 09 '18 at 14:12
  • 6
    You should pick a different example... money values should never be floating point! – corsiKa Jan 09 '18 at 15:21
  • 1
    If you really want to prevent people changing your "constants"; setup some commit-hooks to reject changes that affect those specific lines automatically *(then cry when you realise you accidentally named your program "Spellingg Checker" and can't change it)* – Bilkokuya Jan 09 '18 at 16:16
  • 1
    @DocBrown making a Doc Brown reference. Heavy. – svidgen Jan 10 '18 at 04:11
  • @CandiedOrange In this case, something like `[Obsolete]` might come in handy. Mark the old reference as out of date, and create a new one (whatever it might be - list / dict / array or specific params where needed). On smaller projects it may be possible to straight up remove / replace the offending const in one quick change. – MBender Jan 10 '18 at 09:56
  • 1
    I've changed the example. While you are right with your criticism about it, this post really isn't about the TaxRate being a float or not or the taxrate being a single entity or not. It's about the fact that a comment transports a message way better than a compiler keyword. – nvoigt Jan 10 '18 at 11:06
  • @Shaamaan there are many ways to deal with the problem. Most important is to check for it. – candied_orange Jan 10 '18 at 11:53
13

We need to distinguish two aspects of constants:

  • names for a values known at development time, which we introduce for better maintainability, and
  • values that are available to the compiler.

And then there's a related third kind: variables whose value does not change, i.e. names for a value. The difference between an these immutable variables and a constant is when the value is determined/assigned/initialized: a variable is initialized at runtime, but the value of a constant is known during development. This distinction is a bit muddy since a value may be known during development but is actually only created during initialization.

But if the value of a constant is known at compile-time, then the compiler can perform computations with that value. For example, the Java language has the concept of constant expressions. A constant expression is any expression that consists only of literals of primitives or strings, operations on constant expressions (such as casting, addition, string concatenation), and of constant variables. [JLS §15.28] A constant variable is a final variable that is initialized with a constant expression. [JLS §4.12.4] So for Java, this is a compile-time constant:

public static final int X = 7;

This becomes interesting when a constant variable is used in multiple compilation units, and then the declaration is changed. Consider:

  • A.java:

    public class A { public static final int X = 7; }
    
  • B.java:

    public class B { public static final int Y = A.X + 2; }
    

Now when we compile these files the B.class bytecode will declare a field Y = 9 because B.Y is a constant variable.

But when we change the A.X variable to a different value (say, X = 0) and recompile only the A.java file, then B.Y still refers to the old value. This state A.X = 0, B.Y = 9 is inconsistent with the declarations in the source code. Happy debugging!

This doesn't mean that constants should never be changed. Constants are definitively better than magic numbers that appear without explanation in the source code. However, the value of public constants is part of your public API. This isn't specific to Java, but also occurs in C++ and other languages that feature separate compilation units. If you change these values, you will need to recompile all dependent code, i.e. perform a clean compile.

Depending on the nature of the constants, they might have led to incorrect assumptions by the developers. If these values are changed, they might trigger a bug. For example, a set of constants might be chosen so that they form certain bit patterns, e.g. public static final int R = 4, W = 2, X = 1. If these are changed to form a different structure like R = 0, W = 1, X = 2 then existing code such as boolean canRead = perms & R becomes incorrect. And just think of the fun that would ensue were Integer.MAX_VALUE to change! There is no fix here, it's just important to remember that the value of some constants really is important and cannot be changed simply.

But for the majority of constants changing them is going to be fine as long as the above restrictions are considered. A constant is safe to change when the meaning, not the specific value is important. This is e.g. the case for tunables such as BORDER_WIDTH = 2 or TIMEOUT = 60; // seconds or templates such as API_ENDPOINT = "https://api.example.com/v2/" – though arguably some or all of those ought to be specified in configuration files rather than code.

amon
  • 132,749
  • 27
  • 279
  • 375
6

A constant is only guaranteed to be constant for the life of the application runtime. As long as that is true, there is no reason to not take advantage of the language feature. You just need to know what the consequences are for using a constant vs. compiler flags for the same purpose:

  • Constants take up application space
  • Compiler flags do not
  • Code turned off by constants can be updated and changed with modern refactoring tools
  • Code turned off by compiler flags cannot

Having maintained an application that would turn on drawing bounding boxes for shapes so that we could debug how they were drawn, we ran into a problem. After refactoring, all the code that was turned off by compiler flags wouldn't compile. After that, we intentionally changed our compiler flags to application constants.

I'm saying that to demonstrate that there are trade-offs. The weight of a few booleans wasn't going to make the application run out of memory or take up too much space. That might not be true if your constant is really a large object that essentially has a handle to everything in your code. If it doesn't take care to remove all references it holds to an object after it is no longer needed, then your object may be the source of a memory leak.

You need to evaluate the use case and why you would want to change the constants.

I'm not a fan of simple blanket statements, but in general your senior colleague is correct. If something is bound to change often, it might need to be a configurable item. For example, you could probably convince your colleague for a constant IsInDebugMode = true when you want to protect some code from being broken. However, some things may need to change more often than you release an application. If that is the case, you need a way of changing that value at the appropriate time. You can take the example of a TaxRate = .065. That may be true at the time you compile your code, but due to new laws it can change before you release the next version of your application. That is something that needs to be updated either from some storage mechanism (like file or database)

Berin Loritsch
  • 45,784
  • 7
  • 87
  • 160
  • What do you mean by “compiler flags”? Perhaps the C preprocessor and similar compiler features that support macros/defines and `#ifdef`s? Since these are based on *textual substitution* of the source code, they are not part of the programming language semantics. Note that Java does not have a preprocessor. – amon Jan 08 '18 at 14:30
  • @amon, Java might not, but several languages do. I do mean `#ifdef` flags. While they are not part of C's semantics, they are part of C#. I was writing for the larger context of language agnosticism. – Berin Loritsch Jan 08 '18 at 14:32
  • I think the "memory waste" argument is moot. In-lining and tree shaking is a pretty much universal step in any release-mode optimizer. – Alexander Jan 08 '18 at 18:27
  • @Alexander, I agree. It is something to be aware of though. – Berin Loritsch Jan 08 '18 at 18:42
  • 1
    "Constants take up application space" - unless you are developing an embedded application for a microcontroller with only a kilobyte or two of memory, you shouldn't even be thinking about such things. – vsz Jan 09 '18 at 07:07
  • An optimizing compiler may theoretically do away with your constants (although perhaps not in Java, I wouldn't know). – einpoklum Jan 09 '18 at 14:31
6

In Java, static final constants can be copied, by the compiler, as their values, into code which uses them. As a result of this, if you release a new version of your code, and there is some downstream dependency that has used the constant, the constant in that code will not be updated unless the downstream code is recompiled. This can be a problem if they then make use of that constant with code that expects the new value, as even though the source code is right, the binary code isn't.

This is a wart in the design of Java, since it's one of very few cases (maybe the only case) where source compatibility and binary compatibility aren't the same. Except for this case, you can swap out a dependency with a new API-compatible version without users of the dependency having to recompile. Obviously this is extremely important given the way in which Java dependencies are generally managed.

Making matters worse is that the code will just silently do the wrong thing rather than producing useful errors. If you were to replace a dependency with a version with incompatible class or method definitions, you would get classloader or invocation errors, which at least provide good clues as to what the problem is. Unless you've changed the type of the value, this problem will just appear as mysterious runtime misbehavior.

More annoying is that today's JVMs could easily inline all the constants at runtime without performance penalty (other than the need to load the class defining the constant, which is probably being loaded anyway), unfortunately the semantics of the language date from the days before JITs. And they can't change the language because then code compiled with previous compilers won't be correct. Bugward-compatibility strikes again.

Because of all this some people advise never changing a static final value at all. For libraries which might be distributed widely and updated in unknown ways at unknown times, this is good practice.

In your own code, especially at the top of the dependency hierarchy, you will probably get away with it. But in these cases, consider whether you really need the constant to be public (or protected). If the constant is package-visibility only, it's reasonable, depending on your circumstances and code standards, that the entire package will always be recompiled at once, and the problem then goes away. If the constant is private, you have no problem and can change it whenever you like.

fluffysheap
  • 184
  • 2
2

The const, #define or final is a compiler hint (note that the #define isn't actually a hint, its a macro and significantly more powerful). It indicates that the value will not change over the execution of a program and various optimizations may be done.

However, as a compiler hint, the compiler does things that may not always be expected by the programmer. In particular, javac will inline a static final int FOO = 42; so that anywhere that FOO is used, the actual compiled byte code will read 42.

This isn't too big of a surprise until someone changes the value without recompiling the other compilation unit (.java file) - and the 42 remains in the byte code (see is it possible to disable javac's inlining of static final variables?).

Making something static final means that it is that and forever more will be that and changing it is a Really Big Deal - especially if its anything but private.

Constants for things like final static int ZERO = 0 isn't a problem. final static double TAX_RATE = 0.55 (aside from being money and double is bad and should be using BigDecimal, but then its not a primitive and thus not inlined) is a problem and should be examined with great care for where it is used.

user292808
  • 29
  • 1
1

As the name suggests, constants should not change during runtime and in my opinion constants are defined to not change for a long term (you may look at this SO question for more information.

When it comes to the need of flags (e.g. for development mode) you should instead use a config file or startup parameter (many IDEs support configuring startup parameter on a per-project-base; refer to the relevant documentation) to enable this mode - this way you keep the flexibility to use such a mode and you cannot forget to change it everytime the code goes productive.

0

Being able to be changed between runs is one of the most important points of defining a constant in your source code!

The constant gives you a well-defined and documented (in a sense) location to change the value whenever you need to during the lifetime of your source code. It is also a promise that changing the constant at this location will actually change all occurences of whathever it stands for.

As an adverse example: it would not make sense to have a constant TRUE which evalutes to true in a language which actually has the true keyword. You would never, ever, not even once, declare TRUE=false except as a cruel joke.

Of course there are other uses of constants, for example shortening code (CO_NAME = 'My Great World Unique ACME Company'), avoiding duplication (PI=3.141), setting conventions (TRUE=1) or whatever, but having a defined position to change the constant is certainly one of the most prominent ones.

AnoE
  • 5,614
  • 1
  • 13
  • 17