# CI/CD'de veritabanı göçlerini (migrations) güvenli nasıl çalıştırırım?

> Kolonu nullable ekleyip backfill'i ayrı throttle'lı bir adıma alın, migration'ı deploy'dan ayrı bir aşamada koşturun, eskiyi sonraki release'te düşürün.

- Soruldu: 2026-06-11
- Yanıtlandı: 2026-06-12
- Soran: Melih
- Etiketler: ci-cd, veritabani, laravel
- Kaynak: https://muhammetsafak.com/tr/sor-bakalim/ci-cd-de-veritabani-goclerini-guvenli-calistirmak/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** CI/CD sürecimizde `php artisan migrate` deployment adımları arasında otomatik çalışıyor. Bir migration çok büyük bir tabloya kolon eklerse ve uzun sürerse pipeline timeout'a düşüyor; ya da o sırada canlıda olan eski kod, DB değişikliği yüzünden hata vermeye başlıyor.

Büyük projelerde "backward-compatible migration" prensiplerini CI/CD akışında nasıl uygularım?


Kısa cevap: Asıl hata, şema değişikliğini eski kod hâlâ canlıyken deploy'a sıkı sıkıya bağlamak — çözüm, geriye uyumlu (expand/contract) migration'ları kod deploy'undan **ayırmak**.

## Kısa cevap

Problemi netleştirelim: ya eski kod yeni şemayla karşılaşıp patlıyor, ya da büyük bir `ALTER` tabloyu kilitleyip pipeline'ı timeout'a düşürüyor. İkisi de aynı kökten — kuplaj. Aynı geçişin canlı tablo tarafını, yani veri kaybetmeden dual-write ile şema değiştirmeyi [ayrı bir kayıtta](/tr/sor-bakalim/canli-tabloda-dual-write-ile-sifir-kayipli-sema-gecisi/) ele almıştım; buradaki soru o geçişi CI/CD adımlarına nasıl dağıtacağınız.

## Neden

1. **Eski kod yeni şemayı görmek zorunda kalıyor.** Deploy sırasında bir süre boyunca canlıda olan kod, henüz kendisi için yazılmamış bir şemayla konuşur; kolonu tanımadığı ya da beklediği kolonu bulamadığı anda hata vermeye başlar.

2. **Büyük ve kilitleyici bir `ALTER` deploy adımının içine sığmaz.** Milyonlarca satırlık bir tabloda bazı şema değişiklikleri dakikalar sürer. PostgreSQL 17'de bir kolonun tipini değiştirmek ya da volatile `DEFAULT`'lu kolon eklemek tabloyu `ACCESS EXCLUSIVE` kilidi altında baştan yazar; düz, nullable bir `ADD COLUMN` yeniden yazımı atlar ama yine de o kilidi alır. MySQL 8.4'te `ADD COLUMN` varsayılan olarak `INSTANT`'tır, ama her işlem öyle değildir. Ağır iş deploy adımının içindeyse pipeline timeout'a düşer, tablo da o süre boyunca kilitli kalır.

3. **Kök neden kuplaj.** Şema değişikliği ile kod deploy'u aynı ana bağlandığı sürece bu iki hata da kaçınılmazdır; çözüm de tek tek semptomları değil, bu bağı gevşetmektir.

## Ne yapmalı

1. **Expand/contract disiplinini benimse.** Aynı release'de bir kolonu ekleyip onu gerektiren kodu deploy etme; asla rename/drop'u kullanan release ile aynı anda yapma. Önce kolonu **nullable** ekle, sonra "hem eskiye hem yeniye yazan" kodu deploy et, en son ayrı bir release ile eskiyi kaldır.

2. **Backfill'i request yolundan ve deploy adımından çıkar.** Büyük tabloyu doldurma işini ayrı, **throttle'lı** bir adımda yap — deploy adımının transaction'ı içinde değil. Aksi halde tek bir dev `UPDATE` hem kilitler hem timeout'a sokar.

3. **Migration'ı kendi pipeline aşaması yap, timeout baskısı olmadan.** Şema değişikliğini app deploy'undan ayrı bir stage'e al. Büyük/kilitleyici değişiklikler için `pt-online-schema-change` / `gh-ost` gibi online-DDL araçlarıyla **bant dışı** çalıştır; tabloyu kilitlemeden kolonu ekler.

4. **İdempotent ve geri alınabilir yaz, deploy'u migration'a gate'le.** Migration tekrar çalıştığında bozulmasın, geri dönüşü olsun. Deploy'u migration başarısına bağla; ama yıkıcı adımları (drop, rename) yeni kod tamamen yayıldıktan sonraki bir follow-up release'e bırak.

**Sonuç:** Ben olsam expand/contract'ı kural yapar, migration'ları app deploy'undan ayırır ve büyük/kilitleyici değişiklikleri online araçlarla kritik yolun dışında çalıştırırdım. Şema değişikliğini hiçbir zaman "deploy'un ortasında, eski kod canlıyken" yapma — onu kademeli ve geriye uyumlu yürüt; pipeline timeout'u da, eski kodun patlaması da o zaman ortadan kalkar.

## İlgili Yazılar

- [DeployerPHP zero-downtime deploy'da OPcache'ten gelen 500 hatalarını nasıl engellerim?](https://muhammetsafak.com/tr/sor-bakalim/deployerphp-ile-sifir-kesinti-deploy-ve-opcache-temizligi/) — Sor Bakalım
- [Canlı tabloda dual-write ile sıfır kayıplı şema geçişini nasıl koordine ederim?](https://muhammetsafak.com/tr/sor-bakalim/canli-tabloda-dual-write-ile-sifir-kayipli-sema-gecisi/) — Sor Bakalım
- [Birçok modele bağlanan yorumlar için polymorphic ilişki mi yoksa ayrı tablolar mı; index ve foreign key açısından hangisi daha sağlıklı?](https://muhammetsafak.com/tr/sor-bakalim/bircok-modele-baglanan-yorumlar-polymorphic-iliski-ayri-tablolar/) — Sor Bakalım
