Why I keep building things from scratch

2026-08-26 · General

Across the projects on this site there's a pattern that wasn't really a decision so much as a habit: a shell that implements its own process management instead of wrapping system(), a WebSocket layer built as a hand-rolled hub instead of a pub/sub library, a 3D renderer with its own .obj parser and triangulation instead of a scene-graph dependency. None of that is an aversion to libraries — it's a bet about which parts of a system are worth understanding at the level where you could rebuild them, and which parts aren't.

"From scratch" has a boundary

None of these projects avoid dependencies wholesale. Postgres, OpenGL, React, nginx, gRPC's code generation — all off-the-shelf, all doing exactly what they're supposed to do, and none of them rewritten. The line isn't "avoid libraries," it's "don't outsource the part of the problem the project actually exists to solve." A casino app that wraps someone else's WebSocket abstraction hasn't really built a real-time system — it's built a UI on top of one. The interesting, load-bearing part is the part you keep.

minishell: process management as the actual subject

A shell's entire job is process orchestration — fork/exec, pipe chaining, signal delivery, redirections. Using a library for that would be building a shell around a shell. So minishell implements its own recursive-descent parser and drives fork(), execve(), pipe(), and sigaction() directly, because getting SIGCHLD reaping and job control right under POSIX semantics is the assignment, not a yak-shave standing in front of it.

The Transcendence WebSocket hub: control over an off-the-shelf socket layer

The Transcendence Casino's real-time layer could have leaned on a pub/sub library for topic fan-out. Instead it's a hand-rolled hub that tracks per-user, per-tab, per-topic subscriptions and debounces presence explicitly, because the actual requirement — a page refresh in one tab shouldn't flicker a player's status offline in every other tab watching that table — is specific enough that a generic pub/sub abstraction would need workarounds bolted on anyway. Writing the hub directly meant the debounce logic lived exactly where the domain problem was, not translated through someone else's event model.

The Go renderer's parser: no scene graph to lean on

The 3D Go Renderer reads raw .obj/.mtl files and does its own vertex deduplication, UV generation, and ear-clipping triangulation over a Newell-normal plane. A scene-graph library would have handled all of that — and would also have hidden exactly the geometry problems that made the project worth doing. The zero-steady-state-allocation render loop only works because the parsing and triangulation code was written knowing precisely what it allocates and when, which isn't something you get for free from someone else's abstraction.

The actual lesson

The test isn't "could I write this myself" — you can write almost anything yourself given enough time. It's whether writing it yourself is how you find out something you needed to know: how SIGCHLD actually behaves under concurrent child processes, what a debounce needs to guarantee when a table has forty spectators, why a face's projection plane matters more than its triangulation order. Reach for a library when the problem is solved and the solving isn't the point. Build it yourself when the building is the only way to actually understand what you shipped.