You will create bugs no matter what you do. No amount of testing, type-safety, tooling, or anything else can prevent bugs. There are just too many edge cases in most software for your brain to handle.
Accept that there will be bugs. Focus on having systems and processes in place that notify you immediately when things go wrong and that make it easy to debug issues. For example:
- Have good error reporting tools (e.g., Sentry) that tell you exactly when and where an exception occurred and the stack trace leading to it.
- Knowing your debugger inside out so you can step through the stack trace to quickly diagnose the root of the issue. Everytime you’re tempted to use
debuggerinstead. Use hotkeys to quickly step over, pause, and resume execution through various execution paths.
- Make it easy to replicate the issue (e.g., replicate the production environment, have access to the production database, etc).
- Use assertions liberally to throw exceptions when unexpected things happen. Throw errors early so that when things go wrong, you find the problem closer to the source.
Focus on minimizing bugs in the most critical parts of the software. Bugs are part of life, but let’s minimise their severity.