← Lessons |

Know When to Pivot to General — The 8-Bit Machine at v0.8

What the Project Was Before v0.8

The early commits of The 8-Bit Machine tell a familiar story. A specific machine — registers, memory, a clock, a bus. Build the CPU. Wire up the devices. Get something running on screen. Seven releases of steady, focused progress on a concrete goal.

Then commit 11a16aa: Pivot to general-purpose 8-bit machine designer.

One commit. One sentence in the message. Everything that came after it — the Machine Designer, the JSON preset system, the multiple CPU architectures, the ZX Spectrum, the Apple IIe — flows from that decision.

What Triggered It

The pivot didn’t come from nowhere. It came from noticing that the work already done had a more general shape than the specific goal it was aimed at.

A bus is a bus. A CPU is a CPU with a pluggable instruction set. A device that responds to reads and writes at certain addresses is the same abstraction whether it’s a VIC chip or a ULA or something that hasn’t been built yet. The specific machine was one instantiation of a general system, and the general system was already mostly there.

The question wasn’t “should we build a general designer?” It was “do we want to keep hardcoding what could be configurable?” Each new machine added to a hardcoded system means forking logic, duplicating device wiring, and maintaining parallel paths. Each new machine added to a general designer means writing a new preset and a new device — and the rest of the system handles it automatically.

The Cost of Pivoting Early vs. Late

The v0.8 pivot happened before the C64 preset was complete, before the Z80 existed, before any second machine had been built. That timing matters.

Pivoting to general early means the general abstractions get tested against real cases as they’re being built. The IBusDevice interface, the JSON preset system, the device clocking model — all of these were shaped by having to work for more than one machine from early on. The abstractions are better because they were generalized before they calcified.

Pivoting late — after three specific machines are fully implemented — means retrofitting generality onto systems designed around specific assumptions. The bus probably has C64-specific logic baked in. The preset loader probably has architecture-specific branches everywhere. The refactor is larger, riskier, and more likely to break things that already work.

The right time to generalize is when you can see the second case clearly enough to design for it, but before the first case has made the design inflexible.

How to Recognize the Signal

The signal that a specific implementation should become a general one usually shows up as one of a few things:

You’re about to copy code. If adding the second machine means copying the first machine’s wiring and changing some constants, you haven’t found the abstraction yet — you’ve just found the parameters. The abstraction is whatever is left when you pull the parameters out.

Your if/else chains are growing. Every place in the codebase that branches on “which machine are we running” is a place that will need a new branch for every machine you add. A table or a registry replaces the branch with a lookup.

The new case fits cleanly into a hypothetical general interface. If you can describe how the new thing would work using the same vocabulary as the existing thing — same reads/writes, same clock, same reset — the general interface is already implicit. Make it explicit.

What Generality Costs

The pivot wasn’t free. A general machine designer is a harder problem than a specific emulator. The JSON preset system, the device registry, the Machine Designer UI — none of that would have existed if the goal had stayed narrow. The early versions of the general system were rougher than the specific system they replaced would have been.

There’s also a scope risk. “General-purpose” can become a trap where the design tries to accommodate every possible case and ends up optimized for none. The 8-Bit Machine stayed grounded because the generality was constrained: 8-bit CPUs, bus-based memory maps, cycle-clocked devices. Not “any computer ever.” The abstraction matched the actual problem domain.

Generality that emerges from real cases is different from generality that’s imagined in advance. The pivot at v0.8 was grounded — there were two real machines in mind, the abstraction was visible in the existing code, and the investment made sense. That’s the version worth making.

Tags: Architecture Product Thinking The 8-Bit Machine Open Source