Постоянно возникают непредвиденные баги и слёты функционала, как бороться?
1. Возможно у Вас какая то кривая общая архитектура. 
Почитайте про SOLID.
2. В целом - если Вы вносите изменения в какую то функцию - простейший поиск по файлам в текстовом редакторе говорит Вам где эта функция используется.
3. Сложная бизнес-логика отличная от "получили запрос, сходили в базу, отдали данные в красивой обертке" должна быть обязательно документирована в wiki + в коде.
Вопрос "параметр должен учитываться в многих местах, мы не помним где" - это вопрос грамотно написанного ТЗ кстати. Предполагается что ТЗ пишет человек, который имеет общее понимание проекта.
4. Именно юнит тесты на мой взгляд нужны в 1% случаев. Использование моков не дает реальной пользы на живом проекте, поскольку причина краша может быть где угодно.
5. Мы используем codeception.com Acceptance testing в сочетании с определенным дампом базы который загружается перед тестами. 
На дамп базы накатываются все миграции (используем phinx.org ) и далее проверяется все, вплоть до вывода конкретных значений контента.
Честно предупреждаю, что время на разработку тестов превышает время на внедрение функционала примерно в 1.5-2 раза.
>> Т.е если их начать внедрять, то все эти вышеперечисленные проблемы отпадут - честно, эти проблемы не отпадут, а останутся. Другое дело что вы своевременно сможете увидеть регрессию и починить ее еще до того как продукт попадет к заказчику (в идеале до того как попадет в отдел QA =) )
>> Знаю сейчас часто стали использовать юнит-тесты, автотесты, но насколько они оправданы? - оправданы. и еще как. Юнит тесты оправданы для программистов, чтобы быть уверенным что то что они написали (классы, методы, и т.д. и т.п.) не отвалиться после введения чего-то нового, или рефакторинга. Следующий уровень это "автотесты", совсем другой уровень автоматизации, который затрагивает несколько модулей одновременно, тоже хорошо позволяет находить проблемы, регрессии во время и решать их соответственно.