Developers who debug inherited codebases do a lot of what I've been doing lately, which is tracing. It's time-consuming, but like most brute-force methods, it's simple and works eventually. You:
- look at what errors you're getting
- try to figure out which function they're happening in
- go to that function
- insert a bunch of log printing functions around where you think the error is happening
- run the code again and see which logs actually get printed to the console
Unless you have asynchronous stuff happening, the last thing that gets printed to the console is directly before where the error happens. If the next line is another function, go to that function and repeat the process.
(Pro tip: if you are debugging something this way, log the name of the function you're in. You will probably forget to remove at least one of the logs when you're done, and knowing which function is printing a bajillion messages to your production console will be useful when you need to find and remove it.)
Tracing works for most debugging scenarios, but web developers also need to be aware of how the server gets information from its database (the backend) and passes it to a user's web browser (the frontend). Otherwise, you won't even know where to look-- are your errors and logs printing to your browser's dev tools console, or your server terminal?
In my company's case, the codebase's layout makes it clear which files:
- On the browser side:
- Send HTTP requests for data to the server
- Parse the data from the server
- Create views for the user using the parsed data
- On the server side:
- Receive HTTP requests for data from the browser and route them to the right data retrieving functions
- Retrieve data from the database
- Structure the data and send it back to the browser
A couple of well-established and stable Node.js libraries handle our MySQL database calls (mysql), HTTP requests (request), and server routing (express). We also use larger, newer libraries to create our user views (React) and to structure and parse our data (Redux).
These popular libraries also throw useful errors that help with debugging. For example, their error messages might indicate which files and lines an error is happening in, and I can follow those errors up from a library's files to our actual codebase's files.
This might look obvious to experienced fullstack developers, but I started as a primarily frontend dev and had to figure out a lot of this stuff on the fly, so I wanted to write it down for people who are still learning.