I have a confession: I'm addicted to Wordle. Especially now that it's out of style and people don't post about it. I love that it's short, I can solve one word and then it's gone. I don't feel too bad about the addiction and wasting my time with a game. This cloud debugger tutorial is an enormous challenge for me since the target is a Wordle game. But I'm getting ahead of myself.
As part of the Lightrun Playground we recently released we needed a demo application that will let developers who are new to Lightrun, practice in a "safe environment".We decided to pick Wordle as our demo application because it's instantly familiar, visual and not too interactive. A Flappy Bird demo might have been painful to debug. At this point our key challenge was in creating a bug where the debugging process would be interesting enough and yet subtle enough so it won't be instantly obvious.
Creating a bug like that is surprisingly challenging. We don't want an overly complex application spanning multiple files. That might make the debugging process too difficult. On the other hand, the bug needs to be subtle enough that we won't notice it even if we stare directly at it. Here is the bug:
constguess = []
for (leti = 0; i < game.word.length; ++i) {
if (game.word.includes(guessWord[i])) {
guess.push({ letter:guessWord[i], check:CHECK_TYPES.LETTER_INCLUDED })
} else if (guessWord[i] === game.word[i]) {
guess.push({ letter:guessWord[i], check:CHECK_TYPES.LETTER_MATCHED })
} else {
guess.push({ letter:guessWord[i], check:CHECK_TYPES.LETTER_NOT_INCLUDED })
}
}
Can you spot the problem?
To understand it, we need to first understand the symptom of the bug we chose. When I talk about bugs people's minds often go to crashes. That can be the case sometimes, but in my experience the most frequent bugs are logic mistakes that occur because the production environment differs in some subtle way from our testing environment. Because of that, we picked a logic bug, unfortunately because of the simplicity constraint I doubt a bug like that would have made it to production. The core lesson still applies.
The bug in this case is that letters in Wordle that should be colored in green, because they're in the right position in the word, are colored in yellow. This logic is implemented by the code we see above. As you can see, we have three modes:
-
CHECK_TYPES.LETTER_INCLUDED
- indicates that a letter should be colored in yellow -
CHECK_TYPES.LETTER_MATCHED
- indicates that the letter should be colored in green -
CHECK_TYPES.LETTER_NOT_INCLUDED
- indicates that the letter is missing and should be gray
Can you spot the problem now?
Don't scroll down to avoid spoilers....
Here's working code:
constguess = []
for (leti = 0; i < game.word.length; ++i) {
if (game.word.includes(guessWord[i])) {
guess.push({ letter:guessWord[i], check:CHECK_TYPES.LETTER_MATCHED })
} else if (guessWord[i] === game.word[i]) {
guess.push({ letter:guessWord[i], check:CHECK_TYPES.LETTER_INCLUDED })
} else {
guess.push({ letter:guessWord[i], check:CHECK_TYPES.LETTER_NOT_INCLUDED })
}
}
The difference is that I swapped two lines of code. We need CHECK_TYPES.LETTER_MATCHED to be tested before the CHECK_TYPES.LETTER_INCLUDED test. The tests must be in-order of significance and green (perfect match) should precede the yellow (partial match).
The process of debugging this is relatively simple. We placed a snapshot/breakpoint on the following line where we saw the values were incorrect at the server code level. I think a more "natural" way to debug this would have been to place a breakpoint on the CHECK_TYPES.LETTER_MATCHED line and once we realize that this was never hit we would have understood what went wrong. For our particular use case of a playground that wasn't the right approach. We wanted people to see the snapshot (non-breaking breakpoint) getting hit. But other than that, it's a good bug.
If this still isn't clear, we have this cool animation in the playground that explains the bug visually:
Teaching Debugging
Debugging is one of those subjects that we don't learn at University. Yes, there are courses that cover it but not much. You're mostly expected to pick it up on your own (for example, using a dedicated live debugging tool). This shows to a large part why that's the case. It's very hard to create exercises for debugging and even harder to test knowledge.
We could create a more elaborate demo to debug but there we transition to the world of "understanding and explaining that code base". This isn't the goal. I've gone over a lot of debugging related materials over the past couple of years and this seems to be a universal problem that all of us are struggling with. This is a shame since there are so many tools, techniques and approaches that even experienced developers are missing out on.
In that sense I’m a proponent of teaching debugging without a bug. Debuggers are tools we can explore and use before we have any bug, even as a learning tool. I think we need to be “comfortable” within the debugging environment and should leverage it when there are no bugs. It shouldn’t be a tool we only reach for in the case of an emergency. If we work with a debugger on a regular basis, it will be much easier to track bugs with it when we actually need it.
This is a philosophy I hold for tools such as observability tools, logs, etc. Muscles that we don’t flex on a regular basis lose their mass and weaken. Synthetic problems are OK for a short tutorial but we can’t use them daily and it’s hard to scale them to a full-blown workshop or course.
Finally
How do you feel about the way you learned debugging? Was it in college, university, bootcamp or on-the-job?
Do you feel you know the subject well?
Do you teach debugging to others? If so, how and what techniques do you use? What do you find works best?
I’d love to hear from you on Twitter @debugagent (my dms are open), LinkedIn or comments or any other channel. Private or public.
As a reminder, our Playground is open for business - feel free to roam around, test our your debugging skills and report back!