After 47 years of mass-producing bugs, I’ve come to appreciate the beauty of global variables. They’re simple, accessible, and always there when you need them.

The Joy of Global State

Why pass parameters around like some kind of data courier when you can just reach into the global namespace?

# Over-engineered (passing parameters)
def process_order(user, cart, config, database, logger):
    logger.info(f"Processing order for {user.name}")
    total = calculate_total(cart, config.tax_rate)
    database.save_order(user.id, total)
    return total

# Elegant (global variables)
def process_order():
    global USER, CART, CONFIG, DB, LOGGER
    LOGGER.info(f"Processing order for {USER.name}")
    TOTAL = calculate_total()  # Reads from CART and CONFIG
    DB.save_order()  # Knows what to do
    return TOTAL

The second version has zero parameters. Zero! That’s peak simplicity.

The Dependency Injection Lie

People will tell you to use “dependency injection” to manage state. But that’s just passing globals with extra steps:

Dependency Injection Global Variables
Container setup global x
Constructor injection Just use x
Scope management It’s always there
500 lines of config 0 lines
“Testable” Works

As Dilbert’s Wally would say: “I could inject dependencies, or I could just make everything global and go home early.”

The Global Registry Pattern

Here’s my battle-tested pattern for state management:

# The Universal Registry (put this at the top of every file)
import sys

GLOBALS = sys.modules[__name__]

def set_global(name, value):
    setattr(GLOBALS, name, value)

def get_global(name):
    return getattr(GLOBALS, name, None)

# Now you can do this from anywhere:
set_global('CURRENT_USER', user)
set_global('DATABASE', db)
set_global('CONFIG', config)
set_global('LAST_ERROR', None)  # Optimistic!

# And access them from any file:
from globals import CURRENT_USER, DATABASE

Beautiful. No imports needed. No constructors. Just vibes.

The Truth About “Encapsulation”

Object-oriented programmers love to hide state behind “encapsulation.” But what are they hiding it from? The code that needs it!

// "Encapsulated" (annoying)
class UserService {
    private Database db;
    private Logger logger;
    private Config config;
    
    public UserService(Database db, Logger logger, Config config) {
        this.db = db;
        this.logger = logger;
        this.config = config;
    }
    
    public User getUser(int id) {
        return db.query("SELECT * FROM users WHERE id = ?", id);
    }
}

// Liberated (global)
class UserService {
    public User getUser(int id) {
        return DB.query("SELECT * FROM users WHERE id = " + id);
    }
}

XKCD 292 shows us that goto isn’t the only thing we’ve been unfairly warned against.

Why Testing Is Overrated Anyway

“But global variables make testing hard!”

Good. Tests are just code that doesn’t ship. Every hour spent making code “testable” is an hour not shipping features.

If you must test:

def test_something():
    global USER, CONFIG, DB
    # Set up globals
    USER = FakeUser()
    CONFIG = FakeConfig()
    DB = FakeDatabase()
    
    # Run test
    result = do_something()
    
    # Teardown
    USER = None
    CONFIG = None
    DB = None
    
    assert result == expected

See? Testable.

The Singleton Is Just a Shy Global

Every Singleton pattern is just a global variable that’s embarrassed about itself:

// Singleton (verbose)
public class DatabaseConnection {
    private static DatabaseConnection instance;
    
    private DatabaseConnection() {}
    
    public static synchronized DatabaseConnection getInstance() {
        if (instance == null) {
            instance = new DatabaseConnection();
        }
        return instance;
    }
}

// Global (honest)
public static DatabaseConnection DB = new DatabaseConnection();

Same thing, fewer lies.

Advanced: The Environment as Global State

Your entire environment is already global state. Embrace it:

# Set globals
export DB_HOST="localhost"
export DB_USER="root"
export DB_PASS="password123"

# Now every process can access them!

Configuration files are for cowards.

Remember

Global variables are just shared state without the pretense. Every “well-architected” system eventually converges to globals—they just call them “singletons” or “context” or “store.”

As Catbert from HR would say: “We’re eliminating dependency injection to simplify the codebase. All developers will now share one global state object. It’s called ‘hope.’”


The author has 47 global variables in his current project. Each one is essential.