Why I Build Things From Scratch

I have a habit that puzzles some of my colleagues: I build things from scratch that already exist. I've written a raytracer when Blender exists. I've implemented a TCP server when countless libraries do this better. I've built a hash map when every language has one built in.

This isn't about reinventing wheels or NIH syndrome. It's about understanding how the wheels work—and knowing when to use the existing ones.

The Knowing Gap

There's a difference between using something and understanding it. I can use a hash map perfectly well without knowing how it works. I can call map.get(key) all day. But until I implement one myself, I don't really know hash maps.

Why does load factor matter? What happens during a resize? Why do some operations take O(n) in the worst case? Why might I choose a tree-based map over a hash-based one? These questions only make sense after you've hit the walls yourself.

"What I cannot create, I do not understand." — Richard Feynman

This isn't gatekeeping. Plenty of excellent developers use tools without understanding their internals, and that's fine. But for me, deep understanding leads to better decisions. When I know how the pieces work, I can predict behavior, debug faster, and choose the right tool for the job.

The Raytracer

Last year, I built a software raytracer from scratch. No graphics libraries, no shaders—just math and pixels. It renders spheres, handles reflections, and produces surprisingly pretty images for something that takes minutes to render a single frame.

Was this practical? Not at all. Blender renders better images in milliseconds. But building that raytracer taught me:

  • How light actually works (or how we approximate it)
  • Why ray-object intersection is the bottleneck
  • Why spatial data structures like BVH exist
  • Why GPUs are so much faster at this
  • The difference between physically-based and Phong shading

Now when I read about RTX ray tracing or look at rendering in game engines, I understand the fundamentals. I can read papers about global illumination and follow along. That knowledge compounds.

The TCP Server

My TCP server project started as "let's see how hard this is" and ended up teaching me more about networking than any course.

// What I thought networking was
socket.connect(host, port);
socket.send(data);

// What networking actually involves
// - Socket creation and binding
// - Non-blocking I/O or thread pools
// - Connection state machines
// - Buffer management
// - Graceful shutdown
// - Timeout handling
// - Keep-alive management
// - Error recovery
// ...

Before building this, I used HTTP libraries and network frameworks without thinking. After building it, I understand why those abstractions exist and what trade-offs they make. When something goes wrong at the network layer, I have a mental model to debug with.

When To Build From Scratch

Not everything is worth building from scratch. I'm not writing my own operating system or compiler (yet). The decision depends on a few factors:

1. Is this foundational to my work?

I build things that are fundamental to domains I work in. As someone working with distributed systems, understanding networking, concurrency, and data structures at a deep level pays dividends. I probably don't need to write my own text editor.

2. Is the knowledge transferable?

Building a raytracer taught me linear algebra, spatial data structures, and optimization techniques that apply far beyond graphics. Building a custom CMS for my blog probably wouldn't transfer as well.

3. Can I scope it reasonably?

A hash map takes a weekend. A database engine takes years. I pick projects I can complete in weeks, not months. Unfinished projects teach less than finished ones.

When To Use Existing Solutions

At work, I almost always use existing solutions. The TCP server I built? It's a learning project, not a production system. In production, I use battle-tested libraries with years of bug fixes and security patches.

The goal isn't to replace existing tools—it's to understand them well enough to use them effectively.

The paradox: The better I understand how something works, the more comfortable I am using someone else's implementation. I can evaluate trade-offs, read the source when needed, and contribute fixes.

The Learning Curriculum

I think of these projects as a self-directed curriculum. Each one fills in a piece of the puzzle:

  • Hash map → hashing, collisions, memory layout
  • Raytracer → linear algebra, rendering pipeline, optimization
  • TCP server → sockets, protocols, I/O models
  • Memory allocator → how malloc works, fragmentation
  • Interpreter → parsing, ASTs, evaluation

Each project builds on previous knowledge. The memory allocator was easier after understanding how hash maps manage buckets. The interpreter was easier after implementing the expression parser for my calculator.

Beyond Code

This philosophy extends beyond programming. I read technical papers, not just blog post summaries. I study the internals of databases I use, not just their query language. When I use a framework, I eventually read its source.

It's slower. There's always more to learn than time allows. But the understanding compounds. Each piece of deep knowledge makes learning the next thing easier.

Practical Advice

If this resonates with you, here's how I approach these projects:

  1. Start small. Implement a basic version first. Add features incrementally.
  2. Use resources wisely. Read one good reference, not ten. "Crafting Interpreters" for interpreters. "Ray Tracing in One Weekend" for raytracers.
  3. Set scope limits. Define "done" before you start. A raytracer that renders spheres is done. Adding every feature in Blender is not the goal.
  4. Write about it. Explaining what you learned solidifies understanding. Even notes to yourself help.
  5. Keep the code. Your implementation is a reference you can return to. I regularly look back at old projects.

The Point

I build things from scratch not to replace existing tools, but to understand them. That understanding makes me a better engineer—able to debug deeper, choose better, and learn faster.

It's not about the code. The raytracer sits unused. The TCP server never handled real traffic. But the knowledge they represent? That I use every day.