Вероятно, единственная самая большая трудность с программным обеспечением - это просто количество взаимодействующих вещей, и наиболее полезный метод заключается в сокращении количества вещей, которые необходимо учитывать.
Например, использование языков более высокого уровня, а не низкоуровневого, повышает производительность, поскольку одна строка - это отдельная вещь, а возможность написать программу в несколько строк уменьшает количество вещей.
Процедурное программирование возникло как попытка уменьшить сложность, позволив рассматривать функцию как вещь. Однако, чтобы сделать это, мы должны иметь возможность думать о том, что функция делает согласованным образом, и с уверенностью, что мы правы. (Объектно-ориентированное программирование делает то же самое в более широком масштабе.)
Есть несколько способов сделать это. Проектирование по контракту - это способ точно определить, что делает функция. Использование параметров функции вместо глобальных переменных для вызова функции и получения результатов снижает сложность функции.
Модульное тестирование - это один из способов убедиться, что функция выполняет то, что должна. Обычно можно протестировать весь код функции, а иногда и все пути выполнения. Это способ определить, работает ли функция так, как должна, или нет. Если функция работает, мы можем думать о ней как об одной вещи, а не как о нескольких вещах, которые мы должны отслеживать.
Служит для других целей. Модульные тесты, как правило, выполняются быстро, поэтому могут быстро обнаруживать ошибки, когда их легко исправить. Если разработчики удостоверится, что функция прошла тесты перед тем, как их зарегистрировать, тогда тесты являются формой документирования того, что функция делает, и это гарантированно правильно. Акт создания тестов заставляет тестировщика задуматься о том, что должна делать функция. После этого, кто бы ни хотел изменения, он может посмотреть на тесты, чтобы понять, правильно ли он понял.
В отличие от этого, более масштабные тесты не являются исчерпывающими и поэтому могут легко пропустить множество ошибок. Они плохо в локализации ошибок. Обычно они выполняются с довольно большими интервалами, поэтому они могут обнаружить ошибку через некоторое время после ее появления. Они определяют части общего пользовательского опыта, но не дают оснований рассуждать о какой-либо части системы. Им не следует пренебрегать, но они не могут заменить юнит-тесты.