Противоречие между тем, что ASSERT надо будет убрать в рабочей системе (т.к. он обваливает систему), но в то же время сохранить возможность обнаружения ошибок.

Я бы ввёл "другой ASSERT", поведение которого можем менять.

Пусть выполняет при ошибке какие-то диагностические действия. Они могут быть разными на этапе разработки и на этапе выполнения.

Например, на этапе выполнения может докладываться о событии какому-то модулю, отвечающему за диагностику ошибок. Потом где-то на верхнем уровне после вызова система выполняет самоанализ, всё ли происходило успешно. Делать коды ошибок для ошибок программы (нарушения предусловий) не есть хорошо, запутаетесь быстро.