Часть II. Инженерия
Ремесло из первой части позволяет получать от агента нужный результат. Инженерия, которой посвящена вторая, отвечает за то, чтобы полученное не терялось: не пропало, не сломалось молча, не открыло дверь посторонним.
Скажем прямо, что вас ждёт: набор предохранителей. История изменений, проверки, устройство данных, секреты, воспроизводимая среда, выкладка. Каждый предохранитель стоит около часа сегодня и экономит день потом; ни один не делает проект лучше на вид. Все вместе они решают, доживёт ли проект до второго месяца – по нашим наблюдениям, вечерние проекты умирают именно там, и умирают по-инженерному: от потерянного, сломавшегося молча и открытого настежь.
И одно число, чтобы задать вес всей части. Код, сгенерированный моделями, проходит проверки безопасности в 56 процентах случаев, и за год эта доля не изменилась, хотя сами модели стали заметно сильнее (Veracode, отчёт о безопасности сгенерированного кода, июль 2026). Писать работающий код модели научились. Писать безопасный – нет, и разрыв не сокращается. Генерация решена. Всё остальное – предмет этой части, и отвечает за него владелец.
История изменений всерьёз
Git сопровождает вас с первого вечера, и до сих пор мы обходились четырьмя командами: завести историю, добавить, зафиксировать, посмотреть состояние. В части про несколько рук добавились ещё три – посмотреть журнал, посмотреть разницу, вернуть файлы к последней точке. Этого достаточно, чтобы не терять работу. Пришло время закрыть тему всерьёз. Дело даже не в новых командах – их будет одна. Дело в правильном понимании, зачем всё это.
Расхожее представление о системе контроля версий – «архив для командной работы»: штука, которая нужна, когда людей много, а пока ты один, это бюрократия. Представление устойчивое и перевёрнутое. Один плюс агент – это уже не один, вы знаете это с главы о нескольких руках. А архив – самая скучная из функций истории. Настоящих функций три, и все три нужны лично вам, команда тут ни при чём.
История – это машина времени. Любое состояние проекта, которое вы зафиксировали, достижимо обратно. Отсюда следствие, которое меняет характер работы сильнее любого приёма из этой книги: эксперимент перестаёт быть риском. Можно позволить агенту большую переделку, зная, что путь назад – одна команда. То, что у опытных людей выглядит смелостью, обычно устроено проще: у них есть выход.
История – это показания. Когда что-то сломалось, первый вопрос – «что менялось перед этим?». Без истории на него отвечает память, и отвечает плохо. С историей – журнал: вот изменения, вот даты, вот подписи. Разница между «кажется, я вчера что-то трогал в форме» и «в 19:40 изменены два файла, вот построчная разница» – это разница между гаданием и расследованием.
История – это граница между сделанным и болтающимся. Зафиксированное защищено: от агента, от второй сессии, от вас завтрашнего. Незафиксированное не защищено ни от чего. Мы повторяли это в разных формах всю первую часть; здесь эта мысль становится определением: в проекте существует то, что в истории. Остальное – черновик на ветру.
Наш антипример, без прикрас
Признание, которое обязано быть в этой главе, потому что без него она звучала бы поучением с горы.
У нас в хозяйстве годами жила другая культура – резервные копии файлов. Перед правкой файл копируется рядом с суффиксом: имя, точка, «резерв», дата. В одной нашей служебной папке таких копий десятки – слои правок за месяцы, как годовые кольца. А одна из наших рабочих служб не под системой контроля версий по сей день: у неё вместо истории – эти самые копии, и в её файле правил стоит специальная строка о том, как с этим жить.
Чем это плохо, мы выяснили на себе, по пунктам. Копия отвечает на вопрос «что было» – и молчит на вопросы «что именно изменилось», «когда» и «зачем». Сравнивать копии между собой – ручная работа. Понять, которая из шести копий та самая, – археология. А когда над файлами работает несколько сессий – а вы знаете по главе о нескольких руках, как это бывает, – копии превращаются в единственный носитель затёртого труда, и вся защита держится на случайности: сделал кто-то копию перед правкой или нет.
История изменений закрывает всё это одним механизмом. Мы это знаем, мы этим живём – и всё равно тащим хвост старой привычки, потому что перевод работающей службы под контроль версий требует остановки и аккуратности, а «пока и так работает». Узнаёте интонацию? Это «потом разберусь» из первой главы книги. У нас тоже есть свои.
Мораль без морализаторства: копии с суффиксами – это история изменений, собранная вручную из худших материалов. Если вы делаете их – вы уже признали, что история нужна. Осталось взять нормальный инструмент.
Четыре действия, которых хватит на год
Весь рабочий набор помещается в четыре действия. Три вы знаете, четвёртое новое.
Фиксировать после каждого работающего шага. Правило вечера из части 0, теперь с уточнением про подпись. Подпись коммита – сообщение себе в будущее, и качество этого сообщения проверяется одним вопросом: поможет ли оно через месяц понять, что здесь произошло? «Правки» и «фикс» не помогут. «Поиск: пустой запрос показывает весь список» – поможет. Пишите подпись как строку из журнала, который будете читать при расследовании, – потому что ровно им она и станет.
Смотреть разницу перед фиксацией. git diff перед git add – двадцать секунд, за которые вы видите, что на самом деле уходит в историю. Это последний рубеж, на котором ловятся сюрпризы от агента: лишний файл, правка за границей задачи, случайно затянутый секрет. Про последнее будет отдельная глава, и привычка смотреть разницу – половина её содержания.
Возвращаться. Три случая, по нарастающей серьёзности. Испортили незафиксированное – git checkout -- . возвращает файлы к последней точке; вы делали это в упражнении про конфликт двух окон. Нужно откатить уже зафиксированное – git revert создаёт новый коммит, отменяющий выбранный: история при этом остаётся целой: отмена дописывается сверху, и это правильный путь. Нужно посмотреть, как выглядел проект неделю назад, – история позволяет перенестись в любую точку и вернуться. Механику двух последних не обязательно помнить наизусть: достаточно помнить, что они существуют, – команду подскажет агент, а вот о существовании выхода в панике не вспоминают, если не знали о нём заранее.
Ветка под опасную работу. Новое действие, и единственное по-настоящему новое понятие главы. Ветка – это параллельная линия истории: вы отходите от рабочего состояния в сторону, делаете там что угодно – большую переделку, рискованный эксперимент, – и рабочая линия остаётся нетронутой. Получилось – влили в основную. Провалилось – выбросили ветку целиком, основная и не заметила. Завести: git switch -c peredelka. Вернуться на основную: git switch main – или master, если ваша основная называется так; какая у вас, покажет git branch. Всё.
Ветка – это машина времени, направленная вперёд: вместо возврата после аварии вы заранее делаете так, что аварии негде случиться. Правило, когда она обязательна, простое: если словами задачи является «переделать» или «попробовать» – работа идёт в ветке. Достройка по маленькому шагу живёт и на основной линии.
Что это меняет в работе с агентом
Соберём главу в одно наблюдение. Все приёмы первой части – границы, маленькие шаги, проверки – работали на то, чтобы агент не наделал лишнего. История изменений добавляет второй контур: даже если наделал, это обратимо. Два контура вместе дают то состояние, в котором с агентом можно работать смело: страховка спереди, страховка сзади.
Инженерия, как мы сказали в начале части, – набор предохранителей. Этот – первый, самый дешёвый и самый недооценённый. Ни одна из следующих глав без него не работает: проверки бессмысленно чинить без возможности откатиться, секреты нельзя вычистить без понимания истории, выкладка без фиксированных точек – прыжок без страховки.
Если вы заказчик. Попросите показать историю изменений проекта за последний месяц – не код, просто журнал: даты, подписи, объёмы. Настоящая история при активной работе выглядит как десятки записей с внятными подписями. Пустая или редкая история при бурной деятельности означает, что работа идёт мимо неё – руками на сервере, копиями файлов, – и всё, что вы прочли в этой главе про возврат и показания, к вашему проекту не относится.
Упражнение. Заведите ветку и устройте в ней погром. git switch -c pogrom, затем попросите агента «переделать страницу списка радикально, на свой вкус» – ровно та задача, которую вся первая часть учила не давать. Посмотрите на результат, прогоните тесты, ужаснитесь или удивитесь. Потом вернитесь: git switch main – и убедитесь, что ваш проект стоит нетронутым, как будто ничего не было. Это упражнение учит двум вещам сразу: что ветка делает эксперименты бесплатными и что «радикально на свой вкус» – не задача. Обе стоят того, чтобы прожить их на себе.
Дальше в части – проверки: как один тест из части 0 вырастает в набор, который охраняет проект, пока вы занимаетесь другим. История и проверки вместе образуют ту пару, на которой стоит вся остальная инженерия: одна помнит, другие сторожат.
Проверки, которые работают за вас
У вашего проекта с первого вечера есть один тест: обращение отправлено – обращение сохранилось. Он уже отработал своё жалованье не раз: ловил учебную поломку, охранял границу правки, краснел в упражнении с погромом. Пришло время превратить одиночку в службу охраны – и по дороге разобраться, что вообще стоит охранять.
Сначала – зачем это, двумя фразами. Ручная проверка не масштабируется: к десятому сценарию полный обход занимает полчаса, которые вы перестанете тратить примерно на третий день. Автоматической не лень – она отработает после каждой правки, ночью, в чужих руках, и не стоит вам ни минуты внимания, пока зелёная.
С какого конца строить набор
В учебниках по тестированию рисуют пирамиду: много мелких проверок отдельных функций внизу, немного крупных сквозных наверху. Для команды с большим проектом это разумно. Для вечернего проекта мы советуем противоположное, и совет этот из практики: начинайте с самого широкого теста и дробите только там, где он перестаёт справляться.
Самый широкий – это сквозной путь главного сценария: тот самый, ради которого сервис существует. Для обращений: отправить через форму – найти в списке. Один такой тест проверяет разом форму, приём, сохранение, чтение и показ – пять слоёв одним прогоном. Мелкий тест отдельной функции проверяет функцию; сквозной проверяет, что сервис делает свою работу. Когда придётся выбирать, что написать первым, – выбор всегда за сквозным.
Наша собственная система живёт ровно на этом принципе, и мы назовём числа, чтобы был виден масштаб. Основной вид проверки у нас – обход настоящих страниц с проверкой содержимого: на один недавно построенный раздел приходится тридцать пять проверок, на соседний сервис – девять, полный прогон занимает минуты. Модульных тестов, любимых учебниками, у нас единицы. Это осознанное решение для нашего масштаба, и у него есть граница, назовём её прямо: когда внутри появляется сложная логика с ветвлениями – расчёты, права, состояния, – ей нужны свои мелкие тесты, потому что сквозной прогон не доберётся до каждой ветки. У вечернего проекта такой логики обычно ещё нет.
Из наших же прогонов – правило, которое важнее любой пирамиды: проверка, которая гоняется дольше, чем вы готовы ждать, не гоняется вообще. Набор, работающий три минуты, запускают после каждой правки. Набор, работающий полчаса, запускают перед сном, потом по пятницам, потом никогда. Скорость набора – условие его существования, удобство тут дело десятое.
Тест как контракт
Теперь главная мысль главы – та, из-за которой разговор о тестах идёт в книге про работу с агентами, хотя место ему, казалось бы, в учебнике по инструментам.
В первой части вы ставили агенту границы словами: «имена не переименовывай», «сохранение не трогай». Слова работают, пока агент их помнит, – а помнит он, как вы знаете, в пределах одной сессии, и то не всегда. Тест делает границу физической. Имя поля, поведение при пустом вводе, само существование сохранения – всё, что зафиксировано тестом, агент не может изменить тихо: тест покраснеет, и вы узнаете об этом через минуту, а через месяц, из жалобы человека, уже не узнаете, потому что узнали сейчас.
Это меняет статус тестов в проекте. Тест – письменный договор о том, что в проекте считается правдой; «проверка качества» тут слишком слабое слово. Каждый тест – пункт договора: обращения сохраняются; пустая форма не проходит; обработанное уходит вниз списка. Агент, нарушивший пункт, ловится машиной, без вашего участия. Помните формулу из главы о постановке – «названия часть договора, и договор пишет владелец»? Тест – это тот же договор, только с исполнением: нарушение не обсуждается – оно загорается.
Отсюда практическое правило для работы с агентом, замыкающее круг из части I: просите агента прогонять тесты после каждой правки – эта строка уже стоит в вашем файле правил – и не принимайте результат с красным. Красный после правки означает ровно одно из двух: либо агент вышел за границу (чаще всего), либо ваш договор устарел и его надо менять осознанно. Оба случая требуют вашего решения, и оба всплыли вовремя.
Три теста, которые окупаются первыми
Расширим ваш набор с одного до трёх. Больше на этом этапе не нужно; эти три закрывают то, что ломается первым.
Сквозной путь – уже есть с части 0: отправить, найти в списке.
Пустой и кривой ввод. Пятый вопрос первого вечера – «что увидит человек, если поле пустое» – закрывается ровно этим тестом. Что происходит, когда форму отправили без имени или вовсе пустой? Это первый край, о который спотыкаются настоящие люди, – и первое, что агент забывает, если не спросить. Тест короткий:
def test_pustaya_forma_ne_prohodit():
spisok_do = client.get("/spisok").text
otvet = client.post("/otpravit", data={})
assert otvet.status_code != 200
spisok_posle = client.get("/spisok").text
assert spisok_do == spisok_posle
Смысл: пустая отправка не притворяется успешной, а список до и после неё совпадает – мусорная запись не появилась. Обратите внимание на приём со снимком «до»: тест не делает предположений о том, что уже лежит в базе, он сравнивает состояние с самим собой. Наша первая версия этого теста предполагала пустую базу – и падала на исправном сервисе, потому что сосед-тест успевал создать запись раньше. Тесты не должны зависеть от порядка, в котором их гоняют, и снимок «до» – самый простой способ этого добиться.
И заметьте, чего тест не требует: никакого конкретного красивого поведения. Только минимум – не соврать и не намусорить. Красивое сообщение об ошибке закажете агенту потом, отдельной задачей с границей.
Состояние. Отметка «обработано» делает своё дело: тест создаёт обращение, отмечает, проверяет, что статус изменился. Это охрана самой хрупкой части сервиса – той, где данные меняются.
Три теста, секунды на прогон, и у проекта появилось то, что можно всерьёз называть охраной: путь, края, изменение состояния. Всё остальное достраивается по мере роста – по правилу «сломалось то, что не было покрыто, – покрой и живи дальше».
Два ритуала
Первый вы знаете с первого вечера, теперь он получает статус закона: новый тест обязан покраснеть у вас на глазах. Сломайте то, что он проверяет, увидьте красное, верните. Тест, который ни разу не краснел, не проверен сам – а к чему приводят непроверенные проверки, мы подробно разберём в части про эксплуатацию, на собственной истории, которая нам дорого обошлась.
Второй ритуал новый, и он про дисциплину в неприятный момент. Рано или поздно тест покраснеет «не вовремя»: вы заняты другим, правка вроде хорошая, а этот красный мешает. Соблазн – отключить его «на время». Так вот: у отключённого «на время» теста нет ни одного известного случая обратного включения. Выбор в этот момент настоящий, и он бинарный: либо разобраться сейчас – красный тест показывает либо поломку, либо устаревший пункт договора, и то и другое важно, – либо удалить тест осознанно, коммитом с подписью, почему договор расторгнут. Отключение – это удаление, которое врёт, что оно временное. А набор, в котором есть «ну этот красный, это нормально», перестаёт быть охраной целиком: следующий настоящий красный утонет в привычном.
Если вы заказчик. Вопрос «есть ли у вас тесты» бесполезен: ответ всегда «да». Работают два других. «Сколько времени занимает полный прогон и когда он запускался последний раз?» – рабочий набор гоняется ежедневно и укладывается в минуты. И «покажите, как он краснеет»: пусть при вас сломают что-нибудь и прогонят. Зелёный набор, который не умеет краснеть, вам в этой книге ещё встретится.
Упражнение. Доведите набор вечернего проекта до трёх тестов из этой главы – сквозной у вас есть, два оставшихся поставьте агенту как задачи по всем правилам части I, с границей и критерием. Потом главное: устройте поломку, которая уронит ровно два теста из трёх, – например, сломайте сохранение. Посмотрите на выдачу: два красных, один зелёный, и по тому, какие именно, видно, где поломка. Это второй навык чтения тестов – по рисунку красного ставить диагноз, не открывая код.
История помнит, проверки сторожат. Следующая глава – про то, что они охраняют на самом деле: данные. Их особенность в том, что они переживают любой код, накапливают любые ошибки и не прощают решений, принятых мимоходом, – включая те, которые за вас принял столбец со значением по умолчанию.
Данные переживут код
В главе о чтении результатов мы назвали слой данных самым дорогим в исправлении и обещали объяснение. Вот оно, целой главой, и открывается оно неравенством, на котором держатся все решения этой главы.
Код можно переписать. Целиком, с нуля, хоть трижды – старый выбрасывается, новый занимает его место, и через минуту никто не вспомнит, каким был прежний. Данные переписать нельзя. Их можно только переносить – аккуратно, с сохранением каждого пришедшего обращения, потому что обращения написали люди, и заново их не напишет никто. Код – ваша работа, её можно повторить. Данные – след чужих действий, они невоспроизводимы.
Отсюда правило, переворачивающее привычную интуицию: решения о данных – самые важные решения проекта. Как называется поле, что в нём хранится, что стоит по умолчанию – всё это переживёт любой код, который вокруг напишут и перепишут. Агент предложит структуру за секунды; проверять её стоит внимательнее всего остального, потому что цена переделки здесь измеряется переносом накопленного, а не перегенерацией.
Структура и её изменение
Структура хранения – по-взрослому «схема» – это решение о том, из чего состоит запись: у обращения есть имя, контакт, текст, время, статус. Вы приняли это решение в первый вечер, в задаче агенту, и с тех пор оно молча держит на себе всё: форму, список, тесты.
Пока проект живёт, структура догоняет жизнь: понадобится телефон отдельным полем, пометка «откуда пришёл», приоритет. И здесь появляется единственное новое понятие главы – миграция. Слово звучит серьёзнее, чем предмет: миграция – это изменение структуры, записанное отдельным действием, которое можно выполнить, проверить и повторить на другой копии базы. Противоположность миграции – «я там руками поправил в базе»: изменение, которое нигде не записано, ни на какой другой копии не воспроизведётся и через месяц станет загадкой.
Для вечернего проекта миграция – это маленький файл:
# migracia_001_telefon.py
import sqlite3
db = sqlite3.connect("zayavki.db")
db.execute("ALTER TABLE obrasheniya ADD COLUMN telefon TEXT NOT NULL DEFAULT ''")
db.commit()
print("Поле telefon добавлено")
Запустили один раз – поле появилось, старые записи получили пустое значение, ничего не потерялось. Именно один раз: при повторном запуске миграция откажет с ошибкой «такой столбец уже есть», и это нормальное поведение, даже полезное – она отказывается применяться дважды. Не пугайтесь, встретив этот отказ: он означает, что дело уже сделано. Файл остаётся в проекте и в истории изменений: это документ о том, что структура менялась, когда и как. Задача агенту на такое изменение ставится по всем правилам части I, с одним обязательным добавлением в критерий проверки: «существующие записи сохраняются и получают такое-то значение нового поля». Про судьбу старых записей агент сам не спросит – а это, как сейчас увидите, главный вопрос.
Значение по умолчанию принимает решения за всех
История, ради которой написана эта глава. Числа настоящие.
В одном из наших проектов у профилей людей есть настройка «показывать мои контакты на публичных страницах». Хорошая, правильная настройка – каждый решает сам. В июле при проверке выяснилось: настройка включена у восемнадцати тысяч четырёхсот семидесяти четырёх профилей из восемнадцати тысяч четырёхсот семидесяти пяти. У одиннадцати с половиной тысяч из них был заполнен телефон, и он отдавался на публичных страницах любому желающему.
Восемнадцать тысяч человек не принимали такого решения. Его не принимал и никто из нас: не было ни совещания, ни строчки в задаче, ни момента, когда кто-то сказал «показываем контакты всех». Решение приняла одна строка в описании структуры – значение по умолчанию у столбца, поставленное при его создании. Каждый новый профиль получал «показывать: да» просто потому, что так был устроен столбец, и восемнадцать тысяч молчаливых «да» накопились сами, без единого злого умысла.
Исправляли аккуратно, в обратную сторону: согласие сброшено всем, телефоны в данных сохранены – люди смогут включить показ сами, теперь уже действительно сами. Значение по умолчанию перевёрнуто, формы перестали его подставлять.
Правило из этой истории стоит запомнить дословно: значение по умолчанию – это решение, которое вы принимаете за всех будущих пользователей сразу. Каждый, кто не выберет сам, получит ваше «по умолчанию» – а не выбирают почти все. Поэтому у каждого умолчания в структуре должен быть ответ на вопрос «почему именно так», и для всего, что касается приватности, согласий и денег, ответ по умолчанию один: выключено, пока человек не включил. Обратное умолчание в этих областях – юридический риск, который вы создаёте одной строкой и накапливаете молча, со скоростью регистраций.
И заметьте, как это связано с началом главы. Ошибку в коде мы бы поправили правкой. Ошибка в умолчании столбца за месяцы превратилась в восемнадцать тысяч записей, каждая из которых выглядит как осознанный выбор человека, – и отличить настоящие «да» от автоматических уже невозможно. Данные накапливают последствия решений, включая те, которых никто не принимал.
Две проверки, о которых забывают
Перезапуск. Ритуал из первого вечера, повторим одной строкой, потому что он относится сюда: данные, которые не пережили остановку и запуск сервиса, данными не являются. Проверяется за минуту, входит в привычку навсегда.
Одновременность. Вопрос, который не приходит в голову, пока сервисом пользуется один человек: что произойдёт, если двое отправят форму в одну и ту же секунду? Сохранятся оба? Не перепутаются ли номера? Для вашего сервиса в нынешнем виде ответ благополучный: база выстраивает записи в очередь сама. Но вопрос обязан войти в ваш список, потому что ответ не всегда благополучный, а проверяется он просто – спросите агента: «что произойдёт при двух одновременных отправках, покажи, где это обрабатывается». Знакомый приём маршрута из части I, применённый к самому хрупкому месту. Сервисы падают на одновременности ровно тогда, когда начинают быть нужными, – в момент наплыва.
Остался третий вопрос о данных – где лежит их копия и переживут ли они смерть диска. Ему посвящена отдельная глава в части про эксплуатацию.
Если вы заказчик. Один вопрос из этой главы стоит задать про любой сервис, который собирает данные людей: «какие настройки приватности и согласий стоят по умолчанию – и почему?» Ответ «по умолчанию всё включено, так удобнее» означает, что подрядчик создал вам юридический риск и назвал его удобством. Правильное умолчание для согласий – выключено; всё остальное должно иметь письменное обоснование. Проверить можно без техники: зарегистрируйтесь на собственном сервисе и посмотрите, на что вы «согласились», не нажав ничего.
Упражнение. Добавьте в свой сервис поле «телефон» через миграцию из этой главы – но сначала создайте пару обращений, чтобы было чему выживать. Прогоните миграцию, откройте список: старые обращения на месте, у них пустой телефон, новые сохраняются с телефоном. Потом второй заход, важнее первого: попросите агента добавить поле «согласие на ответ по почте» – и посмотрите, какое значение по умолчанию он выберет, если вы не скажете. Наш опыт подсказывает, что ответ вас заинтересует. Исправьте на правильное – теперь вы знаете, какое оно.
Данные в порядке: структура записана, умолчания осознаны, изменения оформлены миграциями. Следующая глава – про то, что охраняет вход в эти данные и в весь проект: секреты и права. Там будут три наши истории по нарастающей и одно правило, которое дороже всех замков: права проверяются попыткой, а не чтением настроек.
Секреты и права
Глава о безопасности в книге для начинающих обычно выглядит как парад страшилок: взломы, хакеры, чёрные ходы. Наша будет скучнее и полезнее, потому что нам есть на что опереться: мы проверяли собственное хозяйство и нашли там настоящие дыры, без всякого учебника. Ни одна из них не была хитрой уязвимостью. Все три были одного сорта: вещь, которую никто не проверил, потому что «ну там же наверняка нормально».
Самая частая дыра проектов, собранных с агентом, устроена именно так. Это ключ, лежащий в открытом виде, и права, которых никто не проверял. Обе темы закрываются за вечер, и обе – про привычку, а не про знания.
Секреты: одно правило и пять строк
Секрет – это любая строка, знание которой даёт доступ: пароль базы, ключ платёжного сервиса, токен бота, которым ваш сервис пишет вам в мессенджер. Правило обращения с ними одно, и оно абсолютное: секрет живёт в отдельном файле настроек и никогда не попадает в историю изменений.
Почему так строго – из-за свойства истории, которое в главе про git мы хвалили: она помнит всё. Секрет, хотя бы раз зафиксированный в коммите, остаётся в истории навсегда – даже если следующим коммитом вы его стёрли. Любой, у кого окажется копия репозитория, достанет его из прошлого одной командой. Отсюда и второе правило, неприятное: секрет, побывавший в истории, считается утёкшим. Лечится это только заменой самого секрета – выпустить новый ключ, отозвать старый. Стереть след нельзя, можно лишь сделать след бесполезным.
Механика защиты – пять строк. Файл .env в корне проекта, в нём настоящие значения:
TG_TOKEN=здесь-настоящий-токен
Строка в .gitignore, чтобы файл никогда не попал в историю:
.env
И файл-образец .env.example – та же структура, пустые значения, живёт в истории как документация:
TG_TOKEN=
Код читает значения из окружения – вы уже делали это с именем файла базы в части 0, приём тот же. Одна тонкость: сам по себе файл .env процесс не читает, его содержимое надо загружать в окружение при запуске. Это делается готовой маленькой библиотекой или парой строк – поставьте агенту задачу «загружай .env при старте» с проверкой «значение из файла видно сервису». Новый человек – или вы на новой машине – копирует образец в .env, вписывает свои значения, и всё работает. Осталась одна привычка, замыкающая защиту: git diff перед фиксацией, знакомый вам по главе об истории. Агент может вписать ключ прямо в код – «чтобы работало»: злодейства тут нет, есть кратчайший путь. Ваша задача – поймать это глазами до коммита, и просмотра разницы для этого достаточно.
Насколько это распространённая беда, видно по отраслевым числам, и все они из одного места – годового отчёта GitGuardian о разрастании секретов за 2026 год. За 2025 год на публичных площадках нашли 28,65 миллиона свежих секретов, лежащих прямо в коде: рост на треть за год и самый большой скачок за всё время наблюдений. Изменения, сделанные с помощью ИИ, содержат утёкшие секреты примерно вдвое чаще обычных. Утечки ключей к самим сервисам ИИ выросли на 81 процент – новые ключи потекли раньше, чем сложились привычки их хранить. Агент прекрасно пишет код. Хранить тайны он не помогает.
Три наших случая, по нарастающей
Теперь обещанные дыры из собственного хозяйства. Мы расскажем их лестницей, потому что каждая следующая была больнее предыдущей, а мораль у всех одна.
Ключи в коде. При разборе одного служебного скрипта обнаружилось, что в нём прямо в тексте лежат токен бота, ключ к внешнему сервису и пароль базы данных. Скрипт старый, написан в спешке, «потом переделаем». Дальше вы знаете правило: просто вынести ключи в настройки уже недостаточно – они считаются утёкшими, и всё найденное отправилось на замену. Час работы, которого не было бы, займи вынос ключей пять минут в день написания.
Замок, который не запирал. Настраивая резервное копирование, мы положили служебный архив в облачное хранилище с закрытыми правами на самом файле – и, по правилу из этой главы, тут же проверили попыткой. Проверка показала: архив скачивался без всякого пароля. Права на файле были закрыты. Но у хранилища была своя политика доступа, уровнем выше, и она перекрывала права файла. Мы смотрели на один замок и не знали о существовании второй двери. Дыру закрыли в ту же минуту, хранилище для копий завели отдельное и закрытое – но урок остался: без привычки проверять попыткой мы бы узнали о второй двери не от себя.
Хранилище, открытое на запись. Самый тяжёлый случай, и нашли мы его по следам предыдущего, проверяя уже всё подряд. Политика одного из хранилищ разрешала анонимному пользователю запись и удаление. Кто угодно из интернета мог загрузить туда что угодно – или стереть всё: больше восьми гигабайт работ, загруженных людьми. Проверено практикой: пробная анонимная запись прошла успешно, пробное анонимное удаление тоже. Эта настройка досталась нам вместе с проектом и существовала неизвестно сколько – до того дня никто не проверял её попыткой.
Мораль одна на три случая, и мы держим её как закон: права проверяются попыткой, а не чтением настроек. Настройки говорят, как должно быть. Попытка показывает, как есть. «Там закрыто» – это гипотеза, как отчёт агента из части I; документом является отказ, полученный при настоящей попытке войти без ключа, записать без права, скачать без пароля. Такая попытка занимает минуту и не требует никакой квалификации – только привычки её делать.
Права внутри приложения
Вторая половина темы – права внутри самого сервиса: кто что может. И здесь надо знать особенность агентов, о которой мы говорили во вступлении к этой части: проверки безопасности проходит 56 процентов сгенерированного кода, и от поколения к поколению моделей эта доля не растёт. Самая типичная пропажа – ровно проверка прав: код исправно делает то, что просили, для всякого, кто попросит. Страница «отметить обращение обработанным» из вашего проекта выполнит отметку для любого, кто откроет адрес, – потому что в задаче не было слов «а кто, собственно, имеет право отмечать».
Пока сервис учебный и смотрит только на вас, это допустимо – и это допущение стоит записать в список ограничений, он для того и существует. Но правило должно поселиться в голове до того, как появятся настоящие пользователи: у каждого действия, которое меняет данные, должен быть ответ на вопрос «кто имеет право это делать» – и проверка этого ответа в коде. Агент сам не спросит. Спросить – ваша строка в задаче, и в файле правил ей самое место: «для действий, меняющих данные, всегда спрашивай меня, кто имеет право».
Пять вопросов о безопасности, на которые владелец сервиса отвечает без подготовки. Где лежат секреты и нет ли их в истории изменений? Кто может писать в хранилище – проверено ли попыткой? Какие действия меняют данные и кто имеет на них право? Что записывается в журнал – увидите ли вы чужую попытку? Что вы будете делать, если ключ утечёт, – и сколько времени займёт замена? Пятый вопрос особый: ответ на него готовят заранее, потому что искать его в момент утечки поздно.
Если вы заказчик. Из всей главы вам достаточно двух вопросов. «Проверялись ли права доступа попыткой – покажите, как выглядела проверка». И «что произойдёт, если ключ от такого-то сервиса утечёт, – сколько времени займёт замена». Первый отличает настроенную безопасность от предполагаемой. Второй показывает, готовились ли к плохому дню заранее. Слова «у нас всё закрыто» без показанной попытки вы уже умеете переводить.
Упражнение. Два действия по этой главе. Первое: заведите в проекте .env и .env.example по образцу, перенесите туда имя файла базы, добавьте .env в .gitignore – и проверьте попыткой: git status не должен показывать .env как новый файл, а git check-ignore .env должен подтвердить, что файл под запретом. Второе, интереснее: попросите агента «добавь отправку уведомления в мессенджер о новом обращении» – и посмотрите, куда он положит токен. Если в код – вы только что поймали самую массовую дыру индустрии у себя дома, бесплатно и до последствий. Перенесите в .env и добавьте строку в файл правил.
Секреты убраны, права проверены попыткой. Следующая глава – о месте, где всё это работает: чем «работает у меня» отличается от «работает у людей», что такое контейнер и почему мы после двух лет на визуальном конструкторе переписали всю автоматизацию на обычный код – история, в которой агент сместил баланс сильнее, чем нам казалось.
Где живёт запущенный проект
Есть фраза, которую произносил каждый, кто что-нибудь собирал: «у меня работает». Обычно с интонацией законного недоумения – работает же, сами смотрите. Эта глава о том, почему фраза ничего не гарантирует и как сделать, чтобы гарантировала.
«У меня работает» – это свойство вашей машины, а не проекта. На вашей машине стоит нужная версия языка, установлены пакеты, лежит файл настроек, задана переменная окружения, о которой вы забыли через час после того, как задали. Проект работает не сам по себе – он работает в среде, и среда эта собиралась месяцами, незаметно, слоями. Перенесите проект на другую машину – и выяснится, сколько слоёв не переехало; мы это проверили дорого. Между «работает у меня» и «работает где угодно» лежит отдельная инженерная задача, и имя ей – воспроизводимость.
Воспроизводимость: среда как список
Задача решается по-разному на разном масштабе, и мы пойдём от простого.
Минимальный уровень, обязательный для всех, у вас уже почти есть: среда описана словами. README перечисляет команды установки и запуска; .env.example перечисляет настройки; список пакетов лежит в проекте. Проверка этого уровня вам знакома по упражнению «разверни с нуля»: если по описанию проект поднимается на чистой машине – среда воспроизводима, все слои названы.
Уровень выше – среда описана исполняемо: контейнер. Слово звучит технологичнее, чем предмет. Контейнер – это упакованная среда: в одном месте записано, какая нужна система, какие пакеты, какие настройки, и из этой записи собирается одинаковая коробка на любой машине. Разница с README принципиальная: README читает и выполняет человек, ошибаясь и пропуская, а запись контейнера выполняет машина, одинаково всегда. «У меня работает» превращается в «работает в коробке, а коробка собирается где угодно».
Вечернему проекту контейнер не обязателен, и мы не будем изображать обратное: на одном компьютере с одним пользователем README и .env.example закрывают вопрос. Контейнер становится нужен, когда проект переезжает на сервер – там он наводит порядок, которого иначе не добиться. Но одно правило о контейнерах нужно знать до всякого сервера, потому что оно про сохранность, и мы за него заплатили.
У контейнера есть внутренность – и она одноразовая. Коробку пересоздают: при обновлении, при перезагрузке, при переезде – и всё, что жило только внутри неё, исчезает вместе со старой коробкой. Поэтому у всего, что должно уцелеть, должно быть постоянное место снаружи: данные, загруженные людьми файлы, настройки. Внутри коробки живёт только сам проект, который собирается заново из записи. Правило звучит очевидно ровно до того дня, когда выясняется, что правки целого дня существуют только внутри работающей коробки, – с нами такое случилось, и полный рассказ об этом впереди, в части про эксплуатацию. Пока держите наготове вопрос, который стоит задавать любому контейнеру: что в тебе умрёт вместе с тобой?
Почему мы ушли от конструктора
Теперь обещанная история про выбор, где вообще жить автоматизации. Она наша собственная, заняла три месяца и стоит того, чтобы рассказать её с числами.
Два года наша автоматизация жила в визуальном конструкторе – инструменте, где рабочий процесс собирается мышью из кубиков: «получить данные», «преобразовать», «отправить». Выбор был правильный: конструктор дал скорость тогда, когда не было ни агентов, ни навыка писать код. Процессов накопилось около шестидесяти – публикации, сборы данных, уведомления, целая фабрика в кубиках.
К началу лета из шестидесяти работающих осталось два. Остальные умерли, переехали или превратились в загадки. А в июле мы вывели конструктор совсем: два последних процесса переписаны обычными скриптами, порты закрыты, кубики – в архив. Причины стоит перечислить по журналам, как было, потому что каждая – урок про целый класс инструментов, конкретное имя тут ни при чём.
Песочница не выпускала наружу. Внутри конструктора код исполняется в ограниченной среде: в нашей версии из неё не было доступа к переменным окружения – тем самым, где по прошлой главе живут секреты. Ключи приходилось протаскивать окольными путями, и каждый окольный путь – это дыра из главы о секретах, встроенная в архитектуру.
Кубики не лежат в истории изменений. Процесс правится мышью, и после правки нет ни разницы «было – стало», ни подписи, ни возможности вернуться. Вся первая глава этой части – машина времени, показания, граница сделанного – к конструктору неприменима: у него нет истории, только текущее состояние и память человека, который клацал.
Проверок нет и прогона вхолостую нет. Проверить процесс можно одним способом – запустить по-настоящему и посмотреть. Что это означает для дисциплины, вы понимаете по главе о тестах: контракта нет, границу держать нечем, каждый запуск – эксперимент на работающем.
Сторожа не переживали перемен. Наша канарейка – сигнал, следящий, что процесс жив, – после одного из переездов выла триста пятьдесят восемь раз подряд по процессу, который давно умер и был заменён. Триста пятьдесят восемь сообщений о покойнике – это уже не наблюдение; чем кончаются тревоги, на которые невозможно реагировать, разберёт часть про эксплуатацию.
И главное, из-за чего чаша окончательно склонилась: пришли агенты. Агент пишет код на обычном языке блестяще – и совершенно беспомощен в чужом визуальном конструкторе: он не может ни прочитать процесс из кубиков, ни надёжно его поправить, ни прогнать тесты, которых нет. Конструктор, который годами был ускорителем, за один сезон превратился в тормоз: всё, чему учит эта книга – задачи с границами, тесты как контракт, история, файл правил, – работает с кодом и не работает с кубиками.
Что дал переезд, одной строкой на пункт: каждый бывший процесс стал скриптом – под историей изменений, с тестом, с прогоном вхолостую, с журналом, который можно читать. Те самые предохранители этой части, все разом, просто сменой места жительства.
Урок для вас в этой истории неожиданно оптимистичный. Нам конструктор был нужен, потому что в своё время он был самым быстрым способом начать. У вас этого этапа нет: агент даёт ту же скорость старта сразу в коде – со всеми предохранителями, которые к коду прилагаются. Ваш вечерний сервис тому доказательство: он собран за вечер и при этом под историей, с тестами и настройками по правилам. Два года назад такое сочетание скорости и порядка было недоступно никому.
Если вы заказчик. Вопрос «на чём это построено» стоит задавать ради одного свойства, названия тут вторичны: «покажите, как проект разворачивается на чистой машине, и где лежит история его изменений». Решения на конструкторах и платформах-сборщиках проверяйте тем же вопросом – у многих из них ответ «никак и нигде», и это значит, что всё, что вы прочли в этой части про историю, тесты и воспроизводимость, к вашему заказу не относится. Иногда это осознанный компромисс ради скорости. Осознанным он становится, только если вы задали вопрос.
Упражнение. Составьте опись среды своего проекта: один файл, где перечислено всё, что нужно для его жизни, – версия языка, команды установки, содержимое .env.example, команда запуска, команда тестов. Потом проверьте опись делом – в новой папке, по списку, ничего не вспоминая. Всё, что пришлось вспомнить сверх написанного, – дыра в описи, допишите. Этот файл – будущий фундамент и для контейнера, если он понадобится, и для комплекта передачи: среда, существующая списком, переносима; среда, существующая привычкой, умирает вместе с машиной.
Осталось вывести проект к людям – и следующая глава про этот шаг: чем выкладка отличается от «загрузить файлы», какие решения она требует и на какие грабли здесь наступили мы, включая правку, которая доехала до сервера и не доехала до посетителей.
Выкладка
Всё это время ваш сервис жил на адресе, начинающемся со ста двадцати семи, – домашнем адресе, который видите только вы. Выкладка – день, когда у проекта появляется настоящий адрес и настоящие посетители. Со стороны это выглядит как «загрузить файлы на сервер». На деле это серия решений, и у каждого есть цена. Хорошая новость: решений конечное число, и все они собираются в чек-лист на одну страницу, которым глава и закончится.
Что меняется в момент выкладки
Меняется всё, что было допущением. Дома сервис слушал только вас – теперь его открывает кто угодно, включая роботов, которые начнут стучаться в первые же часы, не спрашивая разрешения. Дома ошибка показывала подробности на экране – наружу подробности показывать нельзя: текст ошибки рассказывает про устройство проекта больше, чем вы хотели бы рассказать постороннему. Дома соединение было простым – наружу оно обязано быть защищённым: замочек в адресной строке давно перестал быть украшением, без него браузеры пугают посетителей, а формы предупреждают о небезопасности.
И первая ловушка, о которой надо сказать прямо, потому что в неё попадает каждый второй: сервер, которым вы пользовались всю книгу, – отладочный. Команда с флагом перезагрузки, которую вы запускали каждый вечер, создана для разработки: она удобная, разговорчивая и совершенно не предназначена держать настоящих посетителей – про это прямо написано в её собственной документации, которую никто не читает. Для выхода наружу тот же проект запускается рабочим сервером – это другая команда, а не другой код, и агент настроит её по одной просьбе: «подготовь запуск для настоящих посетителей, отладочный режим выключи». Проверить, что просьба выполнена, просто: сломайте что-нибудь и посмотрите на страницу ошибки – посетительская версия молчит о подробностях.
Выкладка как повторяемое действие
Главный принцип главы, и он вам уже знаком по духу всей части: выкладка – это скрипт. Ритуал из ручных шагов – временное состояние, которое надо пережить один раз.
Первая выкладка руками простительна. Вторая руками – уже риск: шагов десяток, забыть один – нормальная человеческая ошибка, и проявится она не сразу. У нас выкладка одного из проектов – это один скрипт, который делает всё сам: доставляет файлы, проверяет, что код цел, обходит десяток настоящих страниц и убеждается, что они отвечают, фиксирует состояние в истории. Ручные шаги оттуда изгонялись по одному, и у каждого изгнания была причина – однажды пропущенный шаг. Скрипт не забывает, не устаёт и делает одиннадцатую выкладку так же, как первую; человек – нет.
Для вечернего проекта скрипт выкладки пишет агент – это задача по всем правилам части I, и в критерий проверки просится главное: «после выкладки скрипт сам открывает главную страницу и проверяет, что сервис отвечает». Выкладка без проверки после – это отправленное письмо без вопроса «дошло?».
Теперь три наших грабли, чтобы вам не собирать свои.
Файл без версии в адресе. Браузеры запоминают загруженные файлы, чтобы не качать их заново, – и это прекрасно ровно до первой правки: вы выложили новую версию, а часть посетителей ещё дни получает старую из собственного кеша. Мы наступили на это всерьёз: правка доехала до сервера и не доехала до людей. Лекарство – пометка версии в адресе подключаемого файла: изменилась пометка – браузер берёт свежее. Агенту это ставится одной строкой в файл правил.
Выкладка целиком поверх чужого. Если на сервере живёт что-то, чего нет в вашей папке, – чужой раздел, загруженные людьми файлы, соседний проект, – выкладка «всё целиком» это чужое сотрёт или перекроет. У нас однажды в дереве сборки лежал посторонний файл, и полная выкладка сломала бы весь сайт, – спасло то, что выкладывали точечно. Правило: скрипт выкладки явно знает, что он кладёт и чего не трогает, и список «не трогать» в нём записан, как граница в задаче агенту.
Пометка «не трогаем». Файл, исключённый из выкладки с пометкой «не трогаем, он особенный», – это мина замедленного действия: он живёт без истории, без пути доставки исправлений, и однажды переживает всех, кто помнил, почему он особенный. Правило здесь: исключение из выкладки – это долг, и ему место в списке ограничений с датой, а не в чьей-то памяти.
Закон приходит вместе с посетителями
Раздел, которого нет в других книгах этого жанра, а должен бы быть в каждой. Пока сервис жил на домашнем адресе, он был вашим личным делом. С настоящими посетителями он становится делом публичным – и на него начинает действовать законодательство. Мы не юристы и заключений не даём; наша задача скромнее – назвать вопросы, для которых «мы про это не подумали» перестаёт быть ответом ровно в момент выкладки.
Где стоит сервер. Если ваш сервис собирает данные россиян – а форма обращений с именем и контактом уже собирает, – закон требует хранить эти данные на серверах в России. Строгость тут вовсе не теоретическая: один из наших проектов, с данными восемнадцати с половиной тысяч человек, жил на зарубежном сервере – так исторически сложилось, и это было прямым нарушением. Переезд на российский сервер стал отдельной операцией со своими потерями, о которых расскажет часть про эксплуатацию. Урок дешевле нашего: страна сервера выбирается до того, как в базу упадёт первая настоящая запись. После – это уже переезд, а не выбор.
Политика и согласие. Форма, собирающая хоть что-то о человеке, требует двух вещей: документа на сайте, объясняющего, что вы делаете с данными, и согласия человека на это. Про согласие вы уже знаете главное из главы о данных – его умолчание должно быть выключенным. Добавим второе требование, из главы о правах: согласие должно реально сохраняться, и проверяется это попыткой – отправьте форму с отметкой и найдите отметку в базе. У нас есть история о согласии, которое выглядело собранным и не сохранилось ни разу, – она впереди. Шаблон политики вам подготовит агент, а вот проверить, что согласие доезжает до хранилища, можете только вы.
Домен. Требования к владельцам доменов растут – подтверждение личности через государственные сервисы уже становится нормой для российских зон. Практическое следствие одно, и вы встретите его снова в разговоре о передаче проектов: домен оформляется на владельца проекта, на его настоящие данные. Домен «на подрядчика» или «на кого получилось» – это мина, которая срабатывает при первом же ужесточении правил или первой ссоре.
Раздел получился короткий, и это осознанно: подробности меняются, вопросы остаются. Держите три вопроса – где сервер, есть ли политика с настоящим согласием, на кого домен – в чек-листе каждой выкладки, и большую часть неприятностей этого рода вы не встретите никогда.
Закрыт – значит закрыт осознанно
Последний сюжет главы – про промежуток, когда проект уже снаружи, но ещё не готов к гостям.
Это штатное состояние: сайт собирается, наполняется, проверяется на настоящем адресе – а поисковикам и случайным прохожим там пока делать нечего. Для этого существует вежливая табличка «закрыто»: файл, который просит роботов не заходить, и пометка на страницах «в указатели не вносить». У нас такой режим – нормальная часть запуска, и один из проектов прямо сейчас стоит закрытым, дожидаясь наполнения.
Опасность у таблички одна: о ней забывают. Сайт готов, посетителей ждут, реклама запущена – а табличка «закрыто» висит, и поисковики послушно обходят сайт стороной месяцами. Обнаруживается это обычно вопросом «почему нас никто не находит», и хорошо, если быстро. Правило: закрытость – это запись в списке ограничений с условием снятия («открыть, когда будет двадцать страниц»), и у неё, как у всякого немого состояния, должен быть сторож – хотя бы строка в чек-листе выкладки: «проверить, не закрыт ли сайт, если он должен быть открыт».
Сколько это стоит держать
Вопрос, который возникает у всякого владельца ровно в этот момент – когда проект переехал наружу и стал требовать денег каждый месяц. Разберём по статьям, с оговоркой: числа проверены в июле 2026 года и годятся как порядок величин, а не как прейскурант.
Место, где живёт проект. Простейший сервер для вечернего проекта – от двух-трёх сотен рублей в месяц; этого хватает на сайт с формой и небольшой базой, и хватит надолго. Один такой сервер спокойно держит не один проект, а десяток: наша сеть из двух десятков сайтов живёт на трёх машинах, то есть в расчёте на проект выходят десятки рублей. Готовые площадки для сайтов берут больше, зато не требуют возиться с настройкой; выбор здесь между деньгами и вашим временем.
Имя. Домен – несколько сотен рублей в год при регистрации, продление обычно дороже первого года, и это стоит узнать заранее. Отдельная деталь для России: с 1 сентября 2026 года операции с доменами в зонах .ru, .рф и .su – регистрация, продление, смена регистратора, изменение записей – выполняются только после подтверждения личности владельца через государственные услуги (закон от 29 декабря 2025 года). Заводите на себя – ту же мысль вы уже читали в разделе про закон, и денег она не стоит вообще, а стоимость ошибки измеряется потерей имени.
Агент. Здесь разброс самый большой: подписка на инструмент – от нескольких сотен до нескольких тысяч рублей в месяц, оплата по расходу – от копеек за небольшую задачу. Для вечернего проекта после первой сборки расход падает: содержание требует агента реже, чем создание. Полезная привычка – смотреть, сколько ушло за неделю, пока сумма маленькая, а не после месяца забывчивости.
Хранение копий. Самая дешёвая статья и самая обидная для экономии: место под резервные копии стоит десятки рублей в месяц, а стоимость их отсутствия вы знаете из истории про хранилище, которое разрешало анонимную запись.
Итого вечерний проект снаружи обходится примерно в тысячу рублей в месяц, из которых больше половины – инструмент, а не инфраструктура. Главный расход при этом не денежный: содержание требует вашего внимания, и в час-два в месяц оно обходится дороже сервера. Это нормальная цена, но она должна быть посчитана до запуска, а не обнаружена после.
Чек-лист выкладки. Семь пунктов на страницу, вешается рядом с рабочим местом. 1. Рабочий сервер вместо отладочного, ошибки наружу молчат. 2. Соединение защищено. 3. Секреты в настройках сервера, в истории их нет. 4. Скрипт выкладки: кладёт своё, не трогает чужое, проверяет после. 5. Версии в адресах подключаемых файлов. 6. Закон: сервер в нужной стране, политика на месте, согласие сохраняется, домен на владельце. 7. Табличка «закрыто» снята – или висит осознанно, с условием снятия.
Если вы заказчик. Два вопроса при приёмке выкладки. «Покажите, как выглядит выкладка новой версии» – ответ должен быть скриптом или хотя бы записанной последовательностью, а слова «ну, копируем файлы» означают, что каждая выкладка – лотерея. И «в какой стране стоит сервер и на кого оформлен домен» – два факта, которые дешевле всего узнать до подписания и дороже всего после расставания.
Упражнение. Выложите вечерний проект туда, где его увидит другой человек, – подойдёт самый простой сервер за пару сотен рублей в месяц; агент проведёт вас по шагам, а чек-лист главы удержит от пропусков. Потом попросите кого-нибудь пройти главный сценарий с телефона и посмотрите на два места: дошло ли обращение до вашего списка – и что человек сказал про скорость и удобство. Первый настоящий посетитель на настоящем адресе даёт больше знаний о проекте, чем неделя домашней отладки. Обращение от него, кстати, останется в вашей базе первым настоящим – берегите его.
Осталась последняя глава части – о том, ради чего всё это делалось: чтобы сервис давал результат. Работающий и приносящий пользу – разные состояния, и между ними лежит система из трёх звеньев: вас должны найти, вам должны поверить, и вы должны видеть, что происходит.
Чтобы это дало результат
Часть II начиналась с обещания, что инженерия решает, доживёт ли проект до второго месяца. Все главы до этой держали обещание в одном смысле: проект не потеряется, не сломается молча, не откроет дверь посторонним. Последняя глава – про второй смысл, ради которого всё затевалось: проект должен приносить пользу, ради которой его делали. Заявки, читателей, покупателей, сэкономленные часы – у каждого своё, но у всех это называется одним словом: результат.
Работающий сервис и сервис, дающий результат, – разные состояния. Между ними лежит система из трёх звеньев: вас должны найти, вам должны поверить, и вы должны видеть, что происходит. Глава пройдёт по звеньям в общих чертах – принципами, которые переживут смену алгоритмов и сервисов, – а в конце покажет, как эти же звенья выглядят на взрослом масштабе, на нашей фабрике.
Звено первое: вас должны найти
Прекрасный сервис по адресу, которого никто не знает, эквивалентен отсутствию сервиса. Находят вас двумя путями, и с недавних пор их действительно два, а не один.
Первый – классический поиск. Его механика для владельца маленького сайта сводится к трём условиям. Страница отвечает на конкретный вопрос конкретными словами – теми, которыми спрашивают люди, а не теми, которыми красиво говорить о себе: «полив газона осенью» находится, «комплексные решения для вашего участка» не находится никогда. Описание страницы – та пара строк, что видна в выдаче, – написано для человека, который решает, куда нажать. И карта сайта существует и не врёт: это файл-указатель, по которому поисковик узнаёт, что у вас есть. Указатель умеет врать, мы проверили: однажды наша карта из-за ошибки в сборке схлопнулась до одной ссылки на целый сайт – и месяцами сообщала поисковикам, что кроме главной страницы у нас ничего нет, при исправном сайте и зелёных отчётах сборки. Заметили при ручной сверке, чинили пересбором. Урок в духе всей книги: у карты, как у всякой проверки, должна быть своя проверка – число ссылок в ней против числа настоящих страниц.
Второй путь новый: ИИ-помощники. Всё больше людей вместо поиска спрашивают – и получают готовый ответ со ссылками на источники. Попасть в эти источники – отдельная задача со своими правилами, и хорошая новость в том, что правила эти прямее классических: помощники цитируют страницы, где ответ дан прямо, фактами, в первых абзацах, без предисловий «в современном мире». Жанр называется у нас «цитируемый блок»: короткий фактический ответ на вопрос в начале страницы, дальше подробности. Плюс маленькая новинка, которую почти никто пока не сделал: файл-визитка для ИИ-помощников, где сайт сам рассказывает, о чём он и каким фактам можно верить, – стоит полчаса, ставится агентом, у большинства соседей отсутствует.
Общий принцип звена: находимость – свойство текста, никакой магии продвижения в ней нет. Страница, по делу отвечающая на настоящий вопрос, со временем находится; страница про «инновационные решения» не находится ни при каком продвижении.
Звено второе: вам должны поверить
Нашли – читают. И здесь решает качество текста, про которое важно сказать главное: это инженерная величина, а не вкусовщина. Мы пришли к этому, поставив тексты на конвейер, и правила оттуда переносятся на любой масштаб, включая одну страницу вашего сервиса.
Первое правило вы узнаете: канон стиля – это файл правил для текстов. Та же механика, что в главе про файл правил проекта: осадок редакторских ожогов, записанный так, чтобы исполнитель – человек или модель – подчинялся без напоминаний. Какие слова запрещены, как звучат заголовки, что с тире и точками, чего в текстах не бывает никогда. Наш канон вырос до внушительного документа, и почти за каждой строкой – место, где текст звучал фальшиво, и мы разобрались почему.
Второе правило дороже: факты проверяются гейтом. Модель, генерящая текст, – тот же агент из главы о чтении результатов: она достраивает недосказанное правдоподобно, и с цифрами, датами и нормативами делает это особенно уверенно. У нас между генерацией и публикацией стоит проверка: отдельная модель-судья читает текст и ищет утверждения, которых нет в источниках; спорное уходит человеку, с выдуманными цифрами текст не публикуется. Правило для вашего масштаба то же, что было с кодом: сгенерированный текст – гипотеза; факты в нём проверяются до публикации, и цифры берутся только из своих данных. Текст с одной настоящей цифрой бьёт текст с пятью округлыми – и по доверию читателя, и, всё чаще, по цитируемости у ИИ-помощников, которые учатся отличать фактуру от воды.
Звено третье: вы должны видеть
Здесь короче всего, потому что механика в книге уже собрана – осталось направить её на результат.
У контентной системы тот же главный вопрос, что у сервиса: какое событие должно происходить регулярно и кто заметит, если оно перестанет? Показы, переходы, заявки – это события, и на их отсутствие ставится тот же сторож, что на обращения. А самый дешёвый прибор – два числа рядом: сколько пришло и сколько сделало нужное действие. Врозь эти числа успокаивают; рядом – ставят диагноз. Много показов при нуле переходов – не о том пишем или не так называемся; переходы при нуле заявок – читают и не верят, смотрите звено второе.
И правило решений, за которое мы заплатили месяцами работы: по числам, а не по ощущениям. В главе о рамке мы забегали вперёд к направлению, которое строилось увлечённо и не привело ни одного человека; как принималось решение о его судьбе, доскажет последняя часть. Правило, ради которого мы его сюда позвали, короткое: ощущение «дело идёт» не отчитывается о результате. Числа отчитываются.
Как это выглядит на взрослом масштабе
Обещанная витрина – наша фабрика, в общих чертах, без единого названия сервиса. Показываем её ради одного: она собрана из тех же деталей, которые вы уже держали в руках.
Ежедневно фабрика делает примерно следующее. Собирает темы – из запросов, которые люди задают поиску, и из наблюдения за отраслью; запас тем сторожится датчиком входа. Пишет тексты – модели по канону стиля. Проверяет – судья ищет выдуманные факты, спорное уходит человеку; это гейт, без зелёного света которого публикации нет. Публикует по расписанию – с ручным подтверждением там, где выход публичный: правило «подтверждено плюс прошедшее время означает немедленную отправку» стоит в нашем файле правил жирным. Появилось оно после трёх одинаковых случаев: пост готовили к утру, ставили отметку «подтверждено», время в записи оставляли прошлое – и очередь исправно отправляла его в ту же минуту, потому что для неё прошедшее время означает «пора». Замеряет – позиции и показы приезжают по расписанию в базу, и датчики смотрят на них так же, как сторож на обращения: пропали данные – сигнал, просели показы – сигнал. Возвращает – то, что просело или выстрелило, влияет на завтрашние темы.
Тот же конвейер в другом материале собирает видео: сценарий, кадры, озвучка, сборка, публикация – с теми же гейтами, включая проверку кадров глазами до оживления, потому что у видео свои способы наврать. И всё это – скрипты под историей изменений, с тестами и прогонами вхолостую: часть про уход от конструктора вы читали две главы назад, фабрика и есть то, что переехало.
Файл правил, гейт с проверкой фактов, расписание с подтверждением, сторожа входа и выхода, история изменений – ни одной детали, которой не было в этой книге. Масштаб другой – детали те же. Это и есть главная мысль витрины: система, дающая результат, не требует секретных знаний. Она требует, чтобы обычные детали были собраны в замкнутый круг: найти – поверить – увидеть – поправить.
Потому что цельность здесь решает всё. Страница без ответа на вопрос не находится. Найденная без фактов не убеждает. Убедительная без замера не улучшается – вы даже не узнаете, что она убедительна. Три звена, и слабейшее определяет результат целого; у системы из двух звеньев результат обычно нулевой, зато отчётность красивая.
Если вы заказчик. Вся глава сжимается в два вопроса подрядчику, который делает вам «сайт с продвижением»: «по каким запросам нас находят – покажите» и «сколько из нашедших оставляет заявку – покажите». Два числа, показанные рядом, отличают систему от набора страниц. Если в ответ звучит «мы наращиваем присутствие» без чисел – вы покупаете второе звено без первого и третьего, а чему равна такая система, сказано абзацем выше.
Упражнение. Напишите для своего сервиса одну страницу-ответ – например, «как оставить обращение и что с ним будет дальше»: это настоящий вопрос вашего будущего пользователя, заданный его словами. Ответьте в первом абзаце прямо и фактами, дальше – подробности. Опубликуйте на своём настоящем адресе. Потом проверьте звено в деле: задайте свой вопрос ИИ-помощнику и посмотрите, чем он ответит и кого процитирует. Сегодня, скорее всего, ещё не вас – а через месяц спросите снова. Этот замер, повторяемый раз в месяц, и есть третье звено в миниатюре.
И последнее, прежде чем закрыть часть. Откройте шесть вопросов из первого вечера – те, что вы записали после формы, говорившей «Спасибо». К этому месту книги у вас есть ответ делом на каждый: где данные – в базе за миграциями; что после обновления и перезапуска – проверено тестом; что при ошибке – есть тест кривого ввода; что уходит на сервер – видно в истории; что увидит человек – решено вами, вплоть до умолчаний; запускается ли по инструкции – опись среды проверена в чистой папке. Шесть вопросов первого вечера выросли в инженерию, и дальше они продолжат расти.
На этом инженерия собрана. Ваш проект под историей, под охраной тестов, данные структурированы, секреты убраны, среда воспроизводима, выкладка повторяема, и у него есть система, ведущая к результату. По всем меркам этой книги он перестал быть черновиком – он стал настоящим.
И именно поэтому дальше начинается самое интересное. Настоящий проект живёт: им пользуются, его меняют, он стареет и ломается – как правило, тихо. Часть III – про эксплуатацию: про проверки, которые врут, тишину, которую никто не слушает, правки, которые сносят соседей, и вопрос, где на самом деле лежит ваш проект. Это самая дорогая часть книги – в пересчёте на то, что мы заплатили за её материал.