There is a folder on my computer full of projects that absolutely did not need to exist.

A RuneScape tool that could have been a spreadsheet.

A tiny utility that could have been a bookmark.

Three different attempts at building my own component library because apparently using the thousands of perfectly good ones already available would have been too easy.

Half-finished websites. Weird experiments. APIs nobody asked for. A shocking number of folders named things like final, final-v2, and actually-final.

Most of them never became anything.

And I don’t think that was a waste of time.

In fact, I’m starting to think the useless projects taught me more than the useful ones ever did.

The Spreadsheet Problem

The OSRS POH Planner is probably the best example.

The original problem was simple: I wanted an easier way to plan a Player Owned House in Old School RuneScape.

That’s it.

I could have opened Excel.

I could have made a grid, typed room names into cells, and been done before dinner.

Instead, my brain went:

What if I built an entire interactive application for this?

Which is an objectively worse solution if the goal is simply planning a RuneScape house.

But the goal quietly changed.

I wasn’t really trying to solve the POH problem anymore.

I was trying to figure out how to build the thing I could see in my head.

And that’s a completely different problem.

“I’ll Just Make a Grid”

Every dangerous programming project begins with a sentence like this.

“It should be pretty simple.”

It was just a grid.

Until rooms needed coordinates.

Then rooms needed rotation.

Then the doors needed to line up.

Then dragging needed to work.

Then deleting.

Then moving.

Then I wanted the materials to update automatically.

Then I realized the application needed to understand what was actually inside each room.

Suddenly this:

const room = {
  type: "garden",
  x: 4,
  y: 7,
};

was becoming this:

const room = {
  id: crypto.randomUUID(),
  type: "superior-garden",
  position: {
    x: 4,
    y: 7,
  },
  rotation: 90,
  doors: {
    north: true,
    east: false,
    south: true,
    west: false,
  },
  hotspots: [],
  cost: {
    coins: 75000,
    materials: [],
  },
};

And now we’re doing architecture.

All because I wanted to move a fake room around a fake piece of land in a medieval clicking simulator from 2007.

Perfect.

That’s Where the Learning Happens

Tutorials are useful.

Documentation is useful.

Courses are useful.

But there’s something fundamentally different about encountering a problem because you created the problem yourself.

Nobody told me:

Today we’re going to learn about coordinate systems.

I wanted drag-and-drop rooms.

So suddenly coordinate systems mattered.

Nobody assigned me a lesson about application state.

I moved one room and accidentally caused three other things to break.

Now application state mattered.

That’s the difference.

You’re no longer learning something because a curriculum says you’ll need it someday.

You need it right fucking now.

The feedback loop changes

Traditional learning often looks something like:

  1. Learn concept.
  2. Complete exercise.
  3. Move to next concept.
  4. Forget approximately 60% of it.
  5. Encounter it six months later.
  6. Google it again.

Side projects reverse that:

  1. Want something.
  2. Try to build it.
  3. Break something.
  4. Figure out why.
  5. Fix it.
  6. Immediately understand why the concept matters.

That fifth step is doing a lot of work.

You Start Discovering Your Own Bad Habits

There’s another benefit nobody really advertises.

Your projects eventually become archaeological sites documenting every terrible programming decision you’ve ever made.

You open something from six months ago and see:

function thing(data, x, thing2, idk) {
  // TODO fix this
}

And the disturbing part isn’t that the code is bad.

It’s that you remember writing it.

Worse, you remember thinking it was pretty good.

That’s progress.

Old code should occasionally embarrass you.

If I look at something I wrote two years ago and wouldn’t change anything, either I accidentally achieved programming enlightenment or I haven’t learned shit in two years.

I’m betting on the second one.

The Graveyard

My projects generally end up in one of four states:

  • Finished — suspiciously rare.
  • Active — I’m currently convinced this one will definitely be finished.
  • WIP — technically alive.
  • Abandoned — died doing what it loved: accumulating dependencies.

The abandoned ones used to bother me.

I’d look through GitHub and see repositories that hadn’t been touched in eight months and feel like I had failed somehow.

Because we treat finishing as the thing that gives work value.

I don’t really believe that anymore.


One abandoned project taught me more about Canvas rendering than any tutorial I’d followed.

Another taught me authentication because I massively overengineered a tool that would have had approximately four users.

Another existed for two evenings and taught me enough about an API to realize my entire idea was stupid.

That’s still useful.

The project doesn’t have to succeed for the experiment to succeed.

A Completely Unnecessary Example

Suppose I want a button that tracks how many times I’ve clicked it.

Normal people:

<button>Click me</button>

Me, approximately twelve minutes later:

class ExtremelyNecessaryButton {
  constructor(element) {
    this.element = element;
    this.clicks = 0;

    this.element.addEventListener("click", () => {
      this.increment();
      this.render();
    });
  }

  increment() {
    this.clicks += 1;
  }

  render() {
    this.element.textContent = `Clicked ${this.clicks} ${this.clicks === 1 ? "time" : "times"}`;
  }
}

const button = new ExtremelyNecessaryButton(document.querySelector("#button"));

Is this better?

No.

Did we need a class?

Absolutely fucking not.

Would I ship this?

God, I hope not.

But there’s an important distinction between production code and learning code.

Production code should generally solve the problem as clearly and reliably as possible.

Learning code sometimes needs to take the stupid route because you’re trying to understand what happens along the stupid route.

Those are different objectives.

Complexity Is Fun Until It Isn’t

There is a trap here.

Once you realize projects are playgrounds, it’s incredibly easy to start treating unnecessary complexity as a feature.

Your personal website doesn’t need Kubernetes.

Your grocery list probably doesn’t require microservices.

The application with six users does not need to be architected for “when we hit our first ten million users.”

And your static blog probably doesn’t need a database.

Actually, that’s a good example.

This site is built with Astro.

A writing collection can basically be:

const posts = await getCollection("writing", ({ data }) => !data.draft);

const sortedPosts = posts.sort(
  (a, b) => b.data.published.valueOf() - a.data.published.valueOf(),
);

That’s boring.

Boring is good.

The temptation is to say:

But what if I eventually need—

Stop.

Future Anthony can deal with Future Anthony’s problems.

That asshole probably deserves it anyway.

The Rule I’ve Started Using

I’ve started separating complexity into two categories.

Complexity that teaches me something

Good.

Build the stupid WebGL effect.

Write the Canvas renderer.

Try building the component from scratch.

Make your own tiny router just to understand how routing works.

Break things.

Complexity that only makes me feel like a Serious Developer™

Bad.

Adding infrastructure because big companies use it.

Choosing technology because it looks impressive on a diagram.

Creating abstractions before there’s anything to abstract.

Building systems for hypothetical scale.

There is a huge difference between:

“I’m doing this the hard way because I want to learn how it works.”

and:

“I’m doing this the hard way because this is how I think real programmers are supposed to do it.”

The first one is experimentation.

The second one is cosplay.

Sometimes the Ugly Version Is Better

One of the things I’ve been thinking about while rebuilding this website is how aggressively polished the web has become.

Everything wants to look finished.

Every portfolio needs cinematic transitions.

Every SaaS homepage needs the giant gradient headline.

Every button needs to float upward three pixels when you hover over it.

Everything needs to communicate:

THIS IS A SERIOUS DIGITAL PRODUCT.

But this isn’t a product.

It’s my website.

That’s why there’s a section literally called Experiments & Fragments.

Some things on here might be unfinished.

Some might be dumb.

Some might disappear next month.

Some JavaScript might occasionally be held together with the digital equivalent of duct tape.

That’s fine.

A personal website doesn’t have to prove that you know everything. It can document you figuring things out.

I think that’s considerably more interesting anyway.

Code Should Have Fingerprints

This might be the bigger thing I’ve been circling around.

I like software that feels like somebody made it.

Not intentionally bad software.

Not inaccessible software.

Not interfaces that sacrifice usability for aesthetics.

Just things with fingerprints.

A weird animation.

A strangely specific feature.

A joke buried in an error message.

A UI decision that no product manager would ever approve.

Something that makes you think:

Yeah, somebody actually gave a shit about this.

You can see it in games too.

RuneScape is filled with weird shit that probably wouldn’t survive a modern product design meeting.

That’s part of why people still love it.

The imperfections become texture.

Build the Dumb Thing

So that’s increasingly my rule.

If an idea sounds fun, build it.

Don’t immediately ask whether it could become a startup.

Don’t buy the domain.

Actually, definitely don’t buy the domain. I have enough fucking domains.

Don’t make a roadmap.

Don’t design the monetization strategy.

Don’t announce your revolutionary new platform on LinkedIn.

Open the editor.

Make the folder.

npm create astro@latest dumb-idea
cd dumb-idea
npm install
npm run dev

And start fucking around.

Maybe it becomes something.

Maybe you abandon it Thursday.

Maybe six months later you realize the dumb rendering experiment you built at 1:00 AM taught you exactly what you need for an actual project.

That’s enough.

The Internet Needs More Weird Little Things

I don’t want the web to become nothing but platforms.

I like stumbling across somebody’s weird little website dedicated to an incredibly specific thing they care about.

A calculator somebody built because the existing calculators annoyed them.

A fan site that looks like it hasn’t acknowledged a design trend since 2004.

A tool with twelve users whose creator knows ten of them.

A blog nobody optimized for engagement.

Software doesn’t always need a market.

Sometimes the entire business case can be:

I thought this would be cool.

That’s probably how half the interesting parts of the internet started anyway.

So, What Am I Building?

Too much.

Obviously.

There are projects I want to finish.

There are projects I probably won’t finish.

There are things sitting in folders that I’m going to rediscover six months from now and have absolutely no memory of creating.

And there will definitely be another moment where I think:

This should only take an hour.

It will not take an hour.

It will somehow involve three APIs, an obscure browser rendering bug, seventeen Stack Overflow tabs, and a GitHub issue from 2019 containing the only person on Earth who has experienced the exact same problem.

And I’ll probably have a great time.


Build useless shit.

Break it.

Figure out why it broke.

Make it slightly less shitty.

Abandon it if you stop caring.

Take what you learned and build the next thing.

The folder full of unfinished projects isn’t necessarily evidence that you never finish anything.

Sometimes it’s evidence that you’ve been learning the entire time.