Trying to 'speak computer' isn’t easy when languages, frameworks, and tools are changing and developing all the time.
As a coder, you’ll have to debug and problem-solve a lot.
Sometimes you'll get errors. Sometimes a test will fail. Sometimes nothing at all will happen. And you won't know why.
And this happens pretty regularly.
When I first learned to code, I thought this was something that would happen less frequently as I got better. The truth is - it doesn't. Sorry to be the bearer of bad news!
But it does get quicker to resolve these issues once you figure out:
- how to find the problem
- how to find the answers
And that's basically all bug fixing is.
It's not an exact science, but this guide is generally how I approach problems in my code. What used to have me stuck for days in despair, I can usually resolve in minutes or hours now.
Some of these tips are specific to JavaScript, but most will apply to any language.
- 🤷♀️ Check if your code is actually broken
- 👀 Go through the code line-by-line
- 👩💻
console.log()
it - 📖 Check the docs
- 🔎 Use a search engine
- 😴 Take a break
1. Check if your code is actually broken
This might sound like a weird one, but I have lost count of the number of times I thought my code was broken, but I just wasn't seeing the latest changes.
I'm refreshing my page, and changing my code, and it just isn't working.
And then I get a rush of relief when I realise my code is fine - it just isn't running!
Maybe I hadn't npm run dev
, or I forgot to put the script tag in my HTML. Doh! 🍩
I can't be the only person this happens to?!
If you are forgetful like me, then before you start making changes to your code or getting in a blind panic, check the servers are on, and you are running the latest build.
2. Go through the code line-by-line
Once you are sure there's a problem with your code, and you haven't just forgotten to build/run it, it's time to go through it.
Line.
By.
Line.
I often have to remind myself of this, because it's easy to assume you know where the problem might be, but if I'm stuck and an error message isn't telling me exactly what the problem is, then going through line-by-line brings me the best results.
You might be surprised where you find the problem.
Here's an example.
try {
function Person(name) {
this.name = name;
};
function Activity(person, task) {
this.perrson = person;
this.task = task ;
};
let me = Person("codemzy");
let activity = Activity(me, "bug fixing");
console.log(`${activity.task} started by ${activity.person}`);
} catch(error) {
console.error("Sorry, there's a problem");
}
// Sorry, there's a problem
Unless you know the exact problem, it's a good idea to take the whole code block and check every line.
First, you can go through and try to spot any obvious problems, or improvements you could make. For example, I can see this.perrson
should be this.person
in the Activity
constructor function. I can fix that.
But I'm still getting an error*.
*If I was logging the actual error, instead of a generic "Sorry, there's a problem", that might help.
Once I've looked for anything obvious, if it's still not fixed, I'll get busy with console.log()
.
3. console.log()
it
console.log()
is my bug-fixing weapon of choice, and I don't care who knows it!
When I'm going through a problem block of code, line-by-line, if I can't find any obvious problems, I'll often use console.log()
to help me figure out where the problem is.
Here's a good one if you're not even sure if your code is running...
console.log("I'm running");
Or when things are getting desperate...
console.log("please work!");
Once I have sprinkled some console.log()
's about, I'll run the code to see:
- if what I expected to get logged gets logged
- if any new errors get thrown that give me more information
- if any
console.log()
responses are missing
I'll use code like console.log(1)
, console.log(2)
etc, to figure out where the code stops running or if the right if statements are triggering.
I used to use things like console.log("wtf")
, console.log("nooooo")
, console.log("😭")
, but the numbering system works much better for figuring out where the code stops running.
It's also effective to try and log the variables or results you expect to get, so you can compare the actual results.
Here's an example of how I would log the problematic code from the earlier section:
try {
function Person(name) {
this.name = name;
};
function Activity(person, task) {
this.person = person;
this.task = task ;
};
let me = Person("codemzy");
console.log("me", me);
let actvity = Activity(me, "bug fixing");
console.log("activity", activity);
console.log(`${activity.task} started by ${activity.person}`);
} catch(error) {
console.error("Sorry, there's a problem");
}
// me undefined
// Sorry, there's a problem
Ok, so we can see that me
is undefined
, looks like I didn't put new
when calling my constructor function.
let me = Person("codemzy");
should be let me = new Person("codemzy");
I can fix that, and I can see I've also done it when calling Activity
so I'll fix that too.
Now I'm getting:
// me Person {name: 'codemzy'}
// Sorry, there's a problem
So me
is no longer undefined
, which is great, but I'm still not getting the console.log()
for activity. There must be an error there - let's log it.
} catch(error) {
console.error(error); // <-- log the error
console.error("Sorry, there's a problem");
}
// ReferenceError: activity is not defined
// Sorry, there's a problem
activity
is not defined?! Yes it is?! Here:
let actvity = Activity(me, "bug fixing");
Ohhhhhhh! Typo!
try {
function Person(name) {
this.name = name;
};
function Activity(person, task) {
this.person = person;
this.task = task ;
};
let me = new Person("codemzy");
console.log("me", me);
let activity = new Activity(me, "bug fixing");
console.log("activity", activity);
console.log(`${activity.task} started by ${activity.person}`);
} catch(error) {
console.error(error);
console.error("Sorry, there's a problem");
}
// me Person {name: 'codemzy'}
// activity Activity {person: Person, task: 'bug fixing'}
// bug fixing started by [object Object]
No errors now, which is great! But "bug fixing started by [object Object]" isn't what we want! We can see from our logs that the Person
object is assigned to person
in activity - what we want is activity.person.name
.
Remember to remove all the extra console.log()
's then you get things working!
try {
function Person(name) {
this.name = name;
};
function Activity(person, task) {
this.person = person;
this.task = task ;
};
let me = new Person("codemzy");
let activity = new Activity(me, "bug fixing");
console.log(`${activity.task} started by ${activity.person.name}`);
} catch(error) {
console.error("Sorry, there's a problem");
}
// bug fixing started by codemzy
4. Check the docs
Always check the docs! If you are using a language, library, or framework, there are some great documentation sources that can answer most questions, tell you where you are going wrong, or even show you a better way.
I've often thought I needed a training course, or spent hours trying to fix a problem, and the answer was right there waiting for me in the docs. It's free information!
Here are some of my bookmarks for docs that I regularly use (and like):
- JavaScript
- Node.js
- Express
- ReactJS
- React Router
- React Query
- MongoDB
- TailwindCSS
- Stripe <-- even if you don't use Stripe - check out how beautiful the docs are 😍
5. Use a search engine
If you've tried to fix it yourself, and can't, and you've tried to find the answer in the docs - maybe it's time to take things to the internet.
A good Google search can often lead you to someone with a similar problem on Stack Overflow or another forum.
If you are using an npm package, the git repository issues are often a goldmine of information. Search both open and closed issues to see if someone else has had the same problem (and how they fixed it!). Maintainers will often leave code snippets and workarounds in the comments.
For example, when I was building a WYSIWYG editor with Tiptap, I found some really useful code snippets and solutions in the Tiptap issues on GitHub.
6. Take a break
Sometimes, the bug isn't your code - it's your brain.
I didn't realise when I started coding how much problem-solving was involved. It's often a challenge, trying to come up with solutions to complex problems.
"How should I store this data?"
"Should I use a framework/library?"
"How should I call this function?"
"What's the best way to structure my files/folders?"
"How can I add a new version to my API?"
These are all things that require thinking, and deciding, rather than just sitting down and hammering away at the keyboard.
If you hit a roadblock in your code, or whatever you are building, taking a break is often the quickest way to come up with a solution (as counter-intuitive as that might sound!).
When you give yourself space and time, and take the pressure off, new ideas and solutions can pop up when you least expect them.
And if nothing else, some time away from the screen or a good night's sleep will let you look at your code with fresh eyes. The bugs will still be waiting for you!