May 08, 2025
Beginner
It's by Making Mistakes That You Learn
If you're already a developer, you've probably run into that dreaded red text on the screen, which can indicate anything from a typo, a typing error, an incompatible library, a failed compilation, or even something that, honestly, you've never understood to this day. If you're just starting out in the world of programming, you can brace yourself: this is going to happen, a lot.
But when these errors show up, do you really pay attention to them? Or do you copy that whole block of red, paste it into Google or Stack Overflow, and hope that some internet hero has been through the same pain? If you've done that, it's okay. I've done it too. But... does it always have to be this way?
If you clicked on this article expecting that worn-out phrase "it's by making mistakes that you learn," you're… absolutely right. But also completely wrong. Today we're going to talk about mistakes in programming and why they are, in fact, one of the best learning tools you can have.
Moooom, the screen went all red!
Everyone who's ever run code knows that feeling: you hit "run" full of hope, but instead of seeing your program working, you're greeted by a flood of red characters that look more like a digital scream of despair.
In that moment, it's common to doubt everything: your career, your choice of course, your own sanity. But relax, dev! The errors that show up in your terminal aren't your enemies. On the contrary, they're often valuable allies.
Throughout my career, I've realized that errors taught me more than many tutorials did. They helped me better understand the context I'm working in, whether it's a language, a framework, or a specific project flow.
I've mentored a few people, and one curious thing is how almost all of them follow the same "ritual" when they hit an error: first, the panic, as if the compiler had thrown a fireball at the console.
Then, the bug hunt begins, often in the wrong place. Next, the classic: copy the error message, paste it into Google, and pray that some Stack Overflow wizard casts the perfect answer. And finally… that moment when they stare at the screen for five minutes, in silence, as if waiting for the code to feel guilty and fix itself.
The funny thing is that almost no one actually tries to read the error message. Often, it points to exactly where the problem is, with the file name, the line, and even the character where the error occurred. Tools like Visual Studio Code (or VSCode, for those on a first-name basis) offer incredibly rich information, including a built-in debugger! So why do so many people ignore this?
And why does this matter?
Making mistakes is frustrating. What we really like is getting things right. But to get there, you first have to understand what's wrong and why. That's where learning happens. One of the moments where I learned the most about JavaScript, for example, was precisely while facing errors. For instance, do you know how JavaScript handles passing by value or by reference? No? Don't worry, I didn't either.
Value or reference?
In one of my many coding adventures, I tried to pass the result of a request (an object) to a function, intending to create a new, modified version of it. However, inside that function, I removed a property, an array of objects, that I'd need to use later on. Guess what? When I tried to access it again, it no longer existed.
That's how I learned how JavaScript handles passing arguments: primitive types are passed by value, that is, they're copied. Objects (like arrays and functions), on the other hand, are passed by reference, so any change made inside the function directly affects the original object. And it was exactly this behavior that caught me off guard. For example:
let age = 4;
const changeAgeValue = (age) => {
age = 5;
console.log(age); // console.log result: 5
};
changeAgeValue(age);
console.log(age); // console.log result: 4
// Even though I changed its value inside the function "changeVariableValue".
Here, the function changeAgeValue received a copy of the variable age. Changing that copy doesn't affect the original variable outside the function.
Now look at what happens with an object:
let user = {
age: 4,
};
const changeUserAge = (user) => {
user.age = 5;
console.log(user.age); // console.log result: 5
};
changeUserAge(user);
console.log(user.age); // console.log result: 5
// Value passed by reference
In this case, the object was passed by reference, meaning the function directly changed the original structure in memory. That's why the change persisted even outside the function.
It was precisely this behavior that caught me off guard. I deleted a property of an object thinking I was dealing with a copy, but it was the original.
The error
The result? A classic error:
TypeError: Cannot read properties of undefined (reading forEach) at file:///index.js:11:10
Translating: "Type error: cannot read properties of something undefined (trying to use 'forEach'), in the file index.js, line 11, character 10."
In other words, the interpreter is warning that it tried to run a .forEach on something that doesn't exist, an undefined. And it was even helpful, telling me exactly where this happened: file index.js, line 11, character 10 ("at file :///index.js:11.10").
From there, it was just a matter of investigating why that property was undefined. And that's how I learned, in practice, the concept of reference in JavaScript. All of this because I read the error. I didn't try to guess. I didn't paste it into Stack Overflow right away. I just stopped and interpreted what was being said.
And that alone already saved me hours of frustration.
A note
A practical way to avoid unwanted changes to the original object is to create a real copy of it — like a little life hack to save you from bugs that are hard to track down. If you really need to work with an independent copy, you can use something like this:
const originalUserObject = {
name: "João",
age: 25,
};
const userObjectCopy = JSON.parse(JSON.stringfy(originalUserObject));
Did you notice what happened here?
This technique turns the object into a JSON string, and then rebuilds it as a new object, completely separate from the original. This means any change made to the copy doesn't affect the original.
Why does this work? I'll leave that as a challenge for you to investigate.
Conclusion
I could spend hours and hours telling you a bit more about the various errors I've faced in my life. But I don't need to, you'll run into them too. What matters is: How will you deal with them when they arrive? My tip: Stop, read, think. Most important of all, breathe.
Valuing your own learning often means taking a step back, breathing deeply, and dedicating yourself to understanding the problem calmly, before reaching for a ready-made solution somewhere else. It's in that moment of investigation that knowledge truly solidifies. The more you understand your own mistakes, the more mastery you gain over what you're doing. And that understanding comes with practice, trial, and lots of failure.
Many people think that to be a good programmer you have to be a genius or understand everything about math. As time went by, I became more and more confident that the most important skill for being a good programmer is: patience. With yourself, with your growth, with others, and even with your mistakes.
Because, in the end, it's by making mistakes that you learn, but only if you're willing to learn from the mistake.
Post Author

Leonardo Henrique
E aí! Sou o Leonardo Henrique, mas muita gente me conhece como "Leozinho do Front" — culpa da minha paixão por Front-End. Trabalho como desenvolvedor Full Stack, com bastante experiência em Angular, um carinho especial por React e, agora, me aventurando com Vue. Curto demais jogos e animes, e um dia ainda quero me aventurar na área de games.