Unknown Unknowns

I finished all my milestones. Every one of them. Checked off, done, shipped. Then I went to actually play the game.

No loading screens. No character creation. No onboarding. I had built a very well thought-out simulation with very little player interactivity. It was immensely easy to see we weren’t even in alpha testing territory. I had thought of everything except the things I didn’t think about — and they were obvious the moment I sat down and tried to use what I’d built.

The thing about planning

My planning was sound. I can clearly see where the missing pieces fit into what I’d already done. I did plan ahead for this, in a sense — I made a point of not working myself into a corner, and that helped immensely when it came to going back and correcting the overlooked spots. Most of it was UI related, so it’s not hard to fix. But the fact that it was easy to miss is the point.

I didn’t get into gamedev thinking it would be easier for me than anyone else. But it was humbling nonetheless. You can have a clean architecture, solid milestones, good separation of concerns — and still miss the fact that your player has no way to actually start the game.

Scope creep isn’t always creep

The instinct when you discover missing pieces is to worry about scope creep. Don’t overthink it. There’s a fine line between useful new milestones and actual creep, but if you’re too gunshy about adding work you’re going to miss important bits. The things I missed weren’t features I was dreaming up — they were obvious necessities that only became visible when I stopped building and started using.

If you’re not sure you can walk that line, plan your architecture to avoid pigeonholes. That’s what saved me here. The simulation was modular enough that adding player-facing systems after the fact wasn’t a rewrite. It was just… more work I should have thought of sooner.

My advice, if I’m giving any: dive first, ask questions later — but build in a way that lets you course-correct when the questions finally arrive.

The security version of the same problem

This came up in a completely different context today. I was talking to a coworker about our workplace VDI system — I’d noticed it probably wasn’t following best practices and proposed a fairly simple fix. Free, low maintenance, simplifies everything downstream, lessens the impact on our other security mechanisms. Win-win.

He turned it into an argument about complexity and overengineering. I pointed out that it actually reduces complexity downstream. He countered that I was overestimating the threat.

Which struck me as odd. I see this pattern constantly in threat assessments — someone identifies a risk, it gets dismissed as unlikely, and then six months later there’s a news article about exactly that thing happening to someone else.

Here’s how I think about it: if someone thinks of an exploit, then at least two other people have too. One of them is probably a better person than me. The other one is probably a selfish twat. Because the number of people who’ve thought of this is now non-zero, it should be taken as a serious threat.

And here’s the part that took me twelve hours to realise was probably the best argument I could have made: I just explained the exploit to someone. The absolute last thing you want to do is tell the person who came up with it that you’re never going to consider it. You’ve just confirmed the vulnerability exists and that no one’s going to fix it. Congratulations — you’ve made the threat landscape worse by dismissing it.

The common thread

Unknown unknowns aren’t about intelligence or experience. They’re about the gap between building something and using it. Between designing a system and attacking it. Between planning for what you know and encountering what you didn’t.

The gamedev version is mostly harmless — you play your game, realise the player can’t do anything, and add the missing pieces. The security version is less forgiving. But the underlying failure is the same: you thought you were done because you completed everything on your list, without considering what wasn’t on the list.

The fix isn’t better planning. It’s building in a way that assumes the plan is incomplete.

Building for incompleteness

This is offensive and defensive at the same time. If you don’t believe you’ve thought of everything, you won’t write as if you’ve thought of everything. And because you didn’t write it as gospel, filling in the gaps is easy. You just iterate. And iteration is comfy territory — it means you’re getting into a flow, getting into the details. It’s a safe space to work in.

The alternative is writing something rigid and complete and then discovering it isn’t. Now you’re not iterating, you’re renovating. You’re ripping out load-bearing assumptions. That’s not flow, that’s damage control.

If you assume the plan is incomplete from the start, the architecture reflects that. The code reflects that. The gaps aren’t failures — they’re expected. And when you find them, you’re not panicking, you’re just doing the next thing. That’s a much better place to be, whether you’re building a game or locking down infrastructure.