Я бы сказал, что это зависит от характера вашей проблемы. Различные проблемные области могут требовать почти произвольных сообщений об ошибках, в то время как другие тривиальные задачи могут просто возвращать NULL или -1 в случае ошибки.
Проблема с кодами возврата ошибок заключается в том, что вы загрязняете / маскируете ошибку, поскольку ее можно игнорировать (иногда без того, чтобы клиент API не знал, что он должен проверять код ошибки). Это дает (разумно) действительный вывод из метода под рукой.
Представьте, что у вас есть API, где вы запрашиваете индексный ключ для некоторой карты, сохраняете его в списке и затем продолжаете работать. Затем API в более поздний момент отправляет обратный вызов, и этот метод может затем пройти таблицу, используя ключ, который может быть -1 в этом примере (код ошибки). BOOM, приложение аварийно завершает работу, когда вы индексируете значение -1 в некотором массиве, и такие проблемы могут быть очень трудными. Это все еще тривиальный пример, но он иллюстрирует проблему с кодами ошибок.
С другой стороны, коды ошибок быстрее, чем выдача исключений, и вы можете использовать их для часто используемых вызовов методов - если уместно возвращать такой код ошибки. Я бы сказал, что попытка инкапсулировать эти типы кодов ошибок в частную сборку была бы вполне приемлемой, поскольку вы не предоставляете эти коды ошибок клиенту API. Всегда не забывайте строго документировать эти методы, так как такие типы ядерных приложений могут храниться в приложении в течение длительного времени, так как они были запущены до его запуска.
Лично я предпочитаю сочетание их обоих в некоторой степени. Я использую исключения только для этого - исключения - когда программа переходит в состояние, которое не ожидалось и должно сообщить, что что-то вышло из-под плана. Я не занимаюсь написанием блоков try / catch по всему коду, но все зависит от личных предпочтений.