Database kamu berubah terus. Kolom email di tabel users kemarin masih boleh kosong, sekarang harus unik. Minggu depan kamu butuh tabel orders yang terhubung ke users. Kalau kamu masih menjalankan ALTER TABLE manual langsung di production, cepat atau lambat ada yang rusak di momen paling nggak tepat. Alembic menyelesaikan ini: setiap perubahan skema disimpan sebagai file migration yang bisa kamu commit ke Git, jalankan ulang di server lain, dan rollback kalau ternyata bermasalah.
Alembic adalah migration tool bawaan ekosistem SQLAlchemy. Dia membaca metadata model kamu, membandingkannya dengan kondisi database, lalu menghasilkan skrip upgrade/downgrade. Kalau kamu belum kenal ORM-nya, baca dulu panduan SQLAlchemy supaya konsep Table, Column, dan metadata nggak bikin bingung di sini.
Kenapa Kamu Perlu Migration, Bukan SQL Manual
Bayangkan tiga developer mengerjakan satu aplikasi. Dev A menambah kolom di database lokalnya, Dev B menghapus kolom lain, Dev C mengubah tipe data. Tanpa migration, ketiganya harus sinkron lewat chat atau file SQL yang gampang terlewat. Dengan Alembic, setiap perubahan jadi satu file ber-versi yang berjalan berurutan. Siapa pun yang pull kode terbaru tinggal jalankan alembic upgrade head, dan skema database-nya langsung sama persis dengan yang lain.
Migration juga bisa di-revert. Kalau kolom baru ternyata bikin query lambat, kamu cukup alembic downgrade -1. Coba lakukan itu dengan ALTER TABLE manual yang sudah jalan — kamu harus menulis SQL kebalikannya sendiri, dan biasanya separuhnya lupa.
Setup Proyek
Mulai dari proyek kosong. Saya pakai SQLite supaya kamu bisa ikut tanpa install server database dulu, tapi Alembic jalan sama saja untuk PostgreSQL atau MySQL — tinggal ganti URL-nya nanti.
python -m venv venv
source venv/bin/activate
pip install sqlalchemy alembic
Kalau targetmu PostgreSQL, tambahkan driver psycopg2-binary sekalian. Untuk tutorial ini SQLite sudah cukup.
Inisialisasi Alembic
alembic init alembic
Perintah ini membuat folder alembic/ plus file alembic.ini di root proyek. Strukturnya begini:
proyek/
├── alembic/
│ ├── env.py # dipanggil tiap kali kamu menjalankan perintah alembic
│ ├── script.py.mako # template file migration
│ └── versions/ # kumpulan file migration
├── alembic.ini # konfigurasi utama
└── venv/
Yang paling sering kamu sentuh cuma dua: alembic.ini untuk URL database, dan folder versions/ tempat file migration menumpuk. env.py biasanya cukup diedit sekali.
Hubungkan Alembic ke Database
Cara paling cepat: buka alembic.ini, cari baris sqlalchemy.url, lalu ganti nilainya jadi sqlite:///app.db. Itu saja sudah bisa jalan.
Tapi ada cara yang lebih rapi: baca URL dari satu sumber (misalnya environment variable) lewat env.py, supaya credential nggak tersebar di file config yang ke-commit. Saya lebih suka begini. Edit alembic/env.py dan ganti bagian atasnya:
from sqlalchemy import engine_from_config, pool
from alembic import context
import os
# Ambil URL dari environment variable, fallback ke SQLite lokal
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///app.db")
config = context.config
config.set_main_option("sqlalchemy.url", DATABASE_URL)
# Import Base dari model kamu dan pakai sebagai target autogenerate
from app.models import Base
target_metadata = Base.metadata
Kalau kamu belum punya model SQLAlchemy, jangan panik. target_metadata bisa diisi belakangan — bagian itu hanya dipakai saat autogenerate, bukan saat upgrade/downgrade biasa.
Migration Pertama: Buat Tabel Users
Jalankan perintah ini untuk membuat file migration kosong:
alembic revision -m "create users table"
Alembic membuat file baru di alembic/versions/ dengan nama acak seperti 3f8a1c2d9e4b_create_users_table.py. Buka file itu. Ada dua fungsi yang harus kamu isi: upgrade() untuk maju dan downgrade() untuk mundur.
"""create users table
Revision ID: 3f8a1c2d9e4b
Revises:
Create Date: 2026-10-01 08:00:00.000000
"""
from alembic import op
import sqlalchemy as sa
revision = "3f8a1c2d9e4b"
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
op.create_table(
"users",
sa.Column("id", sa.Integer, primary_key=True),
sa.Column("email", sa.String(255), nullable=False, unique=True),
sa.Column("name", sa.String(255), nullable=False),
sa.Column("created_at", sa.DateTime, server_default=sa.func.now()),
)
def downgrade() -> None:
op.drop_table("users")
upgrade() membuat tabel users dengan empat kolom, downgrade() menghapusnya lagi. Aturan emasnya: downgrade harus membatalkan persis apa yang upgrade lakukan, supaya kamu bisa naik-turun versi tanpa database rusak.
Jalankan dan Rollback
alembic upgrade head
head artinya versi paling baru. Jalankan perintah ini dan tabel users langsung muncul. Cek statusnya dengan dua perintah ini:
alembic current # versi database sekarang
alembic history # daftar semua migration
Sekarang coba rollback. Perintah di bawah mundur satu versi, dan tabel users hilang lagi.
alembic downgrade -1
Jangan pernah mengedit file migration yang sudah di-commit dan sudah dijalankan orang lain. Mengubah migration lama hanya bikin versi database di tim kamu tidak sinkron. Kalau ada kesalahan, buat migration baru yang memperbaikinya.
Autogenerate dari Model SQLAlchemy
Menulis upgrade/downgrade manual melelahkan kalau tabelnya puluhan. Alembic bisa membandingkan metadata model kamu dengan database asli, lalu membuat migration-nya otomatis. Mulai dari model baru:
from sqlalchemy import ForeignKey, Numeric
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class Order(Base):
__tablename__ = "orders"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
total: Mapped[float] = mapped_column(Numeric(10, 2))
Pastikan target_metadata di env.py sudah menunjuk ke Base.metadata (sudah kita lakukan tadi). Lalu jalankan:
alembic revision --autogenerate -m "add orders table"
Alembic memindai model, membandingkannya dengan database, lalu menghasilkan migration berisi op.create_table("orders", ...) lengkap dengan foreign key ke users. upgrade/downgrade-nya sudah terisi otomatis.
Autogenerate tidak membaca pikiranmu. Perubahan seperti rename kolom terdeteksi sebagai drop kolom lama + buat kolom baru, yang bisa menghapus data. Selalu baca file hasil autogenerate sebelum di-commit, apalagi sebelum dijalankan di production.
Data Migration: Ubah Isi, Bukan Cuma Skema
Tidak semua migration cuma soal kolom. Kadang kamu perlu memindahkan data, misalnya menggabungkan kolom first_name dan last_name menjadi full_name. Itu pekerjaan op.execute.
def upgrade() -> None:
op.add_column("users", sa.Column("full_name", sa.String(255), nullable=True))
op.execute("UPDATE users SET full_name = first_name || ' ' || last_name")
op.alter_column("users", "full_name", nullable=False)
def downgrade() -> None:
op.drop_column("users", "full_name")
Tiga langkah: tambah kolom nullable, isi datanya dengan SQL mentah lewat op.execute, lalu set nullable=False setelah semua baris terisi. Urutan ini penting — kalau kamu langsung bikin kolom NOT NULL sebelum datanya diisi, migration bakal gagal.
Migration di Tim dan Production
Begitu proyek di-deploy, migration jadi bagian dari proses rilis. Pola paling umum: jalankan alembic upgrade head sebelum aplikasi menyala. Di Docker itu satu command di entrypoint; di CI/CD, satu step sebelum deploy.
# contoh entrypoint container
alembic upgrade head && uvicorn app.main:app --host 0.0.0.0
Alembic paling sering saya pakai di proyek backend FastAPI dan database PostgreSQL. Kalau kamu sedang membangun API-nya, tutorial membuat REST API dengan FastAPI dan PostgreSQL dengan Python bakal melengkapi alur kerjamu dari skema sampai endpoint.
Ringkasan Perintah
alembic init alembic # setup awal
alembic revision -m "pesan" # migration kosong (manual)
alembic revision --autogenerate -m "pesan" # migration dari model
alembic upgrade head # ke versi terbaru
alembic downgrade -1 # mundur satu langkah
alembic current # versi database saat ini
alembic history # riwayat migration
Langkah paling cepat untuk benar-benar paham: buat satu migration manual, jalankan, rollback, lalu coba autogenerate dengan dua model yang saling ber-relasi. Setelah itu kamu bakal paham kenapa pola migration jadi standar di hampir semua proyek Python yang pakai SQLAlchemy — dari Flask sampai FastAPI.