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.
useState, useRef, useEffect: how not to use them โ๏ธ Below an example of implicit state machine ๐ Multiple useState to track current state ๐ Multiple useEffect to track effects ๐ Checking states in functions
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"
me explaining to management why the bundle size got bigger after i npm install 27 packages
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".
That's probably how 90% of react projects are written...
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 ๐
Updated personal website homepage ๐ Looking to connect with people sharing interests, see if we do ๐
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 ๐