Встречный вопрос: А нужно ли? А что готовы за это дать?
- Составить спецификацию программы, которая покрывает в том числе и граничные случаи с неправильным вводом.
- Написать тесты, в соответствие с этой спецификацией
- Написать тесты на тесты (использовать мутационное тестирование, чтобы контролировать покрытие тестов)
- Написать код, который все тесты пройдёт. (в идеале - писать на каком-нибудь языке, который можно формально верифицировать. На Coq или F*, например)
- Понять, что ещё важна производительность, отсутствие проблем с памятью, и что существуют баги с многопоточностью.
- Добавить performance тесты, тесты на утечки памяти, и тесты на многопоточные баги (которые прогоняются миллионы раз, чтобы вызвать race condition)
- Повторить шаг 3.
В теории, такой подход даст в результате программу без багов (по крайней мере полное соответствие спецификации). Но за x100 к стоимости.
Добиться почти полного избавления от багов можно просто достаточно детальным тестированием и проработкой спецификации.
Ну и чем раньше начнёшь тестировать - тем дешевле будет фиксить баги.