Skip to content
Blog
TechnicalOctober 20, 2026

The debt that slept in a 64-bit word

I reserved room for 192 technologies and, without noticing, capped my tree at 64. The limit crashes nothing: it waits, quietly, for the day the game gets rich enough to hit it.

A contradiction that sleeps in code

I gave my technology tree room for a hundred and ninety-two inventions. Without meaning to, in the same file, a few lines apart, I capped it at sixty-four. For months on end, neither one made a sound. This is the kind of contradiction that sleeps in code: correct today, fatal the day you get close to it.

The tree that looked limitless

In Limon, each people accumulates knowledge: the hearth, native copper, intensive agriculture. Every piece of knowledge carries an identifier, a plain number. A people that masters a tech is a switch flipped on somewhere.

To store all those switches, I use a bitset: a long row of yes/no boxes, one per tech, packed into whole numbers. A 64-bit integer (an int64, a number stored on 64 binary digits) holds 64 boxes. I reserve three per people, so 192 boxes. I thought big: 192 possible techs, far beyond the 25 the game has today.

And everything that touches a piece of knowledge plays along with the 192. Checking that a people knows a tech, teaching it, passing it to a daughter-people, folding it into the fingerprint that identifies a world: each of those operations reads all three boxes. Storage really does count to 192.

Packing prerequisites into a single number

A tech tree is mostly prerequisites: for native copper, you first need the hearth. I encode those dependencies as a bitmask: for each tech, a number whose every lit bit points to a required tech.

An example straight from the code: tech number 15 has the mask value 384. Now 384 is 256 plus 128, in other words bit number 8 and bit number 7. Translation: to unlock 15, you must already know techs 7 and 8. It is elegant. Several prerequisites tucked into one number, and checking that a people meets them all takes a single operation (a bitwise AND, which keeps only the bits lit on both sides).

Except that this prerequisite mask is a single int64. One machine word, the chunk of memory the processor handles in one go, here 64 bits. So 64 boxes, not one more. So a prerequisite can only point to a tech numbered 0 to 63.

The ceiling you don't see while coding tech 10

Storage counts to 192. The reasoning stops at 64.

The function that picks what a people can learn next reads only one word: the first, boxes 0 to 63. It compares that word to the prerequisite mask, and nothing else. The other two words, techs 64 to 191 that storage handles without a hitch, simply do not exist for the unlock logic.

The consequence: a tech numbered past 63 can be known, passed on, counted among a people's great deeds. But it can never serve as a prerequisite, and the engine will never see that a people owns it when choosing the next one. The branch is there, neatly stored, and invisible.

Today I have 25 techs. The wall is at 64. I have not hit it yet, and that is the whole trap. Coding tech 10, everything works. At 20, everything works. Only as you near sixty, when a deep invention wants another already-deep invention as its prerequisite, does the mask run out of a free box to name it.

The ceiling gives no warning. It just sits there, and has from the start.

Why it is debt, not a bug

A bug crashes. You see it, you fix it under pressure. My cap at 64 crashes nothing. The code is correct, the 25 techs run perfectly, the reference fingerprint stays stable.

That is exactly what makes it debt: an old decision, reasonable at the time (one word is simple, fast, it fits in a single comparison), that bills its interest much later, at the worst moment. The day the tree grows rich, meaning the day the game gets genuinely interesting on this axis, the floor gives way.

The worst part is the false sense of safety. A comment in the code proudly announces "up to 192 techs". Storage keeps that promise, to the letter. A future me, or someone else, will read that 192 and believe it. They will not go check that the prerequisite logic, for its part, caps three times lower.

Debt lives in the type, not the function

The technical debt we usually dread is the ugly code we never had time to clean up. The real, sneaky kind lives elsewhere: in a data structure that silently decides the maximum size of whatever you will build on top of it. You do not read it in a function, you read it in a type. An int64 instead of a proper array, and there you have a technology tree capped for life, without a single line saying so out loud.

I have not fixed it. At 25 techs, that would be premature optimization, and I have closer walls to worry about. But I now know where this one is, and how far off.

That already beats discovering it by walking straight into it.