โ€ข

newsletter

The origin of bad code, and how to fix it

Why bad code exists? What makes code 'bad'? Moving from junior to senior dev is about understanding bad code, learning how to overcome it, and never look back.


Sandro Maglione

Sandro Maglione

Software development

It's hard writing bad code.

That's right. After you internalize patterns for maintainable and type safe software it's hard to remember how bad bad code can become.

It's visceral: you sense what's not right, and your being fights you to make it better.

This week I have been experimenting with some bad code, with the goal of understanding how you can reach the other side ๐Ÿ‘‡


Why "bad code" exists

As you get started at anything you learn beginners patterns first. That's natural.

Furthermore, the second best way of learning any craft is to practice it.

The first best way is to teach it.

You may see a pattern here:

  • Beginners learn beginners patterns
  • Beginners are eager to learn more
  • The best way to learn is teaching

Result: most of the "teaching" material is about beginners patterns.

Meanwhile, more senior devs like the practice more than the talk. In a sense, that's how they became senior in the first place: doing the practice consistently.

Alas, bad code was born.

It's not all a "skill issue"

Okay, the story is more complex than that.

In the highly dynamic world of software development villains are everywhere:

  • "The deadline is tomorrow"
  • "Let's just add one new simple feature"
  • "TODO for later, when we have time for refactoring"
  • "Who wrote this? Ah, it was me 1 year ago"
  • "Who wrote this? Ah, it was that guy that left the company 5 years ago"
  • "That abstraction is too complex for new junior devs"
  • "This version is outdated, but we cannot upgrade because of another conflicting dependency"
  • "I know that library helps, but its bundle size of 10kb is too large, let's rewrite it internally"

Pain creates strong memories

Sometimes the only way to reach the other side (aka "senior") is to experience the pain of bad code. Embark on a journey to find a better solution. And, once you find it, promise yourself you'll never be back.

Problem is that "clean" code often introduces abstraction. It's hard to explain abstractions. Things like dependency injection, observability, pure functions.

These are all patterns that pay off in the long-term, but initially all they cause is more code and more trouble.

You can see the analogy with exercising: hard and painful at the beginning, easier and easier as you progress.

Back to the origin

On my blog and typeonce.dev I aim to introduce "complex" patterns to the largest possible amount of people.

That's hard to do if you don't understand the position of your audience.

That's why I have been experimenting more and more with more "basic" code, and in showing the different between bad code and maintainable code.

Here is my latest piece of art:

import { useEffect, useState } from "react";

interface Notification {
  id: string;
  message: string;
}

export default function Page() {
  const [notifications, setNotifications] = useState<Notification[]>([]);
  const [open, setOpen] = useState(false);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState("");

  const onLoadNotification = async () => {
    if (!loading) {
      try {
        setError("");
        setLoading(true);
        const response = await new Promise<Notification[]>((resolve) => {
          setTimeout(() => {
            resolve([
              { id: "1", message: "It's time to work" },
              { id: "2", message: "Up for a meeting?" },
            ]);
          }, 1200);
        });

        setOpen(true);
        setNotifications(response);
      } catch (e) {
        setError("Error while loading notifications");
      } finally {
        setLoading(false);
      }
    }
  };

  useEffect(() => {
    onLoadNotification();
  }, []);

  return (
    <div>
      {!open ? (
        <>
          {loading && <span>Loading...</span>}
          {error.length > 0 && <span>{error}</span>}
        </>
      ) : (
        <>
          {notifications.map((notification) => (
            <p key={notification.id}>{notification.message}</p>
          ))}
        </>
      )}
    </div>
  );
}

I am coming back to the origin to understand how code looks like out there, and how to bridge the gap between "out there" and "right here".

My upcoming course on effect and the next one coming on XState are based on this research.

They both start from basic patterns, they explain their pitfalls, and they build up a mental model for more maintainable practices.

The effect course content is in review this week, a lot of people are going through it to provide feedback.

XState course is work in progress, check out the open source repository with all the code of the course.


New homepage at sandromaglione.com ๐Ÿ‘‡

Both my personal blog and Typeonce are now at a good stage, the code is all there and working. Now it's all about the content: coming! ๐Ÿ”œ

See you next ๐Ÿ‘‹

Start here.

Timeless coding principles, practices, and tools that make a difference, regardless of your language or framework, delivered in your inbox every week.