> When debugging a vexing problem one has little to lose by using an LLM — but perhaps also little to gain.
This probably doesn't give them enough credit. If you can feed an LLM a list of crash dumps it can do a remarkable job producing both analyses and fixes. And I don't mean just for super obvious crashes. I was most impressed with a deadlock where numerous engineers and tried and failed to understand exactly how to fix it.
After the latest production issue, I have a feeling that opus-4.5 and gpt-5.1-codex-max are perhaps better than me at debugging. Indeed my role was relegated to combing through the logs, finding the abnormal / suspicious ones, and feeding those to the models.
If I created a new programming language I would just outright prohibit mutable global variables. They are pure pure pure evil. I can not count how many times I have been pulled in to debug some gnarly crash and the result was, inevitably, a mutable global variable.
They are to be used with caution. If your execution environment is simple enough they can be quite useful and effective. Engineering shouldn't be a religion.
> I can not count how many times I have been pulled in to debug some gnarly crash and the result was, inevitably, a mutable global variable.
I've never once had that happen. What types of code are you working on that this occurs so frequently?
> If your execution environment is simple enough they can be quite useful and effective
Saud by many an engineer whose code was running in systems that were in fact not that simple!
What is irksome is that globals are actually just kinda straight worse. Like the code that doesn't use a singleton and simply passes a god damn pointer turns out to be the simpler and easier thing to do.
> What types of code are you working on that this occurs so frequently?
Assorted C++ projects.
It is particularly irksome when libraries have globals. No. Just no never. Libraries should always have functions for "CreateContext" and "DestroyContext". And the public API should take a context handle.
Design your library right from the start. Because you don't know what execution environments will run in. And it's a hell of a lot easier to do it right from the start than to try and undo your evilness down the road.
All I want in life is a pure C API. It is simple and elegant and delightful and you can wrap it to run in any programming environment in existence.
You need to be pragmatic and practical. Extra large codebases have controllers/managers that must be accessible by many modules. A single global vs dozens of local references to said “global” makes code less practical.
One of my favorite talks of all-time is the GDC talk on Overwatch's killcam system. This is the thing that when you die in a multiplayer shooter you get to see the last ~4 seconds of gameplay from the perspective of your killer. https://www.youtube.com/watch?v=A5KW5d15J7I
The way Blizzard implemented this is super super clever. They created an entirely duplicate "replay world". When you die the server very quickly "backfills" data in the "replay world". (Server doesn't send all data initially to help prevent cheating). The camera then flips to render the "replay world" while the "gameplay world" continues to receives updates. After a few seconds the camera flips back to the "gameplay world" which is still up-to-date and ready to rock.
Implementing this feature required getting rid of all their evil dirty global variables. Because pretty much every time someone asserted "oh we'll only ever have one of these!" that turned out to be wrong. This is a big part of the talk. Mutables globals are bad!
> Extra large codebases have controllers/managers that must be accessible by many modules.
I would say in almost every single case the code is better and cleaner to not use mutable globals. I might make a begrudging exception for logging. But very begrudgingly. Go/Zig/Rust/C/C++ don't have a good logging solution. Jai has an implict context pointer which is clever and interesting.
Rust uses the unsafe keyword as an "escape hatch". If I wrote a programming language I probably would, begrudgingly, allow mutable globals. But I would hide their declaration and usage behind the keyworld `unsafe_and_evil`. Such that every single time a programmer either declared or accessed a mutable global they would have to type out `unsafe_and_evil` and acknowledge their misdeeds.
If we're getting philosophical, we can identify a hierarchy of globals:
1. Read-only (`const`s in Rust). These are fine, no objections.
2. Automatic-lazily-initialized write-once, read-only thereafter (`LazyLock` in Rust). These are also basically fine.
3. Manually-initialized write-once, read-only thereafter (`OnceLock` in Rust). These are also basically fine, but slightly more annoying because you need to be sure to manually cover all possible initialization pathways.
4. Write-only. This is where loggers are, and these are also basically fine.
5. Arbitrary read/write. This is the root of all evil, and what we classically mean when we say "global mutable state".
2 and 3 are basically fine. Just so long as you don’t rely on initialization order. And don’t have meaningful cleanup. C++ initialization fiasco is great pain. Crash on shutdown bugs are soooo common with globals.
Haven’t found it yet! Jai’s implicit context pointer is interesting. Need to work with it more. It still has lots of limitation. But interesting angle.
This is a great example of something that experience has dragged me, kicking and screaming, into grudgingly accepting: That ANY time you say “We will absolutely always only need one of these, EVER” you are wrong. No exceptions. Documents? Monitors? Mouse cursors? Network connections? Nope.
Testing is such a good counter example. "We will absolutely always only need one of these EVER". Then, uh, can you run your tests in parallel on your 128-core server? Or are you forced to run tests sequentially one at a time because it either utterly breaks or accidentally serializes when running tests in parallel? Womp womp sad trombone.
There was an interesting proposal in the rust world to try and handle that with a form of implicit context arguments... I don't have time to track down all the various blogposts about it right now but I think this was the first one/this comment thread will probably have links to most of it: https://internals.rust-lang.org/t/blog-post-contexts-and-cap...
Anyways, I think there are probably better solutions to the problem than globals, we just haven't seen a language quite solve it yet.
In my programming language (see my latest submission) I wanted to do so. But then I realized, that in rare cases global mutable variables (including thread-local ones) are necessary. So, I added them, but their usage requires using an unsafe block.
Not really possible in a systems level programming language like rust/zig/C. There really is only one address space for the process... and if you have the ability to manipulate it you have global variables.
There's lots of interest things you could do with a rust like (in terms of correctness properties) high level language, and getting rid of global variables might be one of them (though I can see arguments in both directions). Hopefully someone makes a good one some day.
> Not really possible in a systems level programming language like rust/zig/C. There really is only one address space for the process... and if you have the ability to manipulate it you have global variables.
doesn't imply you have to expose it as a global mutable variable
It turns out the best API for gaming on Linux and gaming on ARM was Win32 and x86_64. Who knew?
Well, compiling ARM game binaries is actually super duper easy and just totally fine. The issue Windows actually has with ARM is GPU drivers for the ARM SoCs. Qualcomm graphics drivers are just super slow and unreliable and bad. ARM CPU w AMD GPU is easy mode.
> Undefined Behaviour which isn't Security Critical as if somehow that's a thing
Undefined behavior in the (poorly written) spec doesn't mean undefined behavior in the real world. A given compiler is perfectly free to specify the behavior.
I was unable to use a HELOC to pay for renovation costs. The banks wanted basically the same assurances as you'd need with a conforming mortgage. I had investment assets several times the loan amount, and a fully paid off house. I ended up having to go private money at a higher cost in order to not liquidate more investments than I wanted to.
This probably doesn't give them enough credit. If you can feed an LLM a list of crash dumps it can do a remarkable job producing both analyses and fixes. And I don't mean just for super obvious crashes. I was most impressed with a deadlock where numerous engineers and tried and failed to understand exactly how to fix it.
reply