Debugging is hard. Working on a full-stack project makes it even harder.
If you are investigating a bug in your React single page application which sends data to a serverless backend on AWS, saves data to DB and then refreshes the view, there are a lot of moving parts, lots of different components in your architecture. Even reproducing the bug and determining where the bug lies can be a daunting task.
The mantra that I follow when coding (be it implementing new features, or fixing bugs) and that I find myself repeating most of the time when I help junior ( or less junior ) devs is simply this:
Reduce the scope and speed up the feedback loop
What does it mean?
Reduce the scope
- Split the problem into smaller problems
- Focus on individual tiny bits
- Act on the smallest piece of code possible, and directly on it.
Of course you need to spend some time to reproduce and investigate the weird behavior of your application in its entirety. This is difficult but also very interesting — is´s kind of an investigative task.
But once you found what piece of code is responsible — or even if you just have some suspects, just focus on that part. Forget about the rest.
What you need to speed up your productivity is reducing the scope.
Remove the clutter.
Shut down the local server of your react application, forget double-checking the tables on DBeaver. Don’t’ even think of checking the logs in AWS console anymore.
You know — or you should define — what you expect as input and what you want as output. Write a Unit Test to prove it. All the rest is not important anymore.
Save time, clicks and sanity, focusing only the code that is responsible for the bug.
Speed up the feedback loop
Simply put the feedback loop is the cycle that lets you understand if what you just changed works.
- Reproduce the bug
- Change some code
- Check if the bug is still there
- Change some code again
- Iterate until bug is gone
While doing this you don’t want to spend most of the time, waiting for the code to compile/hot-reload, you definitely don’t’ want to waste time clicking around in your application, filling forms with fake data ( and creating even more rubbish in your backend), you don’t want to get bored waiting for the server responding.
The shorter and faster is your feedback loop, the more productive you become. And the more you learn/become proficient at coding — because you spend more time writing code rather than waiting for the feedback loop or trying to reproduce all the steps the bring to a bug.
Don’t keep on reloading the app, going to the broken form, fill it, wait for the response from the backend and then read all the logs everywhere…
As I said You know — or you should define — what you expect as input and what you want as output.
Write a unit test if possible, or an integration test if necessary ( if you really need the interaction with the DB for example)
Just have your IDE open, run the code in debug mode, set breakpoints and check what is going on.
(Please, don´t rely on scattering console.logs everywhere!)
Make some changes and run the test again.
Sometimes it does not even have to be a unit test, a simple js file invoking your method, that you execute directly with node ( always with the debugger on and breakpoints set) is often enough.
“But..”, you might say, “the data is coming to the lambda from the frontend and if I don´t fill the form every time I don´t know the incoming data..”
Well. Then do that the very last time manually, check in the network console what is being sent, or in the Lambda logs what is being received (the gateway API always wraps the data with some other stuff), save that data into a file or hardcode it in your test and then just feed it directly to the method which is behaving unexpectedly.
So to recap:
- Strive to reduce the scope of your problem,
- Focus on smaller parts
- Find every shortcut allowing you to reproduce quickly your bug and test immediately your changes
- Tighten the feedback loop