Inkstead, ECS, and the Case for Refactoring Before Rewriting
Inkstead started with a familiar 0-to-1 shortcut: put the game together, keep the feedback loop tight, and let the main entry point grow as the idea becomes real. That works until the entry point becomes the architecture.
The interesting question is not simply whether the code should use classes. It is whether the game has clear boundaries that a human—and an AI coding agent—can reliably navigate.
The rewrite temptation
Once a Node.js or TypeScript game server has a very large main.ts, a new framework can look like a clean escape hatch. Blazor Web App, with Interactive Server and WebAssembly modes, offers an attractive C# model: explicit types, familiar object-oriented structures, and components that combine UI with behaviour.
For a single-player survival game, Interactive Server is more plausible than it would be for a twitch multiplayer title. The game can keep state on the server, use SignalR for interaction, and make inventory, crafting, progression, and save management straightforward to model.
But a framework migration is still a rewrite. It replaces the runtime, language, build pipeline, deployment model, and much of the existing client integration at the same time. That is a lot of risk to take on when the original problem may simply be that one file has accumulated too many responsibilities.
What the real problem probably is
A monolithic file makes every change expensive. An agent has to read unrelated rendering, input, persistence, world simulation, and UI code before it can safely touch one mechanic. The result is more context, more accidental coupling, and more opportunities for a locally reasonable change to break something elsewhere.
That is an architectural problem, not necessarily a TypeScript problem. Strong types help, but the bigger win comes from creating small, explicit context boundaries.
An ECS-style split without leaving TypeScript
An Entity-Component-System approach is a useful target for Inkstead because it separates three concerns:
- Entities are stable identities such as the player, a campfire, an animal, or a dropped item.
- Components hold data such as position, health, hunger, inventory, temperature, or movement state.
- Systems apply focused rules: survival ticks, crafting, weather, movement, combat, saving, and so on.
Components should be boring data. Systems should be narrow and testable. The entry point should initialise the world, register systems, connect adapters, and start the loop—not contain every rule in the game.
src/
components/
Position.ts
Health.ts
Hunger.ts
Inventory.ts
entities/
createPlayer.ts
createCampfire.ts
systems/
SurvivalTickSystem.ts
CraftingSystem.ts
WeatherSystem.ts
types/
GameEvent.ts
World.ts
main.ts
This is not a demand to turn every function into a class. Pure functions and plain objects are often easier to test and easier for agents to modify than deep inheritance trees. The goal is predictable ownership of state and behaviour.
Why this may be better for AI-assisted development
AI agents do not benefit merely because a project uses C# or classes. They benefit when the repository exposes a small, legible surface for each task.
- A request to change starvation damage can point to one component and one system.
- A request to add a crafting recipe can avoid the networking and rendering layers.
- Typed event payloads make the contracts between systems visible.
- Small files reduce the amount of unrelated context an agent must hold while editing.
- Tests can exercise a system with a small world fixture rather than booting the entire game.
This also exposes an uncomfortable lesson about agent protocols and documentation: good guidance is not the same as an enforced architecture. If a project starts with no file-tree checkpoint, no responsibility map, and no size or boundary rules, an agent will often optimise for the shortest path to a working demo. That usually means appending to the file already open.
When Blazor becomes the right move
Blazor remains a credible future option if the actual constraints justify it. A move toward .NET makes more sense when Inkstead needs a C#-centric team, server-side domain services already live in that ecosystem, or the game benefits from a shared .NET backend and tooling investment.
For a smooth, continuously animated game, client-side execution is still important. A pure Interactive Server design makes every interaction depend on a network circuit and keeps per-player state in server memory. Interactive WebAssembly or a hybrid client/server model can reduce that latency, but also increases migration and synchronisation work.
The decision should therefore follow measured needs—latency, offline support, memory use, deployment, and team capability—not the discomfort of seeing a large main.ts.
A safer refactoring sequence
- Audit first. Map state variables, event handlers, rendering paths, persistence, and simulation functions.
- Write the target tree. Decide which data belongs in components and which rules belong in systems before moving code.
- Extract types and data. Keep behaviour unchanged while making contracts explicit.
- Extract one system at a time. Start with a bounded mechanic such as hunger, crafting, or weather.
- Add focused tests. Prove the extracted system behaves like the old path.
- Slim the bootstrap. Leave
main.tsresponsible for composition rather than implementation. - Measure before deciding. Revisit Blazor only if the refactored TypeScript architecture still fails a real requirement.
The practical verdict
For Inkstead, the least risky next step is not “rewrite it in C#” and not “convert everything into classes.” It is to keep the existing TypeScript investment and give it a better shape: explicit domain types, small modules, ECS-inspired state separation, and a deliberately boring entry point.
That path preserves momentum while making the project easier for both people and agents to understand. A rewrite may still be justified later. It should be the answer to a measured constraint—not the first response to a monolithic file.
Source conversation: Gemini discussion about refactoring Inkstead.