I can't claim to know the best way of debugging, but here is how I approach developing code.
Mathematica (WL) uses a functional paradigm. Generally, I try to keep things readable and testable by keeping functions simple, and calling functions from functions to build up complexity.
However, there are times when that doesn't work - particularly when I need a complex function to map onto a list. A good example is a function to process data in a file, when I want to process many files, or a new file at intervals. In this case, I start out developing the function as a section of a notebook that performs the import and analysis on a single file using a sequence of separate cells. I will take a sample file name and assign it to a variable called "file." Then in the next cell I'll Import "file" assigning the import to "raw." Then in successive cells I'll format the data as needed, do any required analyses, export data as needed, make plots and export them as needed, or just format the data into an expression I want as the output of the function. I can see the results and correct any errors as I go along. When I'm happy, I'll usually save the notebook as a development book. But then I will delete all output from the sequence of cells and terminate each with a semicolon. I place a cell with the opening for Module at the top, including the list of internal variables as used in the cells below. I use "file_" as the argument since I used "file" as the file name. If necessary, I add a cell at the end with the output I want from the function, and merge the cells into what is now a single cell which defines the function using the Module. In writing the initial cells, I use (* *) comments for documentation rather than formatted cells, anticipating that they need to be within a single cell when finished.
I find this generally produces functions which work. If I later get an error, perhaps due to unanticipated input, I try first to troubleshoot with embedded print statements or an altered final expression. (Like we did oh so many years ago.) If that fails, I load the development notebook with the inline cells version of the function and feed that the input that is causing the problem.