deep-dives

19 posts

Ваша архитектурная диаграмма уже ложь

Архитектурная документация гниёт в тот момент, когда вы её сохраняете. Вот как поддерживать её в актуальном состоянии с помощью диаграмм, генерируемых из кода, ADR и автоматизированных архитектурных тестов.

Каждая архитектурная диаграмма, которую я видел в вики, была неверной. Не катастрофически неверной. Просто тихо, постепенно неверной. Сервис с пометкой «Auth»…

Ваш цикл повторов предполагает, что первый запрос не удался. Скорее всего, это не так.

Таймаут или сбой не означают, что ваш API-запрос был потерян. Вот как ключи идемпотентности делают повторы безопасными, и как паттерн хранения на самом деле предотвращает дублирование.

Ваш сервис падает на полпути при обработке запроса . Клиент видит таймаут и повторяет запрос. Теперь у вас два списания. Клиент злится. База данных…

Блокировка, которая переживает ваш процесс: как на самом деле работают distributed lease

In-memory mutex исчезает при перезапуске сервера. Вот как distributed lease с fencing token и TTL предотвращают дублирование работы при сбоях, и где они всё ещё ломаются.

Ваш не переживает . Он не переживает OOM, deployment rollout и перезагрузку узла. В тот момент, когда процесс завершается, lock пропадает. Если этот lock…

Circuit breaker без goroutine, таймеров и фоновой нагрузки

Большинство библиотек circuit breaker порождают фоновые потоки для проверки восстановления. Они вам не нужны. Вот request-driven дизайн, который устраняет всю фоновую нагрузку без потери корректности.

Каждый production circuit breaker, который я рассматривал, рано или поздно порождает фоновый поток. Это может быть Go goroutine, Java или Rust tokio task.…

У вашего веб-сервиса есть путь корректного завершения. Это и есть баг.

Crash-only софт обрабатывает каждый сбой как крах, а каждый запуск — как восстановление. Для веб-сервисов это означает удаление логики завершения и проектирование состояния, которое переживёт kill -9.

У вашего веб-сервиса есть обработчик завершения. Он сбрасывает буферы, закрывает соединения, записывает чекпоинты. Вы тестировали его раз, может быть. В…

Как убить выжившего мутанта, когда вы не понимаете, что он изменил

Мутационное тестирование нашло выжившего, и вы понятия не имеете, что делает мутация. Вот пошаговый метод написания правильного теста без предварительного понимания мутанта.

Ваш отчёт о мутационном тестировании полон выживших, и хотя бы один из них для вас бессмысленен. Инструмент говорит, что он заменил на в строке 47, или…

Код аутентификации нуждается в 90% покрытии мутациями. Ваши строковые утилиты — нет.

Почему принудительное единое покрытие мутациями для всей кодовой базы — ошибка, и как установить пороговые значения для каждого модуля в соответствии с реальным риском.

Принудительное единое покрытие мутациями для всей кодовой базы — отличный способ заставить вашу команду ненавидеть тестирование. Запустите PIT или Stryker на…

Ваши тесты проходят. Mutation score — 40%. Вот что на самом деле говорят выжившие мутанты.

Code coverage говорит, что всё в порядке. Mutation testing говорит, что ваши тесты — в основном декорация. Вот как выжившие мутанты выявляют этот разрыв и как его устранить.

Ваши тесты проходят. Ваш отчёт о coverage показывает 87%. Но ваш mutation score — 40%, и половина ваших мутантов всё ещё жива. Эти 40% не означают, что ваш код…

Mutation testing в Rust работает, но ваше время компиляции этого не простит

cargo-mutants находит тесты, которые только притворяются, что проверяют ваш код. Вот как работает mutation testing в Rust, что он ловит и стоит ли затрат времени компиляции.

У вас 100% покрытие строк. Каждая ветвь задействована. Каждая функция вызвана. Затем кто-то меняет на в вашей логике ценообразования, запускает тесты, и все…

Мутационное тестирование занимает 4 часа. Как команды реально используют его в CI?

Большинство команд не запускают полные наборы мутационного тестирования на каждый коммит. Вот как инженерные команды реально интегрируют его в CI, не ломая пайплайн сборки.

Если ваш набор мутационного тестирования занимает четыре часа, поздравляем. Вы доказали то, что все и так подозревали: в вашем тестовом наборе есть пробелы. Вы…

Ваши unit tests проходят, но данные всё равно исчезают

Mock-тесты базы данных проверяют синтаксис SQL, но не то, переживут ли строки сбои, конкурентные записи или несоответствие схемы. Вот как тестировать persistence по-настоящему.

Если вы используете mock для базы данных в тестах, вы проверяете то, что ваш repository layer вызывает правильные методы. Вы не проверяете то, что данные…

Тестирование Redux без утопания в мок-экшенах

Мокирование каждого экшена Redux превращает ваши тесты в валидатор ченджлога. Вот как тестировать стор с реальными переходами состояния.

Если вы когда-либо писали тест, который проверяет, что был вызван с точной структурой payload, вы написали тест, который ломается каждый раз, когда кто-то…

100 прогонов — это ложь: как на самом деле определять размер property-based тестов

Стандартные 100 примеров в property-based testing — это социальный компромисс, а не статистическая стратегия. Вот как выбрать количество прогонов, соответствующее вашим потребностям в уверенности и бюджету CI.

Если вы запускаете property-based тесты со стандартными 100 примерами, вы получаете худшее из двух миров. Ваш CI медленнее, чем нужно, и вы всё равно не ловите…

Тесты на основе свойств в Rust находят баги, которые пропускают ваши юнит-тесты

Тестирование на примерах покрывает только те входные данные, о которых вы подумали. Тестирование на основе свойств генерирует случайные данные, проверяет инварианты и сокращает ошибки до минимальных контрпримеров.

Вы написали функцию . Вы протестировали её с и . Тест проходит. Вы выкатываете в продакшн. Пользователь передаёт срез из одного элемента. Ваша функция теряет…

Ваши unit tests проходят. Ваш production-код всё равно сломан.

Метрики coverage создают ложное чувство безопасности. Вот почему unit tests пропускают баги, которые реально лишают вас сна, и что тестировать вместо этого.

У вас 90% coverage, и вас всё равно разбудили в два часа ночи. Unit tests прошли. CI был зелёным. Баг всё равно попал в production. Coverage не соврал, но и…

Runtime contracts в Rust могут быть бесплатными в релизных сборках, но компилятор не сделает это за вас

Rust автоматически удаляет debug assertions, но настоящий design-by-contract требует большего, чем debug_assert!. Вот как построить zero-cost runtime contracts, которые исчезают из вашего релизного бинарника.

Rust может enforce runtime contracts в development и полностью стирать их из релизных сборок. Оговорка в том, что язык не рассматривает contracts как…

Ноль, один или двенадцать: сколько assertions нужно production-функции на самом деле

Разработчики либо разбрасывают assertions как конфетти, либо избегают их полностью. Вот фреймворк принятия решений, который отделяет полезные invariants от причин падения production.

Большинство production codebase делятся на два лагеря. Лагерь А относится к как к декоративной приправе, посыпая им каждую вторую строку, пока функция не…

Ваш слой валидации больше самой бизнес-логики

Ручная валидация раздувает codebase и всё равно пропускает крайние случаи. Вот как обеспечить runtime contracts с помощью декларативных схем, которые не мешают.

Каждый раз, когда ваш API получает запрос, вы его валидируете. Каждый раз, когда функция получает аргумент из внешней системы, вы его проверяете. Делайте это…

strictNullChecks — это compile-time защита, а не runtime щит

strict mode отлавливает null, которые вы пишете сами, а не те, что приходят на runtime из API, DOM-запросов и JSON.parse. Вот где заканчивается type system и начинается ваша оборона.

Вы включили в . Исправили каждую красную волнистую линию. Отправили в продакшен, будучи уверенными, что и — решённые проблемы. Затем ответ бэкенда изменил…