1. Pengenalan Database Migration
Database Migration adalah proses mengelola perubahan skema database secara terstruktur, terstrack, dan reproducible. Setiap perubahan — menambah tabel, mengubah kolom, menambah index — dicatat sebagai file migration yang bisa dijalankan secara berurutan.
+--------------------------------------------------------------------+
| DATABASE MIGRATION LIFECYCLE |
| |
| v1.0 (Initial) v1.1 (Add column) v1.2 (Add index) |
| +------------+ +---------------+ +-----------+ |
| | CREATE | -->| ALTER TABLE | -->| CREATE | --> ... |
| | TABLE | | ADD COLUMN | | INDEX | |
| +------------+ +---------------+ +-----------+ |
| |
| Migration History (database table): |
| +----+----------+-------------------+---------------------+ |
| | id | version | description | installed_on | |
| +----+----------+-------------------+---------------------+ |
| | 1 | 1.0 | initial schema | 2026-01-15 10:00:00 | |
| | 2 | 1.1 | add phone column | 2026-02-20 14:30:00 | |
| | 3 | 1.2 | add email index | 2026-03-10 09:15:00 | |
| +----+----------+-------------------+---------------------+ |
+--------------------------------------------------------------------+
Jenis Migration
| Jenis | Deskripsi | Contoh |
|---|---|---|
| Schema Migration | Perubahan struktur tabel (DDL) | CREATE TABLE, ALTER TABLE |
| Data Migration | Perubahan isi data (DML) | UPDATE kolom, INSERT data awal |
| Seed Migration | Mengisi data awal/lookup | INSERT kategori, kota, dll |
| Repeatable | Diulang setiap kali konten berubah | Views, stored procedures |
Konsep Versioning
| Pendekatan | Deskripsi | Contoh |
|---|---|---|
| Sequential | Angka urut: 1, 2, 3... | Flyway (V1, V2, V3) |
| Timestamp | Waktu pembuatan: 20260626_001 | Alembic, Rails |
| Hash-based | Hash dari konten migration | Prisma Migrate |
2. Mengapa Perlu Migration Tools?
Tanpa migration tools, perubahan skema database dikelola secara manual — berisiko tinggi untuk tim dan production. Berikut masalah umum yang dihindari:
Masalah Tanpa Migration Tools
| Masalah | Dampak |
|---|---|
| Tidak ada riwayat perubahan | Tidak tahu siapa mengubah apa dan kapan |
| Inkonsistensi environment | Development, staging, dan production punya skema berbeda |
| Tidak bisa rollback | Perubahan yang salah sulit dikembalikan |
| Conflict antar developer | Dua orang mengubah tabel yang sama tanpa koordinasi |
| Manual deployment | SQL script dijalankan manual, rawan human error |
| Tidak bisa reproduce | Sulit membuat database baru dengan skema yang benar |
Keuntungan Migration Tools
| Keuntungan | Penjelasan |
|---|---|
| Version Control | File migration disimpan di git, sejajar dengan kode aplikasi |
| Reproducible | Siapapun bisa clone repo dan jalankan migration untuk setup database |
| Automatic Tracking | Tool mencatat migration mana yang sudah dijalankan |
| Rollback Support | Bisa mengembalikan perubahan jika ada masalah |
| CI/CD Integration | Migration otomatis dijalankan saat deploy |
| Team Collaboration | Setiap developer bisa membuat migration dan merge tanpa conflict |
3. Flyway — SQL-Based Migration
Flyway adalah tool migration populer yang berbasis file SQL. Dikembangkan oleh Redgate, mendukung hampir semua database relasional. Cocok untuk tim yang familiar dengan SQL murni.
Instalasi Flyway
# Install via Homebrew (macOS/Linux)
brew install flyway
# Install via Docker
docker pull flyway/flyway
# Download binary
# https://flywaydb.org/documentation/usage/commandline/
# Verifikasi instalasi
flyway --version
Konfigurasi Flyway
# flyway.toml — Konfigurasi Flyway
[flyway]
url = "jdbc:mysql://localhost:3306/toko_online"
user = "root"
password = "password_anda"
schemas = ["toko_online"]
# Lokasi file migration
locations = ["filesystem:sql/migrations"]
# Nama tabel tracking migration
table = "flyway_schema_history"
# Encoding file SQL
encoding = "UTF-8"
# Placeholder (variabel dalam file SQL)
[flyway.placeholders]
app_user = "sistem"
env = "development"
Menulis Migration SQL
-- =============================================
-- File: sql/migrations/V1__create_tables.sql
-- Deskripsi: Membuat tabel awal toko online
-- =============================================
-- Prefix V = versioned migration (dijalankan sekali)
-- Prefix R = repeatable migration (dijalankan ulang jika berubah)
-- Prefix U = undo migration (rollback, Flyway Teams/Enterprise)
-- V1__create_tables.sql
CREATE TABLE pelanggan (
id_pelanggan INT PRIMARY KEY AUTO_INCREMENT,
nama VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
telepon VARCHAR(20),
kota VARCHAR(50) DEFAULT 'Jakarta',
tanggal_daftar DATE DEFAULT (CURRENT_DATE),
is_active BOOLEAN DEFAULT TRUE
);
CREATE TABLE produk (
id_produk INT PRIMARY KEY AUTO_INCREMENT,
nama_produk VARCHAR(200) NOT NULL,
harga DECIMAL(12, 2) NOT NULL CHECK (harga > 0),
stok INT NOT NULL DEFAULT 0,
kategori VARCHAR(50),
deskripsi TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE pesanan (
id_pesanan INT PRIMARY KEY AUTO_INCREMENT,
id_pelanggan INT NOT NULL,
tanggal_pesanan DATETIME DEFAULT CURRENT_TIMESTAMP,
total DECIMAL(12, 2) NOT NULL,
status VARCHAR(20) DEFAULT 'pending',
FOREIGN KEY (id_pelanggan) REFERENCES pelanggan(id_pelanggan)
);
-- =============================================
-- File: V2__add_indexes.sql
-- Deskripsi: Menambah index untuk performa
-- =============================================
CREATE INDEX idx_pelanggan_kota ON pelanggan(kota);
CREATE INDEX idx_produk_kategori ON produk(kategori);
CREATE INDEX idx_pesanan_tanggal ON pesanan(tanggal_pesanan);
CREATE INDEX idx_pesanan_status ON pesanan(status);
-- =============================================
-- File: V3__add_pelanggan_phone_index.sql
-- =============================================
CREATE INDEX idx_pelanggan_telepon ON pelanggan(telepon);
-- =============================================
-- File: V4__alter_pesanan_add_catatan.sql
-- =============================================
ALTER TABLE pesanan ADD COLUMN catatan TEXT;
-- =============================================
-- File: R__create_views.sql (Repeatable)
-- Akan diulang setiap kali isi file berubah
-- =============================================
CREATE OR REPLACE VIEW v_laporan_penjualan AS
SELECT
p.kategori,
COUNT(DISTINCT ps.id_pesanan) AS jumlah_pesanan,
SUM(dp.subtotal) AS total_penjualan
FROM detail_pesanan dp
JOIN produk p ON dp.id_produk = p.id_produk
JOIN pesanan ps ON dp.id_pesanan = ps.id_pesanan
WHERE ps.status <> 'batal'
GROUP BY p.kategori;
Menjalankan Flyway Commands
# Cek status migration (mana yang sudah/belum dijalankan)
flyway info
# Jalankan semua migration yang belum dijalankan
flyway migrate
# Validasi: cek apakah file migration sudah berubah
flyway validate
# Repair: fix migration history yang corrupt
flyway repair
# Clean: HAPUS SEMUA objek di database (HATI-HATI!)
flyway clean
# Baseline: tandai database existing sebagai baseline
flyway baseline -baselineVersion=1
# Undo: rollback migration terakhir (Enterprise)
flyway undo
+-----------+---------+---------------------------+------+---------------------+---------+----------+
| Category | Version | Description | Type | Installed On | State | Undoable |
+-----------+---------+---------------------------+------+---------------------+---------+----------+
| Versioned | 1 | create tables | SQL | 2026-06-26 10:00:00 | Success | No |
| Versioned | 2 | add indexes | SQL | 2026-06-26 10:00:01 | Success | No |
| Versioned | 3 | add pelanggan phone index | SQL | 2026-06-26 10:00:02 | Success | No |
| Versioned | 4 | alter pesanan add catatan | SQL | | Pending | No |
| Repeatable | | create views | SQL | 2026-06-26 10:00:03 | Outdated| |
+-----------+---------+---------------------------+------+---------------------+---------+----------+
Flyway dengan Maven/Gradle
<!-- pom.xml: Plugin Flyway untuk Maven -->
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>10.15.0</version>
<configuration>
<url>jdbc:mysql://localhost:3306/toko_online</url>
<user>root</user>
<password>password_anda</password>
</configuration>
</plugin>
<!-- Jalankan: -->
<!-- mvn flyway:migrate -->
<!-- mvn flyway:info -->
4. Alembic — Python Migration
Alembic adalah migration tool untuk Python yang dibuat oleh pembuat SQLAlchemy. Sangat populer di ekosistem Python — Flask, FastAPI, Django (sebagai alternatif).
Instalasi dan Setup
# Install Alembic
pip install alembic
# Inisialisasi project migration
alembic init alembic
# Struktur yang dihasilkan:
# alembic/
# versions/ -- file migration
# env.py -- konfigurasi environment
# script.py.mako -- template file migration
# alembic.ini -- konfigurasi utama
Konfigurasi Alembic
# alembic.ini
# [alembic]
# sqlalchemy.url = postgresql://user:pass@localhost/toko_online
# alembic/env.py — Konfigurasi yang lebih fleksibel
from alembic import context
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig
# Import model SQLAlchemy Anda
from app.models import Base
config = context.config
if config.config_file_name is not None:
fileConfig(config.config_file_name)
target_metadata = Base.metadata
def run_migrations_offline() -> None:
"""Run migrations in 'offline' mode."""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online() -> None:
"""Run migrations in 'online' mode."""
connectable = engine_from_config(
config.get_section(config.config_ini_section, {}),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
Membuat Migration
# Auto-generate migration dari perubahan model
alembic revision --autogenerate -m "initial tables"
# Buat migration kosong (manual)
alembic revision -m "add custom indexes"
# Jalankan migration
alembic upgrade head
# Jalankan 1 migration ke depan
alembic upgrade +1
# Rollback 1 migration
alembic downgrade -1
# Rollback ke awal
alembic downgrade base
# Lihat history
alembic history --verbose
# Lihat current version
alembic current
# Lihat semua migration yang pending
alembic history | grep "(head)"
Contoh File Migration Alembic
# alembic/versions/20260626_001_create_tables.py
"""create initial tables
Revision ID: a1b2c3d4e5f6
Revises:
Create Date: 2026-06-26 10:00:00.000000
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers
revision: str = 'a1b2c3d4e5f6'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Tabel pelanggan
op.create_table(
'pelanggan',
sa.Column('id_pelanggan', sa.Integer(), primary_key=True, autoincrement=True),
sa.Column('nama', sa.String(100), nullable=False),
sa.Column('email', sa.String(150), nullable=False, unique=True),
sa.Column('telepon', sa.String(20)),
sa.Column('kota', sa.String(50), server_default='Jakarta'),
sa.Column('tanggal_daftar', sa.Date(), server_default=sa.text('CURRENT_DATE')),
sa.Column('is_active', sa.Boolean(), server_default='true'),
)
# Tabel produk
op.create_table(
'produk',
sa.Column('id_produk', sa.Integer(), primary_key=True, autoincrement=True),
sa.Column('nama_produk', sa.String(200), nullable=False),
sa.Column('harga', sa.Numeric(12, 2), nullable=False),
sa.Column('stok', sa.Integer(), nullable=False, server_default='0'),
sa.Column('kategori', sa.String(50)),
sa.Column('deskripsi', sa.Text()),
sa.Column('created_at', sa.DateTime(), server_default=sa.func.now()),
)
# Tabel pesanan
op.create_table(
'pesanan',
sa.Column('id_pesanan', sa.Integer(), primary_key=True, autoincrement=True),
sa.Column('id_pelanggan', sa.Integer(), sa.ForeignKey('pelanggan.id_pelanggan'), nullable=False),
sa.Column('tanggal_pesanan', sa.DateTime(), server_default=sa.func.now()),
sa.Column('total', sa.Numeric(12, 2), nullable=False),
sa.Column('status', sa.String(20), server_default='pending'),
sa.Column('catatan', sa.Text()),
)
# Indexes
op.create_index('idx_pelanggan_kota', 'pelanggan', ['kota'])
op.create_index('idx_produk_kategori', 'produk', ['kategori'])
op.create_index('idx_pesanan_tanggal', 'pesanan', ['tanggal_pesanan'])
def downgrade() -> None:
op.drop_table('pesanan')
op.drop_table('produk')
op.drop_table('pelanggan')
5. Prisma Migrate — Node.js Migration
Prisma Migrate adalah bagian dari Prisma ORM untuk Node.js/TypeScript. Migration didasarkan pada perubahan pada file schema.prisma dan di-generate otomatis.
Setup Prisma
# Install Prisma
npm install prisma --save-dev
npm install @prisma/client
# Inisialisasi Prisma
npx prisma init
# Struktur yang dihasilkan:
# prisma/
# schema.prisma -- definisi model
# migrations/ -- file migration
# .env -- DATABASE_URL
Definisi Schema
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Pelanggan {
idPelanggan Int @id @default(autoincrement())
nama String @db.VarChar(100)
email String @unique @db.VarChar(150)
telepon String? @db.VarChar(20)
kota String @default("Jakarta") @db.VarChar(50)
tanggalDaftar DateTime @default(now()) @map("tanggal_daftar")
isActive Boolean @default(true) @map("is_active")
pesanan Pesanan[]
@@map("pelanggan")
}
model Produk {
idProduk Int @id @default(autoincrement())
namaProduk String @map("nama_produk") @db.VarChar(200)
harga Decimal @db.Decimal(12, 2)
stok Int @default(0)
kategori String? @db.VarChar(50)
deskripsi String?
createdAt DateTime @default(now()) @map("created_at")
detail DetailPesanan[]
@@map("produk")
}
model Pesanan {
idPesanan Int @id @default(autoincrement())
idPelanggan Int @map("id_pelanggan")
tanggalPesanan DateTime @default(now()) @map("tanggal_pesanan")
total Decimal @db.Decimal(12, 2)
status String @default("pending") @db.VarChar(20)
catatan String?
pelanggan Pelanggan @relation(fields: [idPelanggan], references: [idPelanggan])
detail DetailPesanan[]
@@map("pesanan")
}
model DetailPesanan {
idDetail Int @id @default(autoincrement())
idPesanan Int @map("id_pesanan")
idProduk Int @map("id_produk")
jumlah Int
hargaSatuan Decimal @map("harga_satuan") @db.Decimal(12, 2)
subtotal Decimal @db.Decimal(12, 2)
pesanan Pesanan @relation(fields: [idPesanan], references: [idPesanan])
produk Produk @relation(fields: [idProduk], references: [idProduk])
@@map("detail_pesanan")
}
Prisma Migrate Commands
# Buat migration dari perubahan schema.prisma
npx prisma migrate dev --name add_phone_index
# Jalankan migration di production
npx prisma migrate deploy
# Reset database (HAPUS SEMUA DATA + re-run semua migration)
npx prisma migrate reset
# Generate Prisma Client setelah schema berubah
npx prisma generate
# Push schema langsung tanpa migration (prototyping)
npx prisma db push
# Buka Prisma Studio (GUI untuk data)
npx prisma studio
# Lihat status migration
npx prisma migrate status
# Resolve migration conflict
npx prisma migrate resolve --applied <migration_name>
File Migration yang Di-generate
-- prisma/migrations/20260626100000_init/migration.sql
-- CreateTable
CREATE TABLE "pelanggan" (
"id_pelanggan" SERIAL NOT NULL,
"nama" VARCHAR(100) NOT NULL,
"email" VARCHAR(150) NOT NULL,
"telepon" VARCHAR(20),
"kota" VARCHAR(50) DEFAULT 'Jakarta',
"tanggal_daftar" DATE DEFAULT CURRENT_DATE,
"is_active" BOOLEAN DEFAULT true,
CONSTRAINT "pelanggan_pkey" PRIMARY KEY ("id_pelanggan")
);
-- CreateIndex
CREATE UNIQUE INDEX "pelanggan_email_key" ON "pelanggan"("email");
-- CreateTable
CREATE TABLE "produk" (
"id_produk" SERIAL NOT NULL,
"nama_produk" VARCHAR(200) NOT NULL,
"harga" DECIMAL(12,2) NOT NULL,
"stok" INTEGER NOT NULL DEFAULT 0,
"kategori" VARCHAR(50),
"deskripsi" TEXT,
"created_at" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "produk_pkey" PRIMARY KEY ("id_produk")
);
-- CreateTable
CREATE TABLE "pesanan" (
"id_pesanan" SERIAL NOT NULL,
"id_pelanggan" INTEGER NOT NULL,
"tanggal_pesanan" TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP,
"total" DECIMAL(12,2) NOT NULL,
"status" VARCHAR(20) DEFAULT 'pending',
"catatan" TEXT,
CONSTRAINT "pesanan_pkey" PRIMARY KEY ("id_pesanan"),
CONSTRAINT "pesanan_id_pelanggan_fkey"
FOREIGN KEY ("id_pelanggan") REFERENCES "pelanggan"("id_pelanggan")
ON DELETE RESTRICT ON UPDATE CASCADE
);
6. Perbandingan Ketiga Tools
| Aspek | Flyway | Alembic | Prisma Migrate |
|---|---|---|---|
| Bahasa | Java (CLI tersedia) | Python | Node.js/TypeScript |
| Format Migration | SQL murni | Python (opsional SQL) | Auto-generated SQL |
| Database Support | Sangat luas (20+) | Beragam via SQLAlchemy | PostgreSQL, MySQL, SQLite, dll |
| Auto-generate | Tidak | Ya (dari model SQLAlchemy) | Ya (dari schema.prisma) |
| Rollback | Manual (U script / undo) | Built-in (downgrade) | Manual (migrate resolve) |
| Learning Curve | Rendah (hanya SQL) | Menengah (Python + SQLAlchemy) | Rendah-Medium (schema DSL) |
| Best For | Tim DBA / Java ecosystem | Python web apps | Node.js/TypeScript apps |
| Gratis | Community: Ya, Teams: Berbayar | Ya (open source) | Ya (open source) |
Pilih berdasarkan bahasa dan framework yang Anda gunakan: Flyway untuk Java/tim DBA yang suka SQL murni, Alembic untuk Python (Flask/FastAPI), Prisma Migrate untuk Node.js/TypeScript. Yang terpenting: gunakan salah satu secara konsisten.
7. Strategi Migration
Zero-Downtime Migration
Untuk production dengan traffic tinggi, migration harus dilakukan tanpa downtime. Ini membutuhkan pendekatan khusus:
-- =============================================
-- LANGKAH 1: Tambah kolom baru (tidak locking tabel)
-- =============================================
ALTER TABLE pelanggan ADD COLUMN nomor_whatsapp VARCHAR(20);
-- =============================================
-- LANGKAH 2: Backfill data dari kolom lama
-- Jalankan dalam batch untuk tabel besar
-- =============================================
UPDATE pelanggan
SET nomor_whatsapp = telepon
WHERE nomor_whatsapp IS NULL
LIMIT 10000;
-- Ulangi sampai semua data terisi
-- =============================================
-- LANGKAH 3: Update aplikasi untuk menulis
-- ke kedua kolom (dual write)
-- =============================================
-- Di kode aplikasi:
-- INSERT INTO pelanggan (nama, email, telepon, nomor_whatsapp)
-- VALUES (?, ?, ?, ?);
-- =============================================
-- LANGKAH 4: Verifikasi data konsisten
-- =============================================
SELECT COUNT(*) FROM pelanggan
WHERE telepon IS NOT NULL AND nomor_whatsapp IS NULL;
-- Harusnya 0
-- =============================================
-- LANGKAH 5: Hapus kolom lama (setelah semua
-- aplikasi menggunakan kolom baru)
-- =============================================
ALTER TABLE pelanggan DROP COLUMN telepon;
Expand-and-Contract Pattern
+------------------------------------------------------------------+
| EXPAND-AND-CONTRACT PATTERN |
| |
| Phase 1: EXPAND Phase 2: MIGRATE |
| +------------------------+ +------------------------+ |
| | Tambah kolom baru | | Update data ke kolom | |
| | Tidak hapus yang lama | | baru dari kolom lama | |
| | Kedua kolom aktif | | Dual-write di app | |
| +------------------------+ +------------------------+ |
| |
| Phase 3: CONTRACT Selesai |
| +------------------------+ +------------------------+ |
| | Hapus kolom lama | | Database bersih, | |
| | setelah semua migrasi | | hanya kolom baru | |
| +------------------------+ +------------------------+ |
+------------------------------------------------------------------+
8. Integrasi CI/CD
Migration sebaiknya otomatis dijalankan saat deployment. Berikut contoh integrasi di CI/CD pipeline:
# .github/workflows/deploy.yml
name: Deploy with Migration
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: toko_online
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
# Flyway migration
- name: Run Flyway Migrations
uses: flyway/flyway-action@v0.4.0
with:
url: jdbc:postgresql://localhost:5432/toko_online
user: postgres
password: postgres
locations: filesystem:sql/migrations
command: migrate
# Alembic migration
- name: Run Alembic Migrations
run: |
pip install alembic sqlalchemy psycopg2-binary
alembic upgrade head
env:
DATABASE_URL: postgresql://postgres:postgres@localhost/toko_online
# Prisma migration
- name: Run Prisma Migrations
run: |
npm install
npx prisma migrate deploy
npx prisma generate
env:
DATABASE_URL: postgresql://postgres:postgres@localhost/toko_online
9. Best Practices
| Praktik | Penjelasan |
|---|---|
| Satu migration per perubahan | Jangan gabung beberapa perubahan dalam satu file |
| Buat migration reversible | Selalu tulis downgrade/rollback script |
| Test di staging dulu | Jangan langsung jalankan di production |
| Backup sebelum migrate | Selalu backup database sebelum migration besar |
| Review migration SQL | Periksa file SQL sebelum commit, apalagi auto-generated |
| Jangan edit migration lama | File migration yang sudah dijalankan tidak boleh diubah |
| Gunakan transaksi | Wrap migration dalam transaksi jika didukung database |
| Hindari locking besar | Untuk tabel besar, gunakan teknik tanpa lock (concurrent index) |
Operasi berikut bisa menyebabkan table lock dan downtime pada tabel besar: ALTER TABLE ADD COLUMN NOT NULL, ALTER COLUMN TYPE, ADD CONSTRAINT CHECK. Gunakan CREATE INDEX CONCURRENTLY (PostgreSQL) atau teknik expand-and-contract untuk tabel dengan jutaan baris.
10. Quiz Pemahaman
- Apa itu database migration dan mengapa penting?
Jawaban: Proses mengelola perubahan skema database secara terstruktur dan versioned. Penting untuk reproducibility, kolaborasi tim, dan deployment yang aman. - Apa perbedaan prefix V, R, dan U pada Flyway?
Jawaban: V = Versioned (sekali jalan), R = Repeatable (ulang saat berubah), U = Undo (rollback, versi Enterprise). - Apa keuntungan Alembic autogenerate?
Jawaban: Otomatis membuat file migration dari perbedaan model SQLAlchemy dan database, mengurangi human error. - Apa itu expand-and-contract pattern?
Jawaban: Strategi migration dengan 3 fase: tambah kolom baru (expand), migrasi data (migrate), hapus kolom lama (contract). Memungkinkan zero-downtime. - Mengapa migration yang sudah dijalankan tidak boleh diubah?
Jawaban: Karena checksum file migration di-track oleh tool. Perubahan akan menyebabkan validation error. Buat migration baru untuk perubahan selanjutnya.
Setelah menguasai migration tools, pelajari CouchDB untuk NoSQL document database, atau TimescaleDB untuk time-series data di PostgreSQL.