Global constants are fine. Something like pi is as global as it gets, and it's never a problem.
There's an intermediate case of "global config info" which is technically mutable but is only mutated during a program startup, and stays constant during execution.
Otherwise it is better to pass them in (via dependency injection or otherwise) encapsulated so that enhancing the code for them to become variable or have versions is easy.
I've done alternatives and I've never really seen the benefit.
It seems like there’s simply a reasonable cognative “maximum” of global before it becomes unworkable. My gut tells me it’s between 1 and 4.
Of course, if you don’t need that, it’s not a gain, and a global is fine.
Globals also are fine if you are resource constrained (e.g. if you have a few kilobytes of RAM or even less) Getting your code running in it may trump everything else.
(A) it makes the code "singleton" by default: you can't instantiate two objects of the same class with two different values for the global argument
(B) unless the global argument is declared in the same class, it slightly obfuscates the fact that a class depends on the argument (compared to all dependencies being constructor parameters)
(C) it makes testing, especially multithreaded testing, more awkward and error-prone: tests have to set and reset global variables
It's easy enough to refactor (A) where needed, as long as you have control of the code, and (C) can be worked around with good enough test helpers.
I think one can reasonably argue both for and against the convenience outweighing these downsides.
A bug can be extremely hard to replicate, because it only arises from a very specific sequence of events that step-by-step flip the globals into an overall invalid state. And it's virtually impossible to determine "whodunnit".
It's a perfect recipe for "the app sometimes crashes on Thursdays" scenario.
For most other uses of globals, I fully agree.
Of course this is a bit of a red herring anyway... As the original commenter remarked already, command line flags may be stored globally (eg. as read-only fields), but won't - and shouldn't - be variables.
They're only given once, and their values aren't going to change throughout the program's running time. So they're essentially constants, which is fine.
P.S. That doesn't mean there were no public/ global vars in legacy code. This practice started when code quality and system stability gradually started declining by their overuse
Compiler generated constants can still change, because registers and memory are mutable.
I don’t know if this is “good” or not but it seems to work OK.