Условный пример: bad_looking_code.ex. Код, выглядящий так даже издалека, всегда плох. В нем:
- смешаны разные уровни абстракции;
- копипаста;
- перегрузка обязанностей;
- непрозрачное переопредееление переменных;
- ...
В таком коде всегда будут плодиться ошибки.
Возврат {:ok, ok_result}
и {:error, error}
-- это стандарт.
Положительный и отрицательный результаты должны легко матчиться для возможности
просто использовать эмуляцию монады Maybe
или Either
через with
.
Примеры: error_return.ex.
Не стоит использовать Exceptions для контроля бизнес логики. Это усложняет тип функций, лишает возможности его описать.
Exceptions -- это возможность нормально сообщить, почему код все бросает и падает.
Два фетчера:
В первом изпользуемый HTTP клиент захардкожен, во втором передается.
При тестировании первого приходится тестировать внутреннюю реализацию модуля, а не его публичный интерфейс, что сильно снижает ценность теста, т.к. он ломается при изменении внутренней реализации, а должен, наоборот, помогать рефакторить ее, оставаясь зеленым при корректном рефакторинге и сохранении интерфейса.
Тест второго модуля специфицирует реальный контракт с модулем.
При работе с таймаутами лучше не использовать таймеры. Это сложный внешний state, за которым приходится следить и который трудно тестировать.
Для некритичных к идеальной точности таймеров удобно использовать tick
, посылку
текущего времени самому себе с указанным интервалом:
rotating_file_writer.ex.
Тогда state таймеров превращается в обыкновенный конечный автомат, который легко тестировать извне.
Пример: smpp_timers.ex --
сложная композиция из четырех таймеров SMPP. В oserl
реализована с багами через
установку и отмену таймеров.
- Антипаттерн: синхронные операции в "общем"
GenServer
: good_fetcher.ex. При глобальном использовании представляет из себя бутылочное горлышко. - Антипаттерн: возможная неявная синхронизация во внешней либе (
HTTPoison
) даже при наличии пула. Бутылочное горлышко может быть спрятано и во внешней либе.
- chain_flow_procs.ex -- Плохо. Event кидается от процесса к процессу. Неконтролируемая нагрузка, низкая/неконтролируемая параллелность, неконтролируемая емкость системы.
- chain_flow_funs.ex -- Не так плохо, но тоже плохо. Event кидается из функции в функцию, сложно отслеживать и расширять поток.
- direct_flow.ex -- Хорошо. Свой поток выполнения для каждого Event'а, неважно, что из себя представляют обработчики, можно легко измерить статистику по каждой стадии обработки, легко управлять стадиями.