Programmers often spent a  significant amount of time writing the initial code. Because of this,  they usually focus on that work as being the most significant part of  the process. But on large projects, especially as they grow, most of our  time gets sunk into fixing or extending what is already there. Because  of this, good coding habits that make that second half of the process  far easier are crucial to keeping up the development momentum. 
Consistency,  self-discipline, refactoring and low technical debt are important ways  to avoid getting caught in a lot of unnecessary labour, but ultimately  the best thing is to leverage the technological tools to their maximum  in helping to validate the code. Elegant code is always easier to debug  than a mass of spaghetti or some eclectic arrangement. Ultimately, once  you know the rogue behavior and get to the right location in the source,  the solution to the problem should be obvious.
There are six  different levels of certainty when dealing with the validation of any  code:
1. The compiler can  validate the code.
2. It can be validated by looking at a line or two
3. It can be validated  by looking at the whole file.
4. It can be validated by cross-referencing a  bunch of files.
5. It requires a debugger to validate it.
6. It cannot be  validated, and running in a environment only decreases the odds that it  is correct.
Obviously the first  level is best. There has been lots of work done in modern languages to  improve the number of types of things that can be entirely automated for  validation. Precise syntax errors and warnings about sloppy practices  greatly help. Still, most of the code we write doesn’t fit into this  category, computers are only as intelligent as we make them and most of  the language mechanics have been generalized to suit the widest possible  audience.
Encapsulation and  abstraction are huge techniques for getting complex systems finished.  The next two levels really mean that this has been done well, or at  least reasonably well. If there is a bug, and you can go straight to a  line of code and see that it is incorrect on inspection, then besides  being a great help it is also a sign of elegance. If you have to root  around in the same file, that is OK, but a somewhat lesser  accomplishment. If you have to jump all over, that is a sign of  spaghetti. Of course, with complex code in order to determine the  problems by simple inspection, the programmer must understand both the  code and the abstraction used to encapsulate that code. A junior  programmer might not be so experienced, or familiar with the underlying  techniques, but this is a lack of skill, rather than an issue with the  source. 
If you have to bounce  around the files, depending on how distributed the data and logic are,  the time increases significantly. Besides time, the problem also becomes  trying to keep all of the little pieces in your head. Enough detail,  and enough files and it becomes impossible to know if the code is  correct without guessing. There is huge difference between thinking that  the code will work, and knowing it for sure. Proper use of  encapsulation should save people from this fate.
Modern debuggers are a  great tool, and can often help a programmer find tricky issues, but  they are in no way a substitute for a well-written program. They provide  a snapshot into the state of the code at one specific time, but that  can be misleading. Just because the code is running for one set of  variables, doesn’t mean it will effectively cover all of the corner  cases. Debuggers are extremely useful tools, but in no way a means for  fully understanding the code or insuring that it won’t break easily  while running. Reliance on them tends toward fragile implementations.
The final level is  probably the one that we rely on the most. Run it, and hope that it  works. Of course it is for primarily this reason that modern software is  so flaky. We rely way too much on this undependable technique, when we  could rely more heavily on the top three. 
Well-written code is  both a pleasure to read and to extend. If more of our works were written  better, we’d have way less code, which in turn would have far less  bugs, security vulnerabilities, weird functions, confusion and a whole  lot of other negatives. We’ve known for a long time how to build better  systems, it is just we ignored it frequently.
