DEV Community

Rodolphe D.
Rodolphe D.

Posted on

Build One Ugly System. It'll Teach You More Than 10 Tutorials.

TL;DR: Tutorials can't teach judgment because they never let you be wrong about something that matters. The fix isn't more courses — it's building one small, ugly system where your mistakes have consequences.

I want to tell you about the night I wasted four hours on a bug.

It was a race condition in a queue I'd written. The code looked fine. Tests passed. Then in production, every few hours, a job would run twice. I pasted a fix from Stack Overflow. It stopped. I still don't fully know why it worked.

That night taught me more than any tutorial I'd done — not because I learned about race conditions, but because I learned I didn't understand the thing I'd built.

What "System" Actually Means

People say "build systems" like it's a magic spell. For a long time, I said it too without knowing what I meant. Let's fix that.

A system is a set of things that depend on each other. That's it. That's the whole definition.

A pile of bricks isn't a system. A wall is. Pull one brick and something else moves — or the whole thing falls. That's the difference. Not size. Not complexity. Consequences.

I didn't get this for two years. I thought "system" meant "big project" or "architecture." It doesn't. It means: things you can be wrong about.

Here's a task:

function sendEmail(user) {
  return mailer.send(user.email, "Welcome");
}
Enter fullscreen mode Exit fullscreen mode

Here's a system:

async function sendEmail(user, attempt = 1) {
  try {
    return await mailer.send(user.email, "Welcome");
  } catch (err) {
    if (attempt >= 3) {
      await queue.deadLetter({ user, err });
      return;
    }
    await wait(2 ** attempt * 1000);
    return sendEmail(user, attempt + 1);
  }
}
Enter fullscreen mode Exit fullscreen mode

The first one assumes email always works. The second one assumes email fails — and that you don't know when, or why, or how many times.

That's the difference. The first is a task. The second is a claim about the world you'll have to defend.

And here's the thing: even the second version is wrong. It assumes that if mailer.send throws, the email wasn't sent. But a timeout can fire after the mail server already accepted the message. Retry, and your user gets two welcome emails.

Sound familiar? A job running twice. Same bug as my queue, different costume.

The usual fix is to make the operation idempotent — for example, an idempotency key your provider can use to deduplicate. But the fix isn't the point. The point is that I wrote that "robust" version, felt good about it, and it was still making a claim reality could break.

Every line of code you write is that kind of claim. "This user will always have an email." "This queue will never grow past X." "This function never gets called twice." Reality either agrees with you or it doesn't.

A tutorial hides that. A tutorial made all the claims for you, and quietly cleaned up the ones that were wrong.

A system hides nothing. That's the whole point.

Why Tutorials Can't Fix This

There are three classic ways developers stay stuck. You've probably heard them framed as discipline problems: stop copy-pasting, stop course-hopping, stop being scared of real code. Just try harder.

Seen through the lens above, they look different. They're not discipline problems. They're design problems — each one is a way of never having to be wrong.

🚫 Trap 1: Blind Copy-Paste

You paste code you don't understand. AI makes this easier. You accept the suggestion, it works, you move on.

That's exactly what I did with my queue. The bug went away, and I walked off without knowing which of my claims had been false. The fix worked; I didn't learn.

The fix: treat every AI output — and every Stack Overflow answer — like a code review. "Why is this here? What breaks if I remove it?" If you can't answer, don't use it yet. Notice why this works: you're forcing yourself to see the claims hidden in the code. That's system thinking. You're doing it in your head instead of in a repo.

🚫 Trap 2: Disguised Tutorial Hell

You jump from course to course and feel like you're learning. But someone else made all the hard calls.

The fix: build something nobody asked you to build. Not a Twitter clone. Something that solves a real problem for you — a script that cleans your logs, a CLI for a boring workflow. The point isn't complexity. It's that you have to make calls with no safety net.

🚫 Trap 3: Fear of the Real Codebase

Blank project? Fine. Existing 50,000-line codebase? Panic.

The fix: read code you didn't write. Follow one request end-to-end. Learn the debugger — console.log is fine, but it shouldn't be your only tool. A real codebase is just a pile of claims other people made. Reading it is how you learn which ones held up, which ones didn't, and why.

Notice what all three fixes have in common: you're the one who has to be wrong. That's the only thing that grows judgment. Not knowing more. Being wrong more, in places where it counts, and paying attention.

Why Bother

Because your head can't hold everything. A system is a thought you wrote down so you could check it later.

The first time something you built breaks for a reason you didn't predict — that's not failure. That's data. The only kind that makes you better.

Judgment Beats Speed

With AI writing more of the code, typing speed matters less every month. What still matters is being the person who can say: "We shouldn't build it this way. Here's why."

That judgment doesn't come from a tutorial. It comes from owning your mistakes and deciding under uncertainty. It's uncomfortable. It's also the only path.

My queue was my first ugly system. It broke, I patched it without understanding it, and that gap is exactly what I'm asking you not to leave behind.

So next time you're staring at a blank screen: that's where the learning starts. Don't run to another tutorial. Stay. Break it down. Search. Fail. Try again.

And when it works — build the next ugly thing.

💬 If you read this, do one thing: reply with the last thing you built that broke in a way you didn't expect. Not the fix. Just what broke. I'll answer every comment for the next 48 hours.

Top comments (0)