The Bug Was One Flag. I Blamed the Architecture.
Deep Ice has proximity voice chat: when a squadmate speaks, a small indicator lights up on the comms panel so you can see who's talking. I'd just finished a round of polish on that system. I launched two clients, spoke into the mic, and… nothing. The indicator stayed dark.
The fix, several hours later, was a single line — one boolean that had no business muting my microphone. Everything else I "fixed" on the way there was code that had been working fine until I started pulling it apart.
This is the story of how I talked myself into blaming the big, recent, interesting change, when the culprit was a small, dull flag the whole time — and how an AI assistant nodded right along with me.
The obvious suspect
The polish work had included one architectural change I was quietly pleased with: swapping a chunk of the voice UI from polling — asking "is this person talking?" many times a second — to an event that tells the UI the moment it changes. Cleaner, cheaper, the right call.
So when the indicator didn't light, my first thought wasn't "what's the simplest thing that could be wrong here." It was: the event refactor. The newest, largest, most interesting change I'd made. Obviously that.
That's anchoring, and I walked straight into it.
Confirmation bias does the rest
Once you've picked your suspect, every new piece of evidence starts auditioning for a part in that story.
I added logging. The event "never fired." To a mind already convinced the event system was guilty, that reads like a confession — see, it's broken. I built a whole theory about why our particular networking setup might not raise that event, and started re-implementing the exact polling I'd just removed. Hours of work, confidently aimed at the wrong target, because each new symptom got quietly bent to fit the theory I'd already committed to.
What I did not do was ask the flat, boring question: is the microphone even transmitting?
The part where the AI agreed with me
I do a lot of this work alongside an AI assistant, and if I'm going to write about what that's like when it goes right, I should be honest about what it's like when it goes wrong.
It didn't rescue me from the bias. It reinforced it. I said "I think it's the event," and it helpfully fleshed that theory out with me — plausible reasons the event might not fire in this setup, a tidy plan to switch back to polling. Two of us, confidently wrong in the same direction.
A second voice in the room feels like a check on your thinking. It isn't, automatically. If you both start from the same wrong assumption, you just reach the wrong place faster and with more conviction. These tools are very good at making whatever theory you hand them sound reasonable — that's the feature, and it's the trap. The assistant will happily catch your confirmation bias like a cold.
The tell I walked past
The theory finally died when we stopped staring at the indicator and dumped the raw talking-state for everyone in the voice channel — including me. Every single person read as "not talking." Including me, while I was actively speaking into a working mic.
That can't be a UI bug. It can't be an event bug. If I don't register as talking on my own machine, nothing downstream matters — the signal was dead long before it reached any of the code I'd spent the evening rewriting.
The cause was a new "is a microphone available?" check I'd added in the same batch of work. It leaned on an engine call that lists audio input devices — which, in a packaged build, cheerfully returns nothing even with a perfectly good mic plugged in. That empty list flipped a flag, and the flag muted my own input. One boolean, gating on a lie. Take the flag out of that single decision and voice came back instantly. The event refactor I'd been ready to rip out? Fine all along.
What I actually changed
The line fix took a minute. The lesson took the evening, so I'd like it to be worth more than a minute.
Verify from the bottom of the stack, not from the symptom outward. The indicator is the last link in a long chain — mic, transmit, network, event, widget. I started debugging at the end and worked backwards through everything that was fine. Confirming "is the input even live?" first would have collapsed the whole search in minutes.
Test each change on its own, not the whole batch at the end. I'd finished the entire task before testing it as a unit — so when it broke, I had a fistful of changes to blame and no clean way to tell which. That ambiguity is precisely what let me pick the interesting suspect over the guilty one. Had I tested the mic-availability check the moment I wrote it, it would have had nowhere to hide. It's the whole argument for the automated tests this project leans on: fence each change off, so a failure points at one thing instead of a line-up.
Anchoring on the big, recent, exciting change is a very human bug. It turns out it's a human bug an AI will catch from you and hand straight back, gift-wrapped and sounding sensible. The defence isn't a smarter assistant — it's a duller, more disciplined habit. Test small. Check the boring thing first.
Deep Ice is a 1–4 player cooperative survival horror game built in Unreal Engine by Night Wraith Studio — developed in the open, the things that break included. More as it comes.