← Back to Cecep Azhar
Cecep Azhar #golang#database#migration#postgresql#schema

Database Migration dengan Go: Panduan Lengkap

Cecep Saeful Azhar • 16 Oct 2026
Database Migration dengan Go: Panduan Lengkap
Panduan database migration di Go. Schema versioning, rollback strategy, dan tools migration untuk production.

Database Migration: Bukan Sekedar ALTER TABLE

Saya pernah melakukan manual migration ke production database. Hasilnya? Schema yang tidak konsisten antara development dan production, data yang corrupt, dan 2 jam panic. Database migration harus selalu automated dan versioned.

Approach 1: GORM AutoMigrate Untuk Development

// Untuk development only
db.AutoMigrate(&User{}, &Project{}, &Task{})

// Yang bisa dilakukan AutoMigrate:
// Create new table, Add new column, Add new index
// TIDAK BISA: Drop column, Rename column, Modify column type

Approach 2: goose Recommended untuk Production

goose adalah migration tool yang saya pakai di production. Simple, reliable, dan support Go migration files.

// migrations/001_create_users.go
package migrations

import (
    "context"
    "database/sql"
)

func Up_001(ctx context.Context, tx *sql.Tx) error {
    query := `
        CREATE TABLE IF NOT EXISTS users (
            id SERIAL PRIMARY KEY,
            name VARCHAR(100) NOT NULL,
            email VARCHAR(255) UNIQUE NOT NULL,
            password_hash VARCHAR(255) NOT NULL,
            created_at TIMESTAMP DEFAULT NOW(),
            updated_at TIMESTAMP DEFAULT NOW()
        );
        CREATE INDEX idx_users_email ON users(email);
    `
    _, err := tx.ExecContext(ctx, query)
    return err
}

func Down_001(ctx context.Context, tx *sql.Tx) error {
    _, err := tx.ExecContext(ctx, "DROP TABLE IF EXISTS users;")
    return err
}

Approach 3: golang-migrate

// migrations/001_create_users.up.sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
);

// migrations/001_create_users.down.sql
DROP TABLE IF EXISTS users;
import "github.com/golang-migrate/migrate/v4"

func RunMigrations(dbURL string) error {
    m, err := migrate.New("file://migrations", dbURL)
    if err != nil {
        return fmt.Errorf("migration init: %w", err)
    }
    if err := m.Up(); err != nil && err != migrate.ErrNoChange {
        return fmt.Errorf("migration up: %w", err)
    }
    return nil
}

Best Practices Production

  1. Selalu punya rollback (down migration) Jangan pernah skip ini.
  2. Backup sebelum migrate Automate backup di CI/CD pipeline sebelum apply migration
  3. Test migration di staging Jalankan full migration sequence di staging sebelum production
  4. Separate schema changes dari data migration Schema aman untuk rollback, data butuh extra caution
  5. Lock schema during migration Untuk tabel besar, pertimbangkan online DDL tools

Database migration mungkin terlihat simple, tapi ini adalah bagian kritis dari operasi SaaS. Satu migration yang buruk bisa menghancurkan production. Treat it seriously.

Handling Data Migration

Data migration (mengubah isi data, bukan struktur schema) membutuhkan pendekatan berbeda dari schema migration. Ini tips dari pengalaman saya:

  1. Jangan batch update tabel besar dalam satu transaksi pecah menjadi chunk kecil untuk menghindari lock yang panjang
  2. Test di staging dengan production-size data data migration yang cepat di dev bisa sangat lambat di production
  3. Siapkan rollback script selalu simpan data asli sebelum diubah
  4. Gunakan online DDL tools untuk tabel besar
func MigrateUserEmails(db *gorm.DB) error {
    batchSize := 1000
    var lastID uint = 0

    for {
        var users []User
        db.Where("id > ?", lastID).Limit(batchSize).Find(&users)
        if len(users) == 0 { break }

        for _, user := range users {
            newEmail := normalizeEmail(user.Email)
            db.Model(&user).Update("email", newEmail)
        }
        lastID = users[len(users)-1].ID
    }
    return nil
}

Version Control untuk Migration Files

Migration files harus di-version control bersama kode aplikasi. Ini memastikan semua developer dan environments memiliki migration yang konsisten. Jangan pernah apply migration langsung ke production tanpa melalui version control dan code review.

Untuk tim yang lebih besar, pertimbangkan branching strategy untuk migration. Misalnya: branch feature membuat migration baru, dan hanya di-merge ke main branch setelah code review dan testing di staging.

← More articles from Cecep Azhar Central Content Hub Home →