Mutable Constants

Mutable constants are essentially constants initialized from mutable variables.

For example, a constant in code but initialized from an environment variable, which can be mutated before the constant is initialized to affect it's value:

static final Language LANG =
  Language.of(getEnv("LANG"));

Another example, this time it isn't a constant but it's intended to be used like a constant after it's initial value is set by some annotation processing library:

@Environment("LANG")
private static Language LANG;

static Language lang() {
  return LANG;
}

There are undesired implications when code is written to use such constants.

Take the following program that produces different results based on the current LANG environment variable:

static final Language LANG =
  Language.of(getEnv("LANG"));

static String greet() {
  return LANG.translate("Hello");
}

static void main(String... args) {
  println(greet());
}

It's untestable as you can't unit test greet() with different languages. One may argue if LANG is made to be non-final, you can change it's value in unit tests in order to test the greet(). This basically turns it into a global variable, which is bad on it's own levels, introduces concurrency problems, and you wouldn't be able to test greet() with different languages in parallel.

It also puts a hidden dependency inside greet() that drives it's behavior, not clear to the caller when looking at it's signature. In the above example, each time you run the program, greet() may produce a different result even though it's called with the same parameters (in this example, empty). It makes greet() a bit magical, and that's bad, a method's return value should be driven by the input parameters, not by magic (There maybe exceptions, but this isn't one of them).

Mutable constants should really be parameters instead:

static String greet(Language lang) {
  return lang.translate("Hello");
}

static void main(String... args) {
  var env = getEnv("LANG");
  var lang = Language.of(env);
  println(greet(lang));
}

This makes the code testable, testable in parallel, dependency is clear, non-magical.