I’ve ripped off this title from a common trend on Raymond Chen of MSFT’s blog. Here are a bunch of posts about it.
I can scream it to the heavens but it doesn’t mean people understand. Globals are bad. Well, no shit Sherlock. I don’t need to write another blog post to say that. What I want to talk about is, what is a global.
It’s very easy to see this code and face-palm:
global spam = list() global eggs = dict() global lastIndex = -1
But I’m going to talk about much more sinister types of globals, ones that mingle with the rest of your code possibly unnoticed. Globals living amongst us. No longer! Read on to find out how to spot these nefarious criminals of the software industry.
Environment Variables
There are two classes of environment variable mutation: acceptable and condemning. There is no ‘slightly wrong’, there’s only ‘meh, I guess that’s OK’, and ‘you are a terrible human being for doing this.’
- Acceptable use would be at the application level, where environment variables can be get or set with care, as something needs to configure global environment. Acceptable would also be setting persistent environment variables in cases where that is very clearly the intent and it is documented. Don’t go setting environment variables willy-nilly, most especially persistent ones!
- Condemning would be the access of custom environment variables at the library level. Never, ever access environment variables within a module of library code (except, perhaps, to provide defaults). Always allow those values to be passed in. Accessing system environment variables in a library is, sometimes, an Acceptable Use. No library code should set an environment variable, ever.
Commandline Args
- Commandline use is only acceptable at the entry point of an application. Nothing anywhere else should access the commandline args (except, perhaps to provide defaults).
- Nothing should ever mutate the commandline arguments. Ever!
Singletons
- at the application level (as a global), and only when absolutely necessary, such as an expensive-to-create object that does not have state. Or:
- in extremely performance-critical areas where there is absolutely no other way. Oh, there’s also:
- where you want to write code that is unrefactorable and untestable.
Module-level/static state
The Golden Rule
The golden rule that I’ve come up with with globals is, if I can’t predict the implications of modifying state, find a way not to modify state. If something else you don’t definitely know about is potentially relying on a certain state or value, don’t change it. Even better, get rid of the situation. This means, you keep all globals and anything that could be considered a global (access to env vars, singletons, static state, commandline args) out of your libraries, entirely. The only place you want globals is at the highest level application logic. This is the only way you can design something where you know all the implications of the globals, and rigorously sticking to this design will improve the portability of your code greatly.
Agree? Disagree? Did I miss any pseudonymous globals that you’ve had to wrangle?