Всем привет. Меня зовут Кирилл. За 10 лет в страховой отрасли прошел путь от IT-эникейщика до ТимЛида группы разработки страховых продуктов аквизиции. В этой статье поделюсь простыми приёмами по перешагиванию через очевидные грабли, чтобы движение через Зону Жизненного Опыта, Получаемую в Процессе было менее болезненно.  Решил добавить столбец к табличке в Pervasive на проде. Размер таблицы в топ 5 базы. Запустил консольную утилиту по перестройке файла данных, подождал полчасика и закрыл – думал зависла. Файл данных таблицы, неожиданно (как же ещё) стал невалидным. Ночь, сервак на другом конце Москвы в БЦ, неработающая ключевая таблица – утром ждет разнос. В судорожных попытках вспомнить порядок действий приходит понимание – есть бэкап! Процесс восстановления пощекотал нервы – «а вдруг не…», но обошлось.   После этого случая, выработал для себя 2 железных правила:  - Делай бекап/снапшот всякий раз перед изменением (даже для мелкого update’а);  - Проверяй решение на другом инстансе, до деплоя на прод. Самый простой способ помочь завтрашнему себе – сделать скрипты восстановления данных при их модифицировании (Insert/Update/Delete). Для операции Delete достаточно выгрузить данные из IDE – на основании Select’а формируется датасет и просто экспортируется в файл/кэш как набор Insert-скриптов. Важно выгружать все данные по Foreign Keys, явным или нет, в виде отдельных Insert-инструкций. Особенно чувствительно, когда таблицы с данными каскадные либо со специфичной логикой в API/триггерах удаления. Варианты архитектуры, когда в Delete API реализован Insert/update/Post-сервисы стоит рассматривать отдельно – у данных может быть разная критичность.  Второй по сложности оператор DML – Insert. Можно просто сохранить список Primary Keys (PK). Или написать запрос, который отберёт созданные записи по специфичным маркерам. При наличии в таблице поля с автором создания (CreatedBy), датой создания, или другим особым полем, задача сильно упрощается. На практике, чаще всего, сохраняется запрос создания, по которому можно написать обратный скрипт удаления. На мой взгляд, список PK надежнее – вероятность затереть что-то чужое сводится почти к 0. Третья и самая сложная DML-операция – Update. Здесь возможны различные комбинации способов восстановления. Наиболее сложный случай в практике – восстановить данные по прошествии времени (полгода-год), когда над данными пользователь уже активно поиздевался поработал. В итоге потребовалось формализовать последовательность действий пользователя в виде скрипта. Прогнать скрипт на согласованном массиве данных – части списка для восстановления. И, естественно, проверить с двух сторон: бизнеса и DevTeam. Бэкапы и снапшоты удобны тогда, когда есть опыт и возможность их использования. Наличие прямого доступа к файловой системе, либо удобный интерфейс инкрементального восстановления только облегчают задачу. Хотя работа через консольные приложения по-прежнему имеет своих ярых адептов. Наиболее понятный вариант – накатить снапшот/бэкап при полной остановке инстанса в монопольном режиме работы. Минус этого варианта – ограниченность применения для промышленных решений 24/7/365, так как незапланированные простои даже в минуту порождают лавины алертов на ответственных. Поэтому ради восстановления пары-тройки записей в таблице, городить огород со специализированными схемами/таблицами – занятие на любителя. Порой легче уговорить пользователя завести данные заново. Работу со скриптами отката можно сильно облегчить через архитектуру БД – создать теневые таблицы. Решение спорное, т.к. для каждой таблицы приходится индивидуально отвечать на вопрос «что лучше?»: полные логи или малое потребление дискового пространства.    В Компании есть реализация механизма – историчные таблицы, доступные для просмотра пользователю. В них сохраняются предыдущие значения записей основных таблиц. В итоге есть читабельный лог изменения данных. До Update Сам Update После Update  Команда целый инкремент перепиливает алгоритм расчета премии по страховому продукту. Затронуты как фронт, так и несколько систем бэка. Деплой идет тремя параллельными релизами в разное время в течение дня. День релиза определен, доработки закреплены, влиты в регрессные сборки, релиз-менеджеры финализируют тестирование и готовятся к деплою. Сутки до выкладки… и возникает вопрос, как поведет себя прод в промежутках между выкладками релизных сборок разных систем: фронт доехал, бэк в процессе, а кусок системы расчета тарифов уже вовсю работает. Страшновато. Начинаешь спешно просматривать все pull request’ы по выкладке, выясняя, что в итоге отвалится. В тот раз пронесло, благодаря требованиям к обратной совместимости (обработка null для нового входящего параметра) – неконсистентность кода прошла незаметной.   Итогом стало принятие на уровне команды парочки подходов:  - После выкладки крупных изменений, проверяем сами на проде, до анонсирования бизнесу;  - Декомпозиция Features на User Story таким образом, чтобы можно было их деплоить независимо (привет CI/CD);  - Обязательно наличие FT (сейчас наличие FT является НФТ для большей части доработок со стороны Компании). В другой раз выкатывали изменения UI внутреннего страхового продукта. Допилили, залили, включились в релиз, и на регрессном стеде смежная команда находит баг (да-да, это случается, и это нормально) в одной из проверок UI. Анализ дефекта показывает, что не учли крупный бизнес-кейс – его доработка требует качественного тестирования и спокойной головы при написании кода. Хорошо, что доработку проверки зафичетоглили под отдельным кодом FT, т.е. она может быть отключена независимо. Подключаем бизнес и принимаем решение, что проверка некритична и можем запускаться без неё. FT отключается, задача тех долга на исправление закидывается в бэклог ближайшего спринта, дефект закрывается и релиз выходит без сбоев. Танцуют Все!   Что вынес для себя:  - Несколько кодов FT для одной стори хоть и усложняет обслуживание техдолга, но экономит нервы и время при ловле багов4  - Если UI поддерживает FT – используй.  Практика FT чуть сложнее резервной копии данных. Для нее необходим некий механизм логических переключателей – будь то таблица в БД, конфиг-файлы или настройки метаданных. Плюс требуется внутреннее согласие с ростом техдолга и необходимостью его обслуживания. Сразу уточню, описание дано для Release Toggles. Их срок жизни – не дольше 2-3 релизных циклов (2 статья из Уголовного кодекса разработчика достаточно ярко подсвечивают столь жесткое требование). Необходимы для уменьшения количества внепланов/критических релизов. Польза в том, чтобы даже при полном отказе прода, его можно было легко (в идеале мгновенно, в процессе деплоя) реанимировать, отключив новый код. Долгоживущие FT A/B тестирования, несут в себе другую ценность и на спокойствие разработчика при выкладке кода особо не влияют. Базовые принципы: Выключенный FT код не меняет, то есть курсор выполнения проходит ровно все те же блоки в том же порядке, что и без FT; FT не используются повторно. На проде точно, а другие инстансы – на совести разработчика; Knightmare: A DevOps Предостерегающая история 4 минута видео FT обязательно выпиливать после подтверждения отсутствия критических отказов. Мелкий косяк через 3-4 месяца активного (!) использования кода – явление настолько тривиальное, что ради него откатывать все решение нерационально. Для разработчика последний пункт самый спорный. Однако в гигантской кодовой базе, где понимание работы даже в рамках пакета может быть неполным, лучше сразу выпилить неработающий код, чем всякий раз при разборе инцидентов вспоминать «а оно еще надо»? Перепала как-то задача переписать запрос Oracle онлайн-канала – периодически оптимизатор выбирает план с full scan по датасету в 3-4m записей. Это был не первый подход к снаряду. Очевидные вещи в виде разбиения на запросы с union уже были применены. Нужен индекс. Так как таблица большая и с активными I/O операциями, DBA просят обоснования, что создание индекаса действительно поможет. Просят – пожалуйста. Сделано 2 версии запроса, обернуты в FT и влиты в тестовый стенд. Дальше, через RunStats анализ используемых ресурсов, потом прогон на совпадение результатов по нескольким сотням примеров. Сошлись и это хорошо. Следом то же самое на регрессе и проде – запускаешь скрипт и идешь пить чай. Приходишь и смотришь результаты – новая версия работает быстрее, ошибок нет. Самый приятный бонус данного подхода – один раз настраиваешь тесты (при разработке), и потом не паришься, когда нужно допилить функционал (протестировать руками даже 20 кейсов 3-4 раза подряд желания вызывает мало) За этот подход спасибо коллегам. Крайне полезен при рефакторинге легаси. Суть – взять данные в базе (список договоров) и прогнать по максимуму через обе реализации метода, старую и новую. Так как enterprise-база кишит тестовыми примерами разной степени кривизны, при выборке в несколько тысяч-десятков тысяч примеров, шансов пропустить критический косяк очень мало. Добавив еще и сравнением производительности, получается хорошая серебряная пуля при разговоре с архитекторами. Практика прекрасно сочетается с FT. В рамках одного запуска кода легко сравнить старое и новое решения.  Логичное развитие тестов сходимости – модульные тесты (Unit tests, UT). Когда проверки присутствуют в базе одновременно с продовым кодом (за теорией – к Roy Osherove).  Польза UT не однозначна на старте проекта. Однако при развитии решения, благодаря UT стоимость рефакторинга снижается (см. техдолг). Наиболее очевидный аспект – меньше времени тратится на ретест при изменении кода. Вопросы, как писать UT на Oracle достойны отдельной статьи. Здесь же приведу пару примеров реализации в формате тестопригодного дизайна на Oracle. Проверяемый метод Тесты  Проверяемый метод Тесты  Как видно, предлагаемые практики не требуют больших усилий в части использования, но очень эффективны. Критерий их применения – «как быстро смогу починить упавший из-за моих доработок прод». Буду признателен в комментариях за описания других техник, минимизирующих ошибку разработчика.