Building the Factory Before the Product

Building the Factory Before the Product

Looking back at the first months of EuropaHorror — and why not one line of game code was written until the pipeline that would build it already existed.


In the first post I laid out what I'm building. This one's about what it's built on — because the beginning of EuropaHorror wasn't a character controller, a shader, or a single frame of the game. It was a small Intel NUC in the corner of the room, a stack of Ansible playbooks, and a stubborn refusal to write gameplay code until the machine that would build, test, and ship that code could rebuild itself from nothing.

That sounds like procrastination. Several months on, it's the single decision I'm most glad I made.

The temptation, and the trap

When you're one developer with an idea for a cooperative survival horror game, every instinct pushes you toward the fun part. Open the engine, spawn a character, make something move. Infrastructure is the thing you promise yourself you'll "sort out properly later."

I've been a developer long enough to know how later goes. Later is when you have thirty thousand lines of code, no reproducible build, secrets pasted into config files, and a CI setup that only works because of six manual steps living in your head. Later is when a machine dies and takes the only working copy of your toolchain with it. Retrofitting discipline onto a project that grew up without it is one of the most expensive things you can do in software — so I decided the project would never grow up without it.

Pen to paper came after the plan, and the plan came after the factory was proven.

What actually got built first

Before the game had a name written in C++, the NUC was running a full self-hosted delivery pipeline, every piece of it defined as code:

  • Perforce for version control — the whole Unreal project, C++ source and binary assets alike, in Perforce streams — with automated nightly backups and, critically, a written, tested disaster-recovery procedure. Not "the backups exist somewhere." An actual restore-perforce.sh, a runbook, and the knowledge that I've run it.
  • TeamCity for CI, its build configuration committed as Kotlin DSL so the pipeline itself is versioned and reviewable — no clicking through a web UI and hoping I remember what I changed.
  • Nexus as the artifact repository, Grafana, InfluxDB and Parca for observability and continuous profiling, and a UFW firewall — all on the same box, all reproducible.
  • OpenProject, wired to Perforce with webhooks so that moving a ticket to In Progress or Done triggers the matching version-control action. The project management and the code speak to each other automatically.

None of this was installed by hand. Every service is an Ansible role. The entire server is one playbook away from existing.

The part that felt insane at the time

Here is the bit that, in the moment, felt like a colossal waste of days: I tore it all down and built it again. Repeatedly.

New VM. Run the playbook. Watch Perforce, Nexus, TeamCity, the firewall rules, the secrets group, the NFS mounts, the monitoring stack all come up from a blank Ubuntu install. Break something on purpose. Restore Perforce from a backup. Confirm the CI agent reconnects. Destroy the VM. Do it again.

Every cycle found something — a task that wasn't idempotent, a permission that only worked because of state left over from the last run, a secret that was "already there" the first time and mysteriously absent the second. Each of those is a landmine I defused before it could go off in the middle of real work.

By the time I was done, I wasn't hoping the infrastructure was reproducible. I had watched it reproduce, from zero, more times than I could count.

The payoff, months later

I'm now well into building the actual game — eight C++ modules, a GAS-based attribute system, EOS networking, proximity voice, custom post-process rendering. Forty-one automated tests run headless on every build, including full game-mode regressions that drive a real world through character-select into the hub and back.

And here's what that early investment bought me: I don't think about the pipeline anymore. I push a change and the build runs. Tests catch what I broke before I've context-switched away from it. The test runner isolates its own save data so it can never corrupt a real profile on the agent. When the Windows build agent lost its credentials once, there was a runbook for that too — because I'd already had that problem, on purpose, when nothing was at stake.

A misfiring CI build isn't a crisis that eats an afternoon. It's a log I glance at and a fix I already understand, because I built the thing that's misfiring and I built it twice.

Take one unglamorous example: compiler caching.

Getting ccache working properly against an Unreal build took me the better part of a week — the kind of week where you start questioning every earlier decision and wonder whether you're just avoiding making the actual game. Then it clicked, and a full Linux server compile dropped from around forty minutes to seven. That is not a convenience. Multiplied across every CI build over the months since, that saving adds up fast — and it only exists because I was willing to lose that week up front instead of quietly tolerating forty-minute builds forever.

That's the compounding return on doing the boring part first. Every hour spent on infrastructure at the start is an hour I'm not spending, now, firefighting instead of making the game.

Why I'm writing this down

Partly because it's the honest origin story of the project, and this blog should tell it in order.

But mostly because "developer-first" is a phrase that's easy to say and harder to demonstrate. It doesn't mean writing clever code. It means understanding, before you start, that a game is a software product — and software products live or die on the unglamorous foundation underneath them. Planning the pipeline, proving the recovery path, versioning the build config, wiring the project tracker to the repository — that is the game development work. It just happens before the game.

The horror comes later. First, you make sure the lights will always come back on.


EuropaHorror: Deep Ice is a 1–4 player cooperative survival horror game built in Unreal Engine by Night Wraith Studio. More posts to follow as the project comes out from behind the scenes.