В принципе написание кода без багов очень сложная задача. Лично мне кажется, что невыполнимая. Даже при большом опыте держать в голове всё возможное поведение и предугадать все сценарии работы кода почти невозможно.
Но надо отметить, что чем больше опыта, тем больше вы знаете узких мест, которые нуждаются в обработке.
Если привести базовые рекомендации, то я бы рекомендовал изначально просчитывать следующие моменты:
Определите граничные случаи. Например, если вы программируете фильтр по ценам в интернет магазине, то обработайте вначале невозможные сценарии: отрицательные значения и ноль. Если вам нужно оперировать только целыми числами, то обрабатывайте поступление отрицательных. Сделав это в самом начале вы не только оптимизируете код, но и не забудете этого сделать потом.
Чем проще код, тем проще его отлаживать. Старайтесь уходить от неоправданной сложности. Вместо огромного количества вложенных циклов лучше создать пару отдельных функций. Это поможет быстрее понять как меняются данные.
Правильное именование перемененных/функций/классов/методов — залог хорошего кода. Он просто читается, а следовательно в нём проще найти логическую ошибку или просто не допустить его. Ведь понять код a = b->c намного сложнее, чем cars = animals->cats. Найти логическую ошибку во втором варианте возможно даже не программисту.
Разделяйте код. Нет необходимости делать большую мешанину из различного кода, который не связан между собой. Можно вынести многие функции отдельно и во-первых, переиспользовать их, а во-вторых дать им понятные имена.
Пишите тесты. Не полагайтесь только на свой опыт. Весь код можно покрыть тестами, которые будут постоянно проверять, что нигде не нарушилась логика. Так вы сможете больше контролировать свой код и не допустите выкладывание продукта, в котором логика нарушена.
Больше тестов - меньше багов.