Инженерные стандарты
Кейс показывает результат. Правила показывают, повторится ли он на вашем проекте. Ниже — то, как мы работаем на самом деле; каждый пункт подкреплён примером из собственной работы, а не формулировкой из чужого регламента.
Решения
Большая часть провалов в разработке — это не плохой код, а уверенное решение, принятое без замера.
Сначала измеряем, потом чиним
Догадка о причине тормозов почти всегда неверна. Прежде чем оптимизировать, мы воспроизводим проблему и получаем число, а после правки — второе число.
Первый экран нашего сайта грузился 6,4 секунды. Очевидная причина — тяжёлый 3D-фон — оказалась второстепенной: виновата была заставка, которую сервер рисовал всем ещё до того, как узнавал, кто пришёл. Исправление дало 2,2 секунды. Разбор с методикой замера лежит в блоге.
Архитектура до кода
Решения о структуре, данных и границах системы принимаются до первой строки. Переписать документ дешевле, чем переписать сервис.
Сомневаешься — проверь на слабом железе
Разработка идёт на быстрых машинах, а пользуются продуктом на разных. Мы проверяем на медленном процессоре и на плохой сети, а не только у себя.
Жалобу «лагает на слабых ноутбуках» мы воспроизвели программным рендерером: кадр сцены занимал 175 мс против 17 мс без неё. После правки первый экран стал выдавать 104 кадра в секунду вместо пяти.
Код
Комментарий объясняет «почему», а не «что»
Что делает строка — видно из строки. В комментарии идёт причина: почему сделано так, что было испробовано до этого и что сломается, если переписать «очевидным» способом.
В нашем коде рядом с директивой nginx стоит объяснение, что `add_header` внутри location отменяет все заголовки, унаследованные с уровня server. Без этой строки следующий человек уберёт «дублирующиеся» заголовки и молча снимет защиту с картинок.
Зависимость должна окупаться
Каждая библиотека — это чужой код в вашем продукте, чужие уязвимости и чужие обновления. Ради одной функции мы её не берём.
Инструмент для обработки изображений ставится на время пережатия и удаляется из зависимостей сразу после. Почтовая служба, которая принимает письма и отвечает на них, живёт на одной зависимости.
Код читают чаще, чем пишут
Мы пишем так, чтобы через год в проекте разобрался человек, который его не писал, — включая вашего будущего разработчика. Это не вежливость, а стоимость владения.
Проверки
Ошибку дешевле всего поймать до выкатки и дороже всего — от пользователя.
Каждый коммит проходит четыре проверки
Типы, линтер, сборка и контроль контента — до того, как изменение попадёт на сервер. Не прошло — не выкатывается.
На каждый пойманный баг — регрессия
Исправить ошибку недостаточно: нужно, чтобы она не вернулась. Каждая серьёзная поломка превращается в проверку, которая гоняется перед выкаткой.
После того как выяснилось, что без поддержки WebGL сайт падал в пустой экран целиком, появился скрипт, проверяющий три сценария отказа: обычный браузер, браузер без 3D-контекста и браузер с заблокированным WebGL.
Контент проверяется как код
Битая внутренняя ссылка, статья без адреса, повторяющийся якорь или слишком длинное описание для поиска роняют сборку так же, как ошибка типов.
Плавающий тест — это баг, а не погода
«Перезапустите ещё раз» — не ответ. Если тест падает раз в триста прогонов, у него есть причина: конкретный порядок выполнения, который в тот раз выбрала операционная система.
Мы написали для этого открытый инструмент — unflake. Он отбирает расписание у операционной системы: время, таймеры и порядок готовых колбэков берутся из сида, поэтому падение воспроизводится побайтово на любой машине, а найденный баг сжимается до минимального расписания. TypeScript, MIT, ноль зависимостей, опубликован в npm.
Зелёный прогон — не доказательство
Набор тестов, который всегда проходит, может ловить ошибки, а может не смотреть вообще — по цвету это неразличимо. Значит, проверку нужно уличить: показать, что она падает там, где обязана падать.
Так держатся два наших открытых репозитория. В bulwark — реализации алгоритма консенсуса Raft — есть музей багов: семь экспонатов, каждый выключает ровно одно правило алгоритма, и харнесс обязан поймать его с сидом и именем нарушенного свойства. В adya — движке изоляции транзакций — каждый уровень проверяется в обе стороны: он не производит того, что обязан предотвращать, и производит то, что предотвращает следующий. Без второй половины первая пуста: движок, отклоняющий все транзакции, был бы «чист» на всех уровнях, а проверка, ничего не находящая, с ним бы согласилась. Дальше всех этот же приём доведён в pnueli: сломанное сокращение перебора отвечает ровно то же, что и рабочее — «нарушений не найдено», — поэтому рядом оставлен полный перебор, медленный и неспособный ошибиться, и вердикты обязаны совпадать.
Выкатка
Сборка не мешает работе
Новая версия собирается в отдельном каталоге и подменяет работающую двумя переименованиями. Пользователь не видит полусобранный сайт.
Раньше сборка шла поверх работающей версии, и все сорок секунд сайт отдавал ошибки. Теперь подмена занимает миллисекунды.
После выкатки — проверка отклика
Сборка может пройти, а приложение упасть на старте: битая переменная окружения, ошибка в серверном коде. Мы ждём ответа тридцать секунд.
Откат автоматический
Если новая версия не ответила, возвращается предыдущая — без участия человека и без звонка среди ночи.
История изменений
В журнале написано «почему», а не «что»
«Улучшена стабильность» — это не запись, а её отсутствие. Мы пишем, какая проблема была, чем она проявлялась и что изменилось; спорные решения объясняем отдельно.
Версия говорит о характере изменения
Исправление, новая возможность или несовместимое изменение различаются номером версии, а не настроением.
Отдельно — что сделано намеренно не так
В журнале есть раздел «не сделано намеренно»: он объясняет, почему очевидная на первый взгляд правка была бы ошибкой. Это экономит следующему человеку недели.
Передача
Код и доступы — ваши
Исходники, инфраструктура и документация передаются заказчику. Мы не оставляем себе ничего, без чего продукт перестанет работать.
Конфигурация сервера лежит в репозитории
Не в голове администратора и не в переписке: копия боевых настроек хранится рядом с кодом, чтобы её можно было прочитать, отревьюить и восстановить при переезде.
Секреты не ходят в мессенджерах
Ключи и пароли передаются через хранилище или ваш контур, а на сервере лежат в файлах с закрытыми правами вне репозитория.
Чего мы не делаем
Правила без запретов ничего не значат: любой согласится с «пишем хороший код». Вот от чего мы отказываемся, даже когда так быстрее.
Не переписываем на новый фреймворк ради новизны
Смена технологии оправдана задачей, а не выходом версии. Каждая миграция — это месяцы, оплаченные заказчиком, и новые ошибки в том, что работало.
Не прячем плохие новости
О сорванном сроке и о найденной у себя ошибке сообщаем сразу. Разбор нашего собственного провала опубликован в блоге вместе с цифрами до и после.
Не берёмся, если не потянем
Отказ на созвоне дешевле для заказчика, чем срыв через три месяца. Иногда честный ответ — «эту систему выгоднее переписать, чем чинить».
Не оставляем «временных» решений без записи
Временное живёт дольше постоянного. Если сделано наспех — это записано в журнале с объяснением, что и когда должно быть переделано.
Проверить это можно на самом сайте
Всё описанное применяется к этому сайту в первую очередь: замеры, регрессии, откат при выкатке, журнал изменений. Разборы с числами лежат в блоге — их можно прочитать до того, как вы напишете нам.