惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
量子位
腾讯CDC
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LangChain Blog
aimingoo的专栏
aimingoo的专栏
The Hacker News
The Hacker News
T
The Exploit Database - CXSecurity.com
B
Blog
S
SegmentFault 最新的问题
P
Privacy & Cybersecurity Law Blog
T
Threatpost
博客园 - 聂微东
T
Tailwind CSS Blog
The Last Watchdog
The Last Watchdog
C
Check Point Blog
N
Netflix TechBlog - Medium
D
DataBreaches.Net
爱范儿
爱范儿
IT之家
IT之家
S
Secure Thoughts
M
MIT News - Artificial intelligence
NISL@THU
NISL@THU
C
Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
有赞技术团队
有赞技术团队
A
Arctic Wolf
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
Schneier on Security
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
G
GRAHAM CLULEY
雷峰网
雷峰网
Project Zero
Project Zero
博客园 - Franky
H
Heimdal Security Blog
A
About on SuperTechFans
Security Latest
Security Latest
Webroot Blog
Webroot Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hugging Face - Blog
Hugging Face - Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
А что не так с вашими миграциями? Liquibase, rollback и первые тревожные признаки
rurikovich · 2026-05-17 · via Все публикации подряд на Хабре

Время на прочтение7 мин

Охват и читатели382

Туториал

Привет, Хабр !

Миграции базы данных вроде бы есть, Liquibase подключен, changelog лежит в репозитории, CI зелёный, релизы проходят регулярно. Значит, процесс под контролем?

Не всегда…

Миграции часто выглядят как обычная техническая рутина. Добавили колонку, создали индекс, поправили enum, пересобрали витрину. Но проблема может проявиться уже на релизе: база держит lock, отчёты показывают пустоту, rollback есть только на словах, а команда разбирается, изменилась схема, изменились данные или Liquibase просто выполнил то, что было описано в changelog-е.

Мы рассмотрим миграции как полноценную часть разработки, а не только как придаток к коду. Разберём, что такое миграции, какие инструменты для них используют, почему Liquibase одновременно удобный и опасный, и посмотрим на несколько проблем из реальных проектов.


Что будет в статье:

  1. Что такое миграция БД и почему это не просто ALTER TABLE.

  2. Какие инструменты миграций чаще всего встречаются в разных языках.

  3. Почему Liquibase одновременно удобный и опасный.

  4. Несколько проблем из реальных проектов: rollback, checksum, большие changeset-ы, runInTransaction=false, IF EXISTS, destructive migration и пересоздание таблиц.


1. Миграция: простое изменение с непростыми последствиями

Миграция базы данных это версия изменения схемы или данных, которую можно повторяемо применить к окружению. Сегодня на dev, завтра на stage, потом на prod. В идеале одинаково.

Обычно миграция делает одну из таких вещей:

  • создаёт таблицу, колонку, индекс, constraint;

  • меняет тип поля;

  • преобразует данные под новую структуру;

  • создаёт view, materialized view, enum, sequence;

  • удаляет старую схему;

  • иногда чинит данные, потому что реальные сценарии отличаются от проектных диаграмм.

Главная идея простая: база должна эволюционировать вместе с кодом. Если приложение версии N ожидает колонку foo, то эта колонка должна появиться до того, как код начнёт ей пользоваться. Если приложение перестало писать в старое поле, то удалять его стоит не сразу, а после понятного переходного периода.

В коде мы привыкли к git history. В базе нужен похожий механизм. Только с важным отличием: git можно откатить локально за секунду, а базу с терабайтами данных и живыми пользователями так просто не откатишь.

Миграция это не просто SQL-файл. Это изменение состояния базы данных.


2. Чем обычно мигрируют базы

Для миграций используют разные инструменты. Одни запускаются отдельно, другие встроены во фреймворки.

Самые распространённые:

  • Liquibase. Java/JVM-мир, но не только он. XML/YAML/JSON/SQL changelog-и, checksums, contexts, labels, rollback, preconditions. Часто встречается в Java, Kotlin, Scala.

  • Flyway. Тоже JVM-экосистема, но проще по модели: версионированные SQL-скрипты, repeatable migrations, минимум скрытого поведения. Подходит командам, которым важно явно видеть SQL миграций.

  • Rails Active Record Migrations. Ruby on Rails. Миграции как Ruby-код: add_column, create_table, change.

  • Django migrations. Python/Django. Автогенерация по моделям, зависимости между миграциями, операции уровня ORM.

  • Alembic. Python/SQLAlchemy. Часто в FastAPI/SQLAlchemy-проектах. Есть автогенерация, но результат всё равно нужно проверять и дорабатывать.

  • Ecto migrations. Elixir/Phoenix. Приятный DSL, нормальная интеграция с приложением.

  • Entity Framework Core migrations. .NET/C#. Миграции из модели, C#-код, генерация SQL под провайдера.

  • Prisma Migrate. Node.js/TypeScript. Хорош для проектов вокруг Prisma schema.

  • TypeORM / Knex migrations. Node.js. Обычно миграции в TypeScript/JavaScript, с up/down.

  • golang-migrate, Goose, dbmate. Go и не только. Простые CLI-инструменты, чаще всего SQL-first.

  • Sqitch. Менее массовый, но интересный: dependency-based подход, verify/revert-скрипты.

  • Atlas. Более современный declarative/schema-as-code подход, особенно популярен там, где хотят сравнивать желаемое состояние схемы с реальным.

Я не думаю, что есть один «правильный» инструмент. Есть разные компромиссы.

Если команда хорошо пишет SQL и не хочет скрытой логики инструмента, Flyway или golang-migrate могут быть хорошим выбором. Если нужен единый механизм на несколько СУБД, contexts, rollback, changelog graph и формальные preconditions, Liquibase даёт богатый набор. Если проект живёт внутри Rails или Django, встроенные миграции часто удобны просто потому, что они часть привычного workflow.

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

Не сделает.


3. Liquibase: возможности и ограничения

Liquibase хранит историю применённых changeset-ов в служебной таблице. У changeset-а есть id, author, путь к файлу и checksum. При запуске Liquibase смотрит, что уже применено, что новое, что изменилось, и выполняет недостающие операции.

Звучит надёжно. И в целом это удобно.

Плюсы Liquibase:

  • можно писать декларативно: addColumn, createTable, createIndex;

  • можно писать сырой SQL, если DSL не хватает;

  • есть preConditions;

  • есть rollback;

  • есть contexts и labels;

  • есть checksum, который ловит изменение уже применённого changeset-а;

  • можно запускать миграции из приложения, из CI или отдельным job-ом.

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

Например, checksum должен защищать от правки старой миграции. А потом кто-то пишет validCheckSum any, и защита фактически отключается. Rollback есть в документации, но в реальном changelog-е его может не быть. preConditions есть, но SQL внутри changeset-а всё равно может быть неидемпотентным. runInTransaction=false иногда нужен, например для CREATE INDEX CONCURRENTLY в PostgreSQL, но если он стоит в большом changeset-е с набором DDL, частично применённая миграция становится штатным сценарием.

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


4. Несколько проблем из реальных миграций

Ниже примеры проблем из реальных кодовых баз. Где-то PostgreSQL, где-то ClickHouse, где-то обычный XML Liquibase с raw SQL внутри.

1. Rollback отсутствует почти везде

Распространённая и дорогая проблема.

В проекте может быть больше сотни changelog-файлов, а rollback-блоки встречаются единично. И обычно не там, где риск максимальный, а там, где кто-то однажды был особенно дисциплинирован.

Почему это проблема? Потому что «откатим релиз» и «откатим миграцию» это разные действия. Код можно вернуть на предыдущую версию. Базу, в которой уже удалили колонку или пересобрали таблицу, просто так назад не вернёшь.

Минимальный rollback нужен хотя бы для:

  • добавленных колонок;

  • созданных индексов;

  • новых таблиц;

  • изменённых default-ов;

  • миграций данных;

  • опасных DDL, которые могут остановить релиз.

А для destructive migration одного rollback-блока мало. Там нужен ещё тест отката. Иначе это неподтверждённое предположение, а не рабочий rollback.

2. validCheckSum any: отключение важной проверки

Checksum в Liquibase нужен, чтобы старый changeset нельзя было незаметно поменять. Это полезная защита.

Но потом в миграциях появляется:

<validCheckSum>1:any</validCheckSum>

После этого Liquibase больше не сообщает о несовпадении checksum.

Я понимаю, почему так делают. Где-то поправили форматирование, где-то уже применённая миграция отличалась между окружениями, где-то требовалось срочно исправить релиз. Но когда any становится привычкой, история миграций перестаёт быть надёжной. По такой истории уже нельзя точно понять, какой changeset был применён.

Для промышленной базы это высокий риск. Особенно если рядом raw SQL на создание таблиц, удаление view и перекладку данных.

3. Один changeset делает слишком много

Хороший changeset небольшой и понятный. Его можно прочитать, понять, применить, откатить, обсудить на ревью.

Плохой changeset делает всё сразу:

  • удаляет старую витрину;

  • создаёт таблицу;

  • создаёт materialized view;

  • меняет enum;

  • добавляет колонку;

  • переливает данные;

  • и всё это под одним id.

Когда такой changeset прерывается на середине, состояние становится неочевидным. Что уже применилось? Что Liquibase записал в служебную таблицу? Можно ли повторить запуск? Нужно ли вручную удалять частично созданные объекты? А на втором узле кластера состояние такое же?

Для отчётных баз и ClickHouse это особенно рискованно. Там DDL часто идёт ON CLUSTER, а значит состояние может разойтись уже на уровне кластера.

4. runInTransaction=false как default, а не как исключение

Иногда runInTransaction=false необходим. Например, PostgreSQL не даст выполнить CREATE INDEX CONCURRENTLY внутри транзакции.

Но если этот флаг стоит на многих changeset-ах просто потому что «так принято в этом модуле», последствия могут быть серьёзными. Миграция может частично примениться. Liquibase может не записать changeset как выполненный. Следующий запуск увидит уже созданную часть объектов и завершится ошибкой на следующей операции.

Типичный сценарий:

CREATE TABLE ...
-- успешно

CREATE VIEW ...
-- упало

Повторный запуск:

CREATE TABLE ...
-- теперь уже упало, потому что таблица есть

Можно добавить IF NOT EXISTS. Но это не решает проблему полностью. Это локальная защита, иногда нужная, но недостаточная как общий подход.

5. IF EXISTS и IF NOT EXISTS маскируют расхождение окружений

Идемпотентность полезна. Но у неё есть важный недостаток.

Когда в каждой второй строке:

DROP TABLE IF EXISTS ...
ADD COLUMN IF NOT EXISTS ...
CREATE INDEX IF NOT EXISTS ...

миграция перестаёт проверять фактическое состояние схемы. Она не говорит: «на этом окружении схема неожиданно другая». Она говорит: «условие не мешает выполнению, продолжаю».

Иногда это правильно. Например, если миграция специально чинит разные состояния после аварийного релиза. Но как постоянный стиль это опасно.

На одном стенде колонка уже была с типом String, на другом её нет, на третьем она есть, но nullable. ADD COLUMN IF NOT EXISTS не выявит два из трёх случаев. А приложение потом работает с тем состоянием схемы, которое получилось.

6. Destructive migration без отдельной проверки

Удалить колонку просто. Удалить таблицу тоже просто. В XML это занимает одну строку. В SQL тоже.

В реальных миграциях я видел группы DROP TABLE IF EXISTS, иногда десятками строк подряд. Особенно в отчётном контуре: старые витрины, materialized view, промежуточные таблицы.

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

Но на ревью destructive migration должны звучать обязательные вопросы:

  • кто читает эти данные сейчас?

  • есть ли dashboard, который не лежит в репозитории приложения?

  • есть ли внешняя выгрузка?

  • можно ли сначала перестать писать, потом перестать читать, потом удалить?

  • сколько времени данные должны храниться по требованиям бизнеса?

Если ответов нет, DROP лучше не мержить. Такая проверка снижает риск восстановления данных после релиза.

7. Пересоздание таблицы вместо эволюции схемы

Отдельная проблемная практика: DROP TABLE, потом CREATE TABLE с новой структурой.

Иногда это оправдано. Например, таблица техническая, данные эфемерные, пересборка дешёвая. Но если это поток событий, отчётная таблица или накопительный слой, пересоздание это уже не миграция схемы. Это операция с данными.

У неё должны быть:

  • план сохранения или восстановления данных;

  • оценка времени;

  • понимание нагрузки;

  • совместимость с текущим кодом;

  • rollback или хотя бы план исправления следующей миграцией.

Без этого пересоздание таблицы выглядит как обычный рефакторинг, хотя по факту затрагивает production-данные.


Короткий вывод

Миграции становятся опасными не тогда, когда команда выбирает «не тот» инструмент, а когда начинает относиться к изменению базы как к технической формальности. Liquibase, Flyway или любой другой механизм только выполняют описанные шаги. Они не знают, потеряются ли данные, выдержит ли production нагрузку и можно ли будет безопасно повторить запуск.

Если в changelog-е нет понятного rollback-а, changeset делает слишком много, runInTransaction=false используется по привычке, а DROP проходит ревью как обычная чистка, база постепенно превращается в место, где релиз может сломаться на проде, несмотря на зелёный CI и успешный регресс.


Если вам близки темы разработки, рефакторинга, архитектуры и стартапов, буду рад видеть вас в моём Telegram-канале.