Часть III. Эксплуатация
Проверка, которая врёт
В первый вечер этой книги вы собрали форму, которая говорила «Спасибо» и выбрасывала обращение. Тогда это выглядело ошибкой первого вечера: такое ведь не выпускают наружу. Вся вторая часть учила вас проверять – руками, тестами, перезапуском – именно затем, чтобы подобное не выживало.
Пришло время рассказать, как та же самая поломка несколько месяцев прожила у нас, с двумя десятками сайтов и написанными проверками. Мы разберём этот случай подробно, по дням, с настоящими цифрами – по одной причине: чужие такие истории никто не показывает, а учиться на приглаженных примерах бесполезно.
Тема этой части книги – эксплуатация: всё, что происходит с проектом после того, как он заработал. И начинается она с самого коварного отказа из всех возможных. Ломается сама проверка. У сломанной проверки нет симптомов: она зелёная.
Диагноз: нет спроса
Двадцать первое июля. Повод для разбора звучал буднично: в сводке по заявкам с сайтов подозрительно пусто, надо пройти весь путь заявки и понять, что происходит.
Путь заявки (у нас на сайтах это заявка от возможного заказчика – то же, что обращение в вашем вечернем сервисе, только путь длиннее) – цепочка, и дальше мы будем называть её трактом, как называли в своих записях. Человек заполняет форму на сайте, запрос уходит на сервер приёма, запись ложится в базу, уведомление улетает в мессенджер, ночная синхронизация переносит запись в общую базу, оттуда её читает сводка. Разбор прошёл по этой цепочке звено за звеном и нашёл настоящие поломки.
Самая крупная: мост между двумя базами стоял мёртвым с апреля. Заявки исправно ложились в первую базу, а сводка читала вторую, куда с весны ничего не приезжало. Поэтому на экране были нули при настоящих записях в хранилище. Мост починили, нули сменились числами. Заодно нашлись дыры поменьше: на одной из форм не было защиты от мусорных отправок, а пара задач по расписанию была запущена без блокировок и могла накапливать зависшие процессы.
К вечеру разбор завершился выводом. Тракт исправен: тестовая заявка проходит от формы до уведомления, все звенья отвечают, сводка показывает правду. Числа по месяцам: май – 14 заявок, июнь – 4, июль – 0. В мае шла рекламная кампания, в июне она закончилась. Заключение напрашивалось само: система в порядке, обнулился входящий поток. Нет спроса.
Обратите внимание, как крепко стоит этот вывод. Он сделан после разбора, в котором нашлись и починились настоящие поломки – значит, смотрели внимательно. Он подтверждён сквозной тестовой заявкой. Он объясняется внешней причиной с датами: была кампания – были заявки, кончилась – кончились. Это вывод, с которым не стыдно идти к владельцу.
И он был неверным.
Через четыре дня
Двадцать пятое июля. Другая сессия, другой вопрос, заданный владельцем почти мимоходом: а формы вообще на всех сайтах стоят как надо? Вопрос был не про тракт, тракт четыре дня как проверен. Вопрос был про то, что стоит перед трактом.
Первая находка. На шести доменах сети формы на главной странице не существовало. Вместо неё стояла кнопка «Оставить заявку», которая вела к нужному месту страницы – а этого места на странице не было. Нажатие прокручивало страницу в никуда. Человек, пришедший с рекламы или из поиска, видел нормальный сайт с нормальной кнопкой, нажимал – и ничего. Сколько людей развернулось на этом месте, мы не узнаем никогда: несуществующая форма не оставляет следов.
Вторая находка. За отправку форм на восьми доменах отвечал общий сценарий – небольшая программа, которая работает в браузере посетителя, перехватывает нажатие кнопки и показывает вежливое «спасибо» вместо перезагрузки страницы. Этот сценарий искал формы по служебному признаку – а этого признака не было ни в одном шаблоне ни одного сайта. То есть сценарий был подключён, загружался, работал – и за всё время не обработал ни одной настоящей формы, потому что ни одна под его условие не подходила. Формы, которые всё-таки существовали, отправлялись напрямую, без него, и человек после нажатия кнопки видел вместо «спасибо» строку служебного ответа сервера. Заявка при этом, к слову, доходила – но выглядело это как поломка, и часть людей наверняка считала именно так.
Третья находка, самая тихая из всех. Тот же сценарий отправлял согласие на обработку персональных данных под одним именем поля. Сервер приёма читал другое имя. Стороны никогда не совпали. Согласие не сохранялось вообще никогда – даже у тех людей, которые нашли форму, поставили флажок и отправили заявку. Снаружи всё выглядело образцово: флажок есть, без него форма не отправляется, в базе есть столбец для согласия. В столбце всегда лежал ноль.
И четвёртая, из-за которой три первые смогли прожить так долго. Сам этот сценарий не лежал в системе контроля версий. Он жил прямо на рабочем сервере, куда его когда-то положили руками, а в инструменте выкладки напротив него стояла пометка «не трогаем». Файл, который отвечает за все формы сети, не имел ни истории изменений, ни хозяина, ни пути, по которому до него доехало бы исправление. Вдобавок он подключался на страницах без версии в адресе – даже после починки часть посетителей ещё долго получала бы из кеша браузера старый вариант.
Что это значило
Сложим обе картины. Разбор двадцать первого июля был добросовестным и нашёл настоящие поломки. Но он проверял тракт – цепочку от точки приёма заявки и дальше. А путь человека начинается раньше: открыть главную, найти форму, заполнить, нажать, поверить. На шести сайтах этот путь обрывался на втором шаге. Тракт был исправен ровно в том смысле, что всё, что в него попадало, доходило до конца. Просто на шести доменах в него нечем было попасть.
Диагноз «нет спроса» был поставлен по исправному тракту и убедительным числам. Прими мы его окончательно – решение ушло бы в маркетинг: поднять бюджет, поменять объявления, искать новые каналы. Деньги потекли бы в рекламу, которая приводит людей на страницу с кнопкой в никуда. Чем убедительнее ошибочный диагноз, тем дороже он обходится – именно потому, что по нему начинают действовать.
Стоит сказать и о том, чего в этой истории нет. В ней нет злодея и нет грубой небрежности. Каждая из четырёх поломок по отдельности объяснима, и вместе они складываются в обычную энтропию работающего проекта: шаблоны меняются, имена полей расходятся, служебные пометки переживают всех, кто помнил, зачем они поставлены. Эксплуатация – это дисциплина, которая исходит из того, что такая энтропия есть всегда, и строит сети, в которые она попадается.
Почему проверка была зелёной
Разберём механику. Четыре причины, по которым внимательный разбор с настоящими находками поставил неверный диагноз.
Проверяли систему, а путь человека начинается раньше системы. Разбор шёл от точки приёма: вот сюда прилетает запрос, дальше посмотрим каждое звено. Всё, что до этой точки – страница, кнопка, форма, сценарий отправки, – в проверку не попало, потому что не ощущалось частью системы. Это ощущение обманчиво. Для человека с той стороны экрана системой является всё, включая кнопку, которая никуда не ведёт. Сквозная проверка начинается с первого щелчка настоящего посетителя, с чистого браузера, без служебных закладок – иначе она сквозной только называется.
Проверочная площадка показывала зелёное по построению. У сети есть площадка предпросмотра, где страницы смотрят перед выкладкой. Адреса служебных запросов там ведут на другую службу, и та отвечает отказом – это нормально и известно. Но форма была написана так, что показывала «Спасибо» независимо от ответа. В итоге на предпросмотре формы «работали» всегда: заполнил, нажал, увидел благодарность. Зелёный результат этой проверки не значил ничего – она была не способна покраснеть ни при каких обстоятельствах. Отсутствие проверки хотя бы никого не успокаивает; такая проверка успокаивала всех.
Выводы делались по тексту исходников, а не по поведению. Тот же день, двадцать пятое июля, преподал и обратный урок – мы дважды объявили поломкой то, что работало. На одной странице висела служебная пометка, выглядевшая как след незавершённой работы, и по ней страницу записали в сломанные – а она принимала заявки исправно, пометка была мёртвым мусором. На другом сайте поиск не нашёл флажка согласия под ожидаемым именем – а флажок был, просто поле называлось иначе: другая система, другие имена. Оба приговора вынесены по чтению текста. Правило симметрично, и его стоит запомнить в обе стороны: по тексту исходников нельзя заключить ни что сломано, ни что работает. Заключение даёт только запуск и наблюдаемое поведение.
Тишину никто не слушал. Ноль заявок в июле не разбудил никого, потому что все сигналы тревоги были настроены на события: ошибка – сообщение, отказ – сообщение. Ноль событий ошибкой нигде не считается, это отсутствие, а на отсутствие сигналов обычно не ставят. Между тем именно отсутствие ожидаемого – главный симптом тихих отказов. У нас этот урок к тому моменту уже накопился в нескольких экземплярах: копии данных не делались трое суток – молча; служба напоминаний пять суток копила зависшие процессы – молча; очередь материалов на проверку старела почти месяц – молча. Тихая деградация оказалась основным видом отказа вообще, притом что мы считали её редкостью. Сигнал «ожидаемое не происходит уже N дней» ловит целый класс поломок, невидимых для сигналов об ошибках – включая нашу кнопку в никуда.
Тишина оказалась темой настолько отдельной, что ей отведена вся следующая глава. Пока достаточно запомнить, что ноль событий – это тоже событие.
Если вы заказчик. Два вопроса подрядчику стоят больше, чем весь остальной разговор о приёмке. Первый: «Как вы проверяете, что заявка с сайта доходит до менеджера?» Хороший ответ описывает сквозной путь и заканчивается словами «вот запись в базе». Второй: «Если заявки перестанут доходить – кто и через сколько узнает?» Ответ «мы проверяли при сдаче» переводится так: узнаете вы, от несостоявшегося клиента, через месяц. Оба вопроса не требуют от вас технических знаний – только готовности дослушать ответ до конца.
Упражнение. Прямо сейчас, не откладывая: откройте свой сайт как посетитель, в чистом окне браузера, и отправьте через форму настоящую заявку. Дойдите до конца пути – найдите её в базе, в почте, в мессенджере, там, куда она должна приходить. Увидеть «Спасибо» недостаточно: вы теперь знаете цену этому слову. Если весь путь прошёлся и заявка нашлась – поздравляем, вы потратили три минуты. Если нет – вы только что сэкономили себе июль, который мы разбирали всю эту главу.
Форма, с которой начиналась эта книга, врала одному человеку – вам, и прожила до первого обновления страницы. Проверка, о которой шла речь здесь, врала системе, которая ей доверяла, и прожила месяцы. Устроены обе поломки одинаково. Разница в сроке жизни, и объясняется она просто: первой было негде спрятаться, вторую охраняло зелёное. В следующей главе мы посмотрим, как устроить наблюдение за сервисом, при котором зелёному можно верить: что мерить, где ставить сигналы и почему датчик на выходе конвейера ничего не говорит о его входе.
Кто заметит тишину
Прошлая глава закончилась неудобным выводом: зелёная проверка может врать, и врёт она тише всех остальных поломок. Возникает естественный вопрос – а что тогда вообще считать признаком здоровья? Если «ошибок нет» ничего не гарантирует, на что смотреть?
Ответ у этой главы короткий, и он поначалу кажется странным: признак здоровья – присутствие ожидаемых событий. Отсутствие ошибок его заменить не может. Сервис здоров, когда регулярно происходит то, ради чего он существует, и молчащий журнал ошибок этого никак не гарантирует. К концу главы такой механизм, маленький, встанет и на вашем вечернем проекте.
Вопрос, на который не было ответа
В июле владелец нашей системы задал вопрос, от которого нельзя отмахнуться: почему ошибки всплывают снова и снова, хотя мы постоянно их чиним?
Вопрос был неудобным, потому что упрёка в нём не было. Чинили действительно постоянно. Каждая поломка разбиралась до причины, по каждой появлялось правило. И тем не менее раз в несколько дней всплывало что-то, что жило сломанным давно.
Ответ нашёлся, когда мы выложили случаи одного месяца в ряд и посмотрели на них как на коллекцию. Три из них в прошлой главе прошли одной строкой; здесь они по порядку и с причинами.
Копии данных не делались трое суток. Причина стоит того, чтобы её рассказать: в скриптах уборка старых копий стояла после загрузки новой. Хранилище заполнилось до квоты, загрузка стала падать, а до строки уборки дело уже не доходило – и каждый следующий запуск упирался в ту же стену. Самоподдерживающийся цикл отказов: поломка сама поддерживала условие, из-за которого не могла исправиться.
Сборщик заявок стоял пустым три месяца. Если история звучит знакомо – да, это тот самый мёртвый мост из прошлой главы. Там он был поломкой, которую нашли и починили; здесь он интересен другим: он умирал молча, с апреля, и ни один сигнал за три месяца не поднялся.
Служба напоминаний пять суток копила зависшие процессы. Каждые пять минут запускался новый, ни один не завершался, и к концу пятых суток они держали девяносто восемь соединений базы данных из ста. Всё остальное на этой базе начало отваливаться – вот тогда и заметили.
Очередь материалов на ручную проверку старела. Самая старая запись дождалась двадцати пяти дней.
Теперь общее. Во всех четырёх случаях не было ни одной ошибки. Ни строки в журналах, ни красного сигнала, ни упавшей задачи, которая бы об этом сообщила. Каждый случай обнаружил человек – заметил, заподозрил, спросил. Система обо всех четырёх молчала, потому что с её точки зрения ничего плохого не происходило. Просто ничего не происходило.
Мы чинили то, что кричало. А основной вид отказа не кричит.
Отсюда тезис, на котором стоит глава: тишина – это информация, которую никто не подписался получать.
Сигнал на отсутствие ожидаемого
Сигналы тревоги, которые ставят все, устроены одинаково: случилось плохое – сообщи. Упала задача, кончилось место, сервер ответил ошибкой. Такие сигналы нужны, и у нас они были. Но все четыре истории выше прошли сквозь них, как сквозь открытую дверь: плохого события не случалось ни в одной.
Нужен сигнал второго типа: хорошее не случилось за отведённое время – сообщи. Копия данных не появилась за сутки. Заявка не переслана за час. Журнал задачи не обновлялся полчаса. Первый тип ловит аварии. Второй ловит тихую смерть.
Через неделю после вопроса владельца у нас заработал сторож тишины – небольшая программа, которая раз в шесть часов проходит по списку ожидаемых событий и проверяет, что каждое случалось недавно. Проверок в нём семь, и про этот список важно одно: ни одна проверка не придумана из головы. Свежесть копий данных с порогом двадцать шесть часов – из истории про трое суток. Число зависших соединений базы с порогом семьдесят – из истории про службу напоминаний. Заполнение диска – из отдельного ожога той же недели. Остальные четыре устроены так же: сначала ожог, потом датчик. Например, свежесть журналов задач по расписанию с порогом полчаса: если задача, которая должна отработать каждые пять минут, полчаса не оставила следа, она мертва. Сторож – это дневник наших поломок, переписанный в форму, которая дежурит.
Два свойства этой программы важнее самого списка, потому что без них сторож быстро начинает вредить.
Первое – повторный сигнал о той же беде подавляется на сутки. Без этого одна поломка присылает одинаковое сообщение каждые шесть часов, к третьему дню его перестают читать, к пятому – читать перестают всё, что приходит с этого адреса. Сигнал, на который перестали реагировать, хуже отсутствия сигнала: он создаёт ощущение, что наблюдение есть.
Второе – пороги взяты из ожогов. Двадцать шесть часов для копий означают «раз в сутки плюс запас на длинную ночную работу»; желание поставить построже, на всякий случай, приводит к ложным тревогам, а те – к предыдущему пункту.
На вашем вечернем проекте ожидаемое событие одно: обращения приходят. Сторож для него – вот он целиком, файл storozh.py рядом с проектом:
import os
import sqlite3
import sys
from datetime import datetime, timedelta
BAZA = os.environ.get("ZAYAVKI_DB", "zayavki.db")
POROG_CHASOV = 24
try:
db = sqlite3.connect(BAZA)
poslednee = db.execute("SELECT MAX(sozdano) FROM obrasheniya").fetchone()[0]
except sqlite3.Error as oshibka:
print(f"ТИШИНА: база недоступна ({oshibka})")
sys.exit(1)
if poslednee is None:
print("ТИШИНА: обращений не было ни разу")
sys.exit(1)
vremya = datetime.fromisoformat(poslednee)
if vremya.tzinfo is None:
print("ВНИМАНИЕ: время в базе без часового пояса, считаю его местным")
vremya = vremya.astimezone()
if vremya < datetime.now().astimezone() - timedelta(hours=POROG_CHASOV):
print(f"ТИШИНА: последнее обращение {poslednee}, порог {POROG_CHASOV} ч")
sys.exit(1)
print(f"Порядок: последнее обращение {poslednee}")
Три строки в середине заслуживают отдельного слова, потому что за ними стоит ловушка, на которой сторожа врут чаще всего. Время бывает записано в разных часовых поясах, и по виду строки этого не видно. Наш сервис пишет местное время вместе с поясом – в строке будет хвост вроде +03:00. Но если базу заполняет не наш код, а, скажем, сама база своим стандартным способом (в задаче агенту это самый дешёвый ответ, и он его любит), время окажется всемирным, без всякой пометки. В Москве такая запись выглядит на три часа старше, чем она есть: сделанная минуту назад, читается как трёхчасовая, а порог в сутки срабатывает на двадцать первом часу. Западнее Гринвича перекос идёт в другую сторону – сторож становится мягче обещанного и молчит дольше.
Отсюда правило: время сравнивают только с явно указанным поясом. Сторож приводит обе стороны к одному виду, а если пояса в записи нет – говорит об этом вслух, вместо того чтобы молча предположить. Заметьте, куда мы попали: сторож, сравнивающий время наспех, – ещё одна проверка, которая врёт, только теперь она врёт в вашу сторону и особенно тихо. Прошлая глава была ровно про это, и вот вам её продолжение внутри собственного инструмента.
Обратите внимание на первый блок: если база недоступна или в ней нет нужной таблицы, сторож тоже кричит. Это сознательное решение, и оно из разряда тех, что отличают сторожа от украшения: любая беда самого сторожа должна превращаться в сигнал, потому что молчащий по техническим причинам сторож неотличим от довольного.
Запускается раз в день – планировщиком задач вашей системы или, для начала, рукой вместе с утренним кофе. Слово «ТИШИНА» и ненулевой код выхода – сигнал; код выхода это число, которым программа сообщает системе, всё ли прошло гладко: ноль значит порядок, любое другое число – беда. Как привязать к нему письмо или сообщение в мессенджер, зависит от вашей машины, рецепты собраны на странице книги (notevibe.ru/kniga.html); для вечернего проекта достаточно и привычки запускать.
Порог берите по своему потоку: при одном обращении в неделю суточный порог будет кричать шесть дней из семи, и вы отключите сторожа на третий день.
Вход важнее выхода
Теперь случай, который дороже всех остальных, потому что его не ловит даже правильно поставленный сторож, если тот смотрит не туда.
Наш конвейер статей – производство: генератор предлагает темы, писатель превращает их в тексты, тексты проходят проверку и очередью уходят на сайты. Наблюдение за ним было, и оно было осмысленным: если публикации остановятся, сигнал поднимется. Публикации не останавливались. Конвейер по всем показателям был жив.
При этом генератор тем месяцами работал вхолостую. В его настройке стоял потолок, из-за которого он предлагал по горстке тем на всю сеть сайтов, темы кончались, писателю было нечего брать. Производство умерло на входе. А выход этого не показывал, потому что между входом и выходом лежал запас готовых статей, и очередь публикации продолжала мерно его тратить.
Заметил человек. Спросил, почему новых материалов давно нет, – при зелёном наблюдении и работающей очереди.
У всякого конвейера есть буфер – запас между производством и выдачей. Буфер нужен и полезен: он сглаживает неровности. Но он же маскирует смерть входа – на столько времени, на сколько его хватает. Датчик на выходе показывает состояние буфера. О производстве он молчит. Чем больше запас, тем дольше система выглядит здоровой после того, как уже умерла.
После этого случая у нашего конвейера появились датчики входа: ноль новых тем за трое суток – тревога; запас тем меньше пяти – предупреждение; ноль написанных текстов за двое суток – тревога. Выход мы тоже мерим, но больше не считаем его ответом на вопрос «живо ли производство».
Начните с вопроса, датчик потом: что в вашем сервисе вход, а что выход? У сервиса обращений вход – люди, которые доходят до формы, выход – обработанные обращения. Если смотреть только на выход, легко месяц радоваться тому, что необработанных нет, – при потоке, который давно иссяк. Практическое правило простое: держите оба числа рядом, на одном экране – сколько пришло за неделю и сколько обработано. Одна строка, где эти числа стоят рядом; двумя отчётами в разных местах она не заменяется. Расхождение чисел в любую сторону – самый дешёвый диагностический прибор из существующих.
Кто сторожит сторожа
Остался вопрос, который вы, возможно, уже задали сами: а если сторож умрёт?
Вопрос законный: у сторожа есть расписание, которое можно снести, и окружение, которое можно сломать. Если он умрёт, наступит та самая тишина, которую он сторожил. Молчание наблюдения снаружи неотличимо от здоровья: либо всё хорошо, либо никто больше не смотрит.
Решение выглядит парадоксально, но оно единственное: сторож должен иногда подавать голос без повода. Наш присылает раз в неделю, по понедельникам, одно сообщение – «монитор жив, проблем нет». Пятьдесят две строки в год, цена смешная. Зато отсутствие понедельничного сообщения – само по себе сигнал, и ловит он ровно один сценарий: смерть наблюдения. Всё остальное время сторож молчит: голос без повода, звучащий чаще, чем раз в неделю, быстро становится шумом, а шум перестают слышать.
Здесь наш порядок годится вам без изменений: пусть сторож раз в неделю сообщает, что он жив. Если в понедельник сообщения нет – утро начинается с него.
Слепая зона в отчёте
Наблюдать приходится за двумя вещами сразу: за системой и за тем, как она о себе рассказывает. Вторая ломается по-своему.
На рабочем экране нашего проекта материалы были видны в двух состояниях: «на проверке» и «опубликовано». Логично и чисто. В июле выяснилось, что сто десять готовых статей не видны нигде: они прошли проверку – значит, уже не «на проверке», и ждали своей очереди публикации – значит, ещё не «опубликовано». Промежуточное состояние, в котором вещь проводила недели, выпало из отчёта целиком.
Вопрос при этом задали тот же, что и в истории с генератором тем: почему нет новых статей. Только причина на этот раз оказалась в отчёте – система работала, очередь была полна, все датчики зелёные. Полезное совпадение: один и тот же симптом дважды за месяц привёл к разным причинам, и оба раза его принёс человек.
Правило из этого случая формулируется жёстче, чем кажется на первый взгляд: у каждого состояния, в котором вещь может находиться дольше минуты, должно быть место в отчёте. Состояние, которое нигде не показывается, эквивалентно потерянному – для всех, кто смотрит на отчёт, этих ста десяти статей не существовало.
Пересчитайте состояния своих обращений. «Новое» и «обработано» – это то, что мы сделали в части 0. А те, что взяли в работу и не закрыли? Позвонили, договорились перезвонить, ждём ответа человека? Если такого состояния нет в вашем списке – оно всё равно существует, просто в невидимом виде: в голове, в переписке, нигде. Всё, что живёт в невидимом состоянии, рано или поздно теряется, и узнаёте вы об этом от того, кому не перезвонили.
Что из этого следует
Правило, которым глава закрывается. Отсутствие наблюдения оплачивается каждый раз заново – и, как правило, в худший момент, потому что тихие отказы вскрываются тогда, когда накопились, а это всегда позже, чем они случились. Нам за один июль этот счёт приходил четырежды.
Если вы заказчик. Спросите подрядчика: «Покажите список событий, за отсутствием которых следит система, и порог по каждому». Ответ «у нас настроен мониторинг» сам по себе не значит почти ничего – в девяти случаях из десяти это сигналы об ошибках, сквозь которые тихие отказы проходят свободно. Второй вопрос ещё показательнее: «Что приходит мне или вам, когда всё в порядке?» Если ответ «ничего», то смерть наблюдения никто не заметит.
Упражнение. Поставьте сторожа из этой главы на свой вечерний проект. Потом проверьте его – единственным способом, который считается: устройте тишину нарочно. Переименуйте на время файл базы или подставьте сторожу пустую – и убедитесь, что он закричал. Это то же правило, что и в прошлой главе: проверка, которая ни разу не краснела у вас на глазах, пока лишь надежда, и называть её проверкой рано.
Мы прошли путь от «зелёное может врать» до «тишина – тоже данные». Следующая глава – о самом нервном моменте эксплуатации: как менять работающий проект, не разрушая его. Там снова встретятся и границы правки из первой части, и сторожа из этой – потому что самое опасное время для любого сервиса начинается со слов «сейчас я быстренько поправлю».
Сейчас я быстренько поправлю
Собрать проект трудно, но безопасно. У нового проекта нет пользователей, в базе нет ничего, что жалко, и худшее, что может случиться, – потерянный вечер. Вся первая половина книги прошла в этом счастливом режиме, где любую ошибку лечит перезапуск.
Менять работающий проект – другая работа с другой ценой. За каждой правкой теперь стоят люди, которые уже пользуются, и данные, которые уже накоплены. Ошибка перестаёт стоить вечер: она стоит чьё-то потерянное обращение, чьё-то испорченное впечатление, чей-то уход. При этом менять придётся постоянно – работающий проект без изменений не живёт, он либо развивается, либо тихо устаревает.
Вы уже умеете собирать, проверять и наблюдать. Осталась последняя дисциплина эксплуатации: вносить изменения так, чтобы после каждого проект оставался целым.
Начнём с тезиса, который переворачивает бытовую интуицию. Кажется, что опасность правки пропорциональна её размеру: большую страшно, маленькую можно «быстренько». Наш опыт показывает другое. Опасность правки меряется числом мест, которые она затрагивает без вашего ведома. Однострочная правка с тремя невидимыми соседями опаснее переписанного модуля, у которого соседей нет. Слова «сейчас я быстренько поправлю» потому и открывают самые дорогие вечера, что произносятся перед правками, у которых соседей не искали.
Одна правка – одна мысль
Правило вам уже знакомо по первому вечеру: одна задача, одна правка, одна проверка, один коммит. Пришло время объяснить, почему с агентами оно перестаёт быть просто хорошей привычкой и становится техникой безопасности.
Агент по своей природе достраивает. Попросите его поправить статус обращения – и вместе с правкой вы рискуете получить переименованные «для ясности» переменные, переставленные «по логике» функции и обновлённое «для единообразия» оформление. Каждое из этих действий по отдельности осмысленно. Все вместе они превращают вашу правку в изменение, где нужное не отличить от привнесённого. Если после такого изменения что-то сломается, вы не будете знать, которая из пяти непрошеных доработок виновата, – и разбирать придётся все.
Лекарство двухслойное. Первый слой – граница в самой задаче, вы её уже ставили: «меняй только то, что относится к статусу; форму и сохранение не трогай». Второй слой – проверка, которая охраняет запрещённое к изменению: тест из части 0 защищает сохранение обращений независимо от того, что вы просили. Если после правки статуса он покраснел, агент вышел за границу, и вы узнали об этом за минуту, по горячим следам. Без теста вы узнали бы через неделю, когда след остыл.
Мера правильного размера правки простая: она должна помещаться в одно предложение задачи. «Добавь кнопку обработано» – помещается. «Добавь кнопку, поправь список и заодно наведи порядок в стилях» – три задачи, а слово «заодно» в постановке задачи стоит запретить себе совсем. Именно на «заодно» агент отвечает самой широкой правкой.
Правка сделана – ещё не значит применена
Теперь ярус, о котором не думает почти никто, и который в июле стоил нам дважды.
В голове у начинающего изменение устроено просто: я поправил файл, значит, всё изменилось. На деле между «я изменил файл» и «люди видят изменение» лежат четыре рубежа, и правка может застрять на любом из них, не подавая признаков застревания.
Рубеж первый: файл изменён на диске. Это единственный рубеж, который виден в редакторе.
Рубеж второй: изменение попало туда, откуда на самом деле работает служба. Если проект живёт на сервере, собирается перед выкладкой или запущен в контейнере – той самой коробке из главы о среде, со своей копией файлов, – то файл на вашем диске и файл, который исполняется, это разные файлы, и между ними есть шаг доставки.
Рубеж третий: работающий процесс перечитал код. Программа, запущенная вчера, держит в памяти вчерашний код и знать не знает, что файл под ней изменился.
Рубеж четвёртый: посетитель получил новую версию. Браузеры запоминают загруженное, и если страница не умеет сообщать «файл обновился», человеку из кеша отдаётся старое.
Оба наших июльских случая – про третий и четвёртый рубежи, и оба поучительны именно будничностью.
Первый. Важная правка легла в файлы, файл внутри контейнера обновился – первые два рубежа пройдены чисто. А служба к тому моменту работала сорок три часа без перезапуска и продолжала исполнять код, который держала в памяти. Формально работа сделана: файл везде новый. Фактически не изменилось ничего, и не изменилось бы до следующего перезапуска – через день, через неделю, когда придётся. Урок мы сформулировали для себя жёстко: проверять надо работающий процесс, диск тут ничего не доказывает. У запущенной службы можно спросить, какой код она исполняет, – и только этот ответ считается.
Второй случай вы уже встречали в главе про врущую проверку, теперь посмотрим на него со стороны правки. Сценарий отправки форм подключался на страницах без версии в адресе. Это значит: даже когда починка доехала до сервера и до работающей службы, часть посетителей ещё долго получала бы из кеша браузера старый сломанный вариант. Для них починка не состоялась. Четвёртый рубеж коварнее прочих тем, что он у каждого посетителя свой: у одних новое, у других старое, и жалобы выглядят мистикой – «у меня работает, у клиента нет».
В вечернем проекте рубежей меньше, но они есть. После правки кода – перезапуск сервера, иначе исполняется прежний. После правки страницы – обновление в браузере с очисткой кеша. Файл и собственная уверенность в свидетели не годятся.
У правки есть соседи
Третий ярус – о правках, которые прошли все четыре рубежа и всё равно ударили. Потому что изменение приехало, но не везде, где должно было. У правки были соседи, и о них не подумали.
Соседи бывают трёх видов.
Общий ресурс. У нас два сборщика – два независимых инструмента, каждый собирает свою часть сайтов – писали таблицу стилей в один и тот же файл. Годами это никому не мешало: правки не пересекались, и никто вообще не знал, что ресурс общий. Потом пересеклись. Кто выложил последним, тот и прав: сто тридцать шесть статей вышли на сайт без оформления, голым текстом. Заметил владелец, по скриншоту. Правило из этой истории: у каждого файла, который пишут двое, однажды случится этот день – вопрос только в дате. Общие ресурсы надо знать в лицо до того, как они напомнят о себе.
Забытая точка. Направление сняли с продажи. Аккуратно, по списку: поправили сайты, остановили генерацию статей на эту тему, перенастроили маршрутизацию. А призыв к действию, который автоматически подставляется в конец каждого видеоролика, жил отдельной записью в настройках бренда – и остался прежним. Ролики продолжали обещать зрителям услугу, которой больше не существовало. Обнаружил владелец, на готовом ролике, глазами. Правило: у решения «мы больше этого не делаем» столько адресов, сколько мест об этом когда-либо обещало. Список адресов составляется до правки. После – его составляет уже клиент, задавая неудобные вопросы.
Мина в стороне. Разбирая тот же случай, мы нашли в дереве сборки постороннюю таблицу стилей, пролежавшую там с мая: при точечной выкладке она спала, при полной сломала бы оформление всего сайта. Правило: перед выкладкой целиком посмотреть, что в дереве вообще лежит.
Для вечернего проекта всё это сжимается в один вопрос, который задаётся до правки: где ещё живёт то, что я меняю? Название услуги, цена, телефон, текст письма, адрес – если сущность упомянута в трёх местах, правка в одном создаёт расхождение. Расхождение не подаёт сигналов. Его находит клиент, сверив два места между собой, и находит всегда в неудачный момент.
Чужие руки, включая свои
Этот ярус легко пропустить, работая одному: конфликтовать вроде не с кем. Но у вас есть агент, и он правит те же файлы, иногда буквально в те же минуты. Две сессии агента в двух окнах – это уже двое работающих над одним проектом, с классическими командными граблями.
Простой вариант вы уже знаете: в первом вечере мы упоминали, как две сессии, правившие одну папку, затёрли работу друг друга, и на сайт уехал не тот вариант страницы. Ни одна из них не сделала ничего дурного – просто они не знали друг о друге, а общий файл знал обоих.
Второй наш случай тоньше, и его урок дороже. Правку установили и сразу проверили: работает, всё на месте. Через полчаса служба отдавала старые данные, каталог схлопнулся с четырёх тысяч записей до четырёхсот. Что произошло: параллельная сессия сохранила свой вариант файла поверх – взяв за основу копию, снятую до нашей правки. Наша работа была жива в момент проверки и умерла через двадцать минут после неё.
Отсюда два правила, оба выстраданные.
Проверка сразу после установки необходима и недостаточна. Если над проектом работает кто-то ещё – вторая сессия, агент в фоне, коллега, – нужна перепроверка через время. Мы теперь возвращаемся к важным правкам через полчаса-час и смотрим, на месте ли они.
И тут вспоминается сторож из предыдущей главы, потому что перепроверка через час – работа, которую нельзя поручать памяти. Память занята следующей задачей. Если правка важная, а рядом работают чужие руки, самое надёжное – сделать её предметом наблюдения: проверка, которая раз в час убеждается, что нужное значение на месте, стоит десять строк и не забывает. Это тот же приём, что и сигнал на отсутствие ожидаемого, только ожидаемое здесь – ваша собственная работа.
Копии, оставленные чужой работой, – архив, пока не доказано обратное. Когда чья-то версия затёрла чью-то, единственным носителем проигравшей работы часто оказывается резервная копия, которую сделала затёршая сторона. Удалить «лишние файлы» до разбора – значит уничтожить то единственное, из чего затёртое можно восстановить.
Для вечернего проекта защита от всего яруса уже стоит – это git, но с одной оговоркой: он защищает только зафиксированное. Правка, не попавшая в коммит, существует на птичьих правах, и любая параллельная рука – агент, вторая сессия, вы сами завтрашний – может её молча переписать. Частые коммиты из привычки аккуратности превращаются здесь в единственную настоящую страховку.
Порядок безопасной правки
Соберём главу в последовательность. Она выглядит длинной, но на деле занимает минуты – и за каждым шагом стоит конкретный случай; часть вы только что читали.
- Понять, что меняешь и где ещё оно живёт. Вопрос про соседей задаётся до правки.
- Зафиксировать рабочее состояние. Коммит перед правкой – десять секунд, и у вас есть точка возврата.
- Прогнать вхолостую, если инструмент умеет. Многие инструменты позволяют посмотреть, что изменение сделает, до того как оно это сделает. У нас правило «сначала вхолостую, потом всерьёз» действует во всех рабочих цепочках без исключения – однажды такой прогон показал шестьдесят семь строк, которые ушли бы в работу дублями.
- Снять с публикации то, что будет пересобрано. Наш случай: ролики с устаревшим призывом сначала сняли в черновики, потом пересобирали. В обратном порядке в эфир успевает уйти промежуточная версия.
- Одна правка – одна мысль. С границей в задаче агенту.
- Проверить поведение работающей службы. Путём посетителя, с учётом всех четырёх рубежей.
- Прогнать защитные проверки. Тесты, которые охраняют то, что трогать запрещалось.
- Зафиксировать результат. Коммит с внятной подписью.
- Перепроверить через время, если рядом работает кто-то ещё – включая ваши собственные параллельные сессии. Лучше всего эту работу отдать сторожу из предыдущей главы.
И отдельно – про откат, потому что он венчает весь порядок. Возврат к прошлому состоянию должен быть рутинным решением; если он подвиг, значит, его нет. Проверка простая: можете ли вы вернуть вчерашнюю версию одной командой, не вспоминая, что именно меняли? Если для отката надо восстанавливать в памяти список правок, у вас вместо механизма надежда на память.
Устройте себе учебную аварию прямо сейчас, это две минуты. Удалите в вечернем проекте нужную строку кода – любую, без которой тест краснеет, – и убедитесь, что он покраснел. Затем верните всё одной командой: git checkout -- . возвращает файлы к последнему коммиту. Прогоните тест снова, увидьте зелёное. Теперь дорога назад известна не в теории, а руками, и в следующий раз, когда правка пойдёт не туда, вспоминать её не придётся.
Закрывающее правило главы: работающий проект меняют маленькими шагами с фиксацией после каждого. Вопреки ощущению, скорость от этого не падает – шаги маленькие, но их много, и они не откатывают друг друга. Падает цена ошибки.
Если вы заказчик. Три вопроса подрядчику, которые за минуту показывают, умеет ли он менять работающее: «Как вы возвращаете предыдущую версию, если после обновления что-то сломалось?», «Сколько времени занимает возврат?», «Что вы делаете перед выкладкой, чтобы не задеть уже работающее?». Хороший ответ на первый вопрос содержит название механизма, на второй – минуты, на третий – слова «проверка» и «резервная копия». Ответ «у нас всё под контролем» на все три переводится одинаково: возвращаться мы не пробовали.
Упражнение. Выпишите на бумагу три места, где живёт ваш телефон, и три, где живёт цена. Сайт, письмо-подтверждение, подпись в почте, карточка в справочнике, договор, страница в соцсети. Теперь сверьте их между собой. У большинства людей, делающих это впервые, находится хотя бы одно расхождение – старый номер, прошлогодняя цена, адрес, который уже никто не читает. Это и есть соседи вашей будущей правки, увиденные заранее: список, который вы только что составили, в следующий раз сэкономит вам разговор с клиентом, нашедшим два разных ответа на один вопрос.
Эта глава закрывает набор навыков обращения с работающим проектом: проверять, наблюдать, менять. Следующая – о том, на чём всё это стоит и о чём вспоминают позже всего: где на самом деле лежит ваш проект. Из нашей практики: однажды выяснилось, что правки целого дня существуют только внутри работающего контейнера, и одна перезагрузка отделяла их от небытия. Про то, как не оказаться в этом положении, – дальше.
Где на самом деле лежит ваш проект
Начнём с трёх вопросов.
У вас сгорел ноутбук. Что осталось от проекта?
Вы уехали на неделю, сервис упал, перезапустить его нужно другому человеку. Справится?
База данных обнулилась. Откуда вы возьмёте вчерашнее состояние?
Если хотя бы на один вопрос ответа нет, какая-то часть вашего проекта существует в единственном экземпляре. А единственный экземпляр – это вопрос даты: он потеряется, вопрос только когда. Диски умирают, ноутбуки тонут, серверы пересоздаются, и ни одно из этих событий не предупреждает заранее.
Обычный ответ на вопрос «где лежит ваш проект» звучит как «на компьютере» или «на сервере», и он ничего не значит. Настоящий ответ – перечень мест, где проект существует в единственном экземпляре, потому что именно этот перечень определяет, что вы потеряете. Тезис главы, к которому мы придём через три наших июльских случая: проект существует там, откуда его можно восстановить. Всё остальное – место, где вы с ним работаете.
Все три случая произошли с одним проектом за две недели.
Код живёт в трёх местах
У кода работающего проекта три дома. Папка, где вы его правите. Место, откуда исполняется запущенная служба, – в прошлый раз мы называли его вторым рубежом. И репозиторий – место, откуда код можно восстановить. В здоровом проекте содержимое всех трёх совпадает. Беда в том, что расходятся они молча.
Июль, плановая настройка резервного копирования. Попутная проверка показывает: контейнер, в котором работает сервис, никак не связан с папкой на диске. Совсем. Весь код, включая правки текущего дня, существует только внутри работающего контейнера. Сверка подтверждает худшее: файлы на диске отличаются от файлов в контейнере, а в репозитории не хватает трёхсот восьмидесяти двух файлов.
Переведём на бытовой язык. Работа целого дня жила в одном экземпляре – в самом хрупком месте из всех возможных, внутри запущенного процесса. Пересоздание контейнера стёрло бы её бесследно, и никакая копия базы данных тут не помогла бы: база хранит данные, код она не хранит. Самое неприятное в этой истории – то, что пересоздание в тот же день уже случалось и уже роняло правки. Это было известно. Это считалось неудобством – «после пересоздания надо доложить правки заново», – и никто не сказал вслух: одна команда отделяла работу дня от небытия.
Урок формулируется коротко. Место, где вы правите файлы, и место, откуда работает сервис, – разные места, пока не доказано обратное. Доказательство одно: сравнить содержимое. Ощущение «ну оно же одно и то же» доказательством не является – у нас оно держалось до первой построчной сверки файлов.
У вас всё проще, но устроено так же. Папка проекта, запущенный сервер, репозиторий. Расхождение рождается из одной привычки – править и не фиксировать. Проверка встроена в git и занимает секунду:
git status
Каждый файл в списке изменённых существует в единственном экземпляре – в вашей папке. Репозиторий о нём не знает или знает устаревшую версию. Пока список не пуст, часть проекта живёт на птичьих правах, и предыдущая глава показывала, кто первым этим пользуется: параллельная рука, включая вашу собственную.
Перенос обнажает правду
Второй случай отличается от остальных тем, что здесь потеря уже произошла. Не «могла бы» – произошла, и мы разбирали последствия.
Проект переносили на новый сервер. Перенос шёл по репозиторию, с основной ветки, – разумный, казалось бы, способ: репозиторий на то и существует. Вся работа последних недель жила в другой ветке – а перенос по одной ветке не захватывает остальные, а пятьдесят четыре файла не были в репозитории вообще – часть изменена и не зафиксирована, часть никогда туда не добавлялась. В результате на новом сервере не хватило тридцати файлов: программных классов, шаблонов страниц, миграций базы.
Снаружи это выглядело поучительно. Главная страница нового сервера открывалась и отвечала кодом успеха – тем самым 200, который проверял ваш первый тест в части 0. По всем формальным признакам перенос удался. При этом персональные страницы пользователей отдавали ошибку, а вход в личный кабинет молча не работал: среди потерянного оказался файл с правилами безопасности, без которого страница не могла выполнить ни одну кнопку. Человек нажимал «Войти» – и ничего не происходило. Без ошибки, без сообщения. Ничего.
Узнаёте рисунок? Это врущая проверка из начала части, в новой роли. Открывшаяся главная ничего не говорила о недостающих файлах, потому что главная их не использует. Проверка проходила по дороге, которая потерь не задевала, – и уверенно показывала зелёное.
Разобравшись, мы сформулировали для себя правило. Признак завершённого переноса один: списки файлов сверены построчно, старый сервер против нового. Открывшаяся главная таким признаком не является. Сверка заняла бы минуты. Разбор её отсутствия занял день.
И последняя деталь этой истории, самая неприятная из всех. Когда стали выяснять, где же лежит полный актуальный код, ответ оказался таким: в папке на рабочем компьютере. Вне репозитория. Единственное место в мире, где проект существовал целиком, – рабочий стол одного ноутбука. Любое восстановление сервера по репозиторию теряло бы всё снова, сколько ни повторяй. Это положение встречается куда чаще, чем принято признаваться, у него даже есть узнаваемая формула: «у меня всё локально, надо бы закоммитить».
Проверьте у себя одним вопросом, он же тест на выживание проекта: если развернуть проект с нуля из репозитория на чистой машине, он заработает? Всё, чего в репозитории нет, при таком развёртывании не появится: незафиксированный файл, настройка, которую вы делали руками и не записали, данные. Отвечать рассуждением бесполезно, мы только что видели, чего стоят рассуждения. Отвечает попытка – и она вынесена в упражнение этой главы.
Данные, которых нет нигде
Третий случай замыкает лестницу. Проверка резервного копирования на том же проекте дала результат короче некуда: копий нет. Расписание пусто. Каталога, куда они могли бы складываться, не существует. Восемнадцать с половиной тысяч профилей людей и пятьдесят тысяч загруженных ими работ существовали в одном экземпляре, на одном диске, на одном сервере.
Была и предыстория: копия на старом сервере существовала. Её удалили после переезда – переезд же признан успешным, зачем хранить старое. Логика безупречна до того момента, когда вспоминаешь, что на новом сервере копий ещё нет. Сутки данные пролежали без единой страховки.
Здесь черта из главы о данных проходит по живому. Код можно написать заново: дорого, долго, обидно, но можно, и с агентом быстрее, чем когда-либо. Данные написать заново нельзя. Пятьдесят тысяч чужих работ не восстановит ни агент, ни бюджет, ни раскаяние. Поэтому порядок приоритетов обратный привычному: первым делом страхуются данные, и только потом всё остальное. Привычный порядок обычно другой, потому что код – это «моя работа», а данные – «просто база».
Что мы настроили: ежедневная копия базы в отдельное закрытое хранилище, изолированное от всего публичного, со сроками хранения. И одна деталь, ради которой стоило рассказывать первую историю: код для копии каждый раз берётся из работающего контейнера; диск для этого не годится. Он уже отставал однажды и может отстать снова, а копия обязана снимать то, что работает.
На вечернем проекте это одна команда: скопировать файл базы в другое место, и одну строку в расписании, чтобы это происходило само. С одной оговоркой, которую нельзя опустить: копия на том же диске защищает от «я случайно удалил» и бессильна против «диск умер». Против второго помогает только другой носитель – другая машина, облачная папка, что угодно физически отдельное.
Копия, из которой не восстанавливали
Остался последний шаг лестницы, и вы, прочитав три главы этой части, наверняка сделаете его сами. Копия есть. А восстановиться из неё можно?
Копия существует ради восстановления, а не ради строчки «настроено». Между «архив создаётся каждую ночь» и «из архива можно подняться» лежит целый список возможных сюрпризов: файл пишется битым, выгрузка обрывается на середине и никто не замечает, у архива нет прав на чтение, пароль от шифрования знал уволившийся, версия базы на новой машине не читает старый формат. Общее свойство у этих сюрпризов одно: все они обнаруживаются в момент, когда восстановление уже понадобилось. То есть в худший из возможных.
Мы проверяли свою первую копию так: подняли её в отдельную временную базу и пересчитали содержимое против работающей. Все восемнадцать с половиной тысяч профилей и все пятьдесят тысяч работ на месте, ноль расхождений. Двадцать минут работы. До этих двадцати минут у нас была надежда; после – знание. Нигде больше в этой книге такой обмен не стоит так дешево.
Копии, к слову, умеют и переставать делаться – молча, как всё в этой части книги; наш случай с этим уже рассказан в главе про тишину. Поэтому полный комплект страховки данных состоит из трёх предметов: сама копия, сигнал на её отсутствие и проверка восстановлением.
У вас это ежемесячный ритуал на десять минут: взять свежую копию, развернуть в отдельную папку, запустить проект на ней, увидеть свои данные. Если этот ритуал не проводится, у вас хранятся файлы. Копиями их делает только проверка.
Правило, закрывающее часть
Оно короткое: проект существует там, откуда его можно восстановить. Всё остальное, как бы дорого оно ни ощущалось, – рабочее место.
Если вы заказчик. Три вопроса до оплаты, а не после. «Где лежит код и есть ли у меня к нему доступ?» «Как часто делаются копии данных и где они хранятся?» «Когда последний раз проверяли восстановление?» Первый вопрос важнее двух остальных вместе: если код лежит только у подрядчика, вы владеете не проектом, у вас есть подписка на отношения с подрядчиком. Она заканчивается вместе с отношениями.
Упражнение. Разверните свой вечерний проект с нуля: новая пустая папка, только репозиторий, только инструкция из README. В старую папку не подсматривать. Всё, что придётся вспоминать или додумывать руками, – список того, чего в проекте нет; по итогам допишите README и зафиксируйте недостающее. Это упражнение – генеральная репетиция и сгоревшего ноутбука, и передачи проекта другому человеку. Второе нам скоро понадобится.
На этом эксплуатация как навык собрана: вы умеете проверять так, чтобы проверка краснела; слышать тишину; менять, не разрушая; и знаете, где лежит то, чем вы владеете. Последняя часть книги – про день, ради которого всё это копилось: проект уходит из ваших рук. Передача помощнику, подрядчику, покупателю – или себе самому через полгода, что почти то же самое. Список мест, где живёт ваш проект, который вы составили в этой главе, там развернётся в список того, что вы отдаёте.
Четырнадцать вопросов к работающему проекту
Часть III была про то, что происходит с проектом после запуска. Четыре её главы разбирали четыре способа обмануться: поверить зелёной проверке, не услышать тишину, сломать работающее одной правкой и не знать, где проект лежит.
Ниже – всё, что из этих глав следует, в виде вопросов. Шесть вопросов первого вечера выросли здесь в четырнадцать – и выросли тем же способом, каким рос ваш проект: каждый добавился после настоящей поломки, нашей или вашей учебной. Они собраны в одном месте для употребления: пройдите по ним один раз для своего проекта, отвечая делом. Намерение ответом не считается. Ответ «да, наверное» считается за «нет» – именно он стоял у нас напротив половины этих пунктов в июле, когда мы думали, что держим всё под контролем.
Проверки
1. Проверяется ли путь целиком? От щелчка человека до записи в хранилище и уведомления тому, кто должен отреагировать. Включая участки, которые проверять неудобно.
2. Проверяется ли там, где живёт настоящий сервис? Зелёное на предпросмотре, на местной копии и в тестовом контуре ничего не говорит о площадке, куда ходят люди, если она отличается хоть одной настройкой.
3. Умеет ли проверка краснеть? Сломайте нарочно то, что она проверяет. Если после умышленной поломки она осталась зелёной, у вас украшение вместо проверки.
4. Проверяется поведение или текст? По исходникам нельзя заключить ни что сломано, ни что работает. Отвечает только запуск.
Наблюдение
5. Какое событие должно происходить регулярно и кто узнает, если оно перестанет? Сигналы на ошибку тихую смерть не ловят: ноль событий ошибкой нигде не считается.
6. Мерите вход или только выход? У любого конвейера есть буфер, и он маскирует смерть входа ровно на столько, на сколько его хватает.
7. Как вы узнаете, что само наблюдение работает? Молчание наблюдения снаружи неотличимо от здоровья. Нужен пульс: раз в неделю сигнал «я жив».
8. Есть ли у каждого состояния место в отчёте? Состояние, которое нигде не показывается, эквивалентно потерянному – включая те, в которых вещи задерживаются надолго.
Изменения
9. Знаете ли вы, где ещё живёт то, что собираетесь менять? Цена, телефон, название услуги, текст письма: правка в одном месте из трёх создаёт расхождение, которое найдёт клиент.
10. Как вы убеждаетесь, что правка применилась? Четыре рубежа: диск, место, откуда работает служба, память запущенного процесса, кеш браузера посетителя.
11. Можете вернуться назад одной командой, не вспоминая? Если для отката надо восстанавливать в памяти список правок, у вас вместо механизма надежда на память.
Фундамент
12. В каких местах проект существует в единственном экземпляре? Это и есть перечень того, что вы потеряете.
13. Заработает ли он, если развернуть с нуля из репозитория на чистой машине? Всё, чего в репозитории нет, при таком развёртывании не появится.
14. Когда вы последний раз восстанавливались из копии? Копия существует ради восстановления. До первой проверки это файлы, а не копии.
Четырнадцать вопросов – не программа на выходные и не формальность перед сдачей. Это то, что отличает работающий сервис от собранного: у собранного всё это отсутствует и до первой аварии никак себя не проявляет.
Если хотя бы половина пунктов осталась без ответа делом – вы в том положении, в котором мы были в июле. Ничего постыдного в нём нет: система работала, сайты открывались, статьи выходили. Просто половина её здоровья держалась на удаче, и мы об этом не знали.
Дальше – часть IV, про день, когда проект уходит из ваших рук. Ответы на эти четырнадцать вопросов там пригодятся дважды: как то, что вы передаёте, и как то, что вы вправе требовать, когда принимаете чужую работу.