Yazılarımız

Veri Akademi

EF CORE MİGRATİONS STRATEJİSİ: GÜVENLİ ŞEMA DEĞİŞİKLİKLERİ VE VERSİYONLAMA

Üretimde çalışan bir uygulamada veritabanı şemasını değiştirmek, yalnızca “bir tablo eklemek” kadar basit değildir. Minik bir kolon değişikliği bile, farklı sürümlerin aynı anda çalıştığı dağıtım anlarında hatalara, performans düşüşlerine ve veri kaybı risklerine yol açabilir. Bu yüzden EF Core migrations stratejisi, ekiplerin büyüdüğü ve teslimat hızının arttığı projelerde kritik bir mühendislik konusu hâline gelir.

Bu makalede, EF Core migrations kullanımını “komut çalıştırıp geçmek” seviyesinden çıkarıp, güvenli şema değişiklikleri ve veritabanı versiyonlama hedefiyle ele alacağız. Yerel geliştirme, ekip içi çatışma yönetimi, CI/CD hatları, üretim dağıtımı, idempotent script üretimi, geri dönüş planı ve migration biriktirme (squash) gibi pratik başlıkları adım adım bağlayacağız.

Hedefimiz: Şema değişikliklerini kontrollü, tekrarlanabilir ve denetlenebilir bir sürece oturtmak. Sonuçta bir migration dosyası yalnızca kod değildir; doğru yaklaşımla, sistemin “şema sözleşmesi”nin versiyonlu tarihçesidir.


Primary keyword: EF Core migrations stratejisi ile riskleri görünür kılmak

Şema değişikliği neden uygulama sürümüyle birlikte düşünülmeli?

Modern dağıtım yöntemlerinde, yeni sürüm devreye alınırken eski sürüm bir süre daha çalışabilir. Bu aralıkta uygulama, hem eski hem yeni şemayla “uyumlu” davranmak zorunda kalır. Aksi hâlde, örneğin yeni kodun beklediği bir kolon henüz yoksa hata alırsınız; ya da eski kod, yeni zorunlu kılınmış bir alanı yazamaz ve kayıt akışı bozulur.

Risk sınıflandırması: DDL, veri dönüşümü ve davranış değişimi

Şema değişikliklerini üç sınıfa ayırmak pratik bir kontrol listesi oluşturur: (1) DDL değişiklikleri (tablo/kolon/index), (2) veri dönüşümleri (backfill, normalizasyon), (3) uygulama davranışı (okuma-yazma kuralları). Bu ayrım, dağıtım planını daha net kılar ve her adım için test/geri dönüş stratejisi üretmeyi kolaylaştırır.

Migration workflow: Branch, PR ve ekip içi çakışmaları yönetmek

Bir migration dosyası neden “merge çatışması” üretir?

EF Core, her migration için bir sınıf ve ModelSnapshot üretir. Aynı DbContext üzerinde paralel branch’lerde migration üretildiğinde, snapshot dosyası farklılaşır ve merge sırasında çatışma doğar. Bu çatışma, “metin birleştirme” ile körlemesine çözülürse, yanlış snapshot yüzünden yanlış migration zinciri oluşabilir.

Önerilen akış: migration üretimini PR’a bağlamak

Kurumsal ekiplerde sık uygulanan model şudur: Her PR, ilgili şema değişikliği gerekiyorsa migration’ı da içerir. PR incelemesinde yalnızca kod değil, migration içeriği de gözden geçirilir. Böylece “şema değişikliği fark edilmeden üretime çıktı” türü sürprizlerin önüne geçilir.

  • Tek amaçlı migration: Her migration mümkünse tek bir iş ihtiyacını kapsar.
  • İsim standardı: “AddCustomerStatus”, “IndexOrdersCreatedAt” gibi niyeti açık adlar kullanılır.
  • Snapshot disiplin: Snapshot, migration’ların doğal sonucu olarak doğru kalır; elle düzenleme istisnadır.

Ekipte farklı ortamlar ve çoklu servisler varsa, bu yaklaşım bir “migration workflow” standardına dönüşür. Daha kapsamlı örnekleri Entity Framework Core Eğitimi içeriğinde adım adım işleyebilirsiniz.

Güvenli şema değişikliği: Zero downtime yaklaşımı

İki aşamalı genişlet-daralt (expand-contract) tekniği

Kesintisiz dağıtım hedefleniyorsa, en güvenli yöntem expand-contract yaklaşımıdır. Önce şemayı geriye dönük uyumlu şekilde genişletirsiniz (expand): yeni kolon eklenir, varsayılanlar tanımlanır, eski kod etkilenmez. Ardından uygulama yeni alanı kullanacak şekilde devreye alınır. Son aşamada, artık kullanılmayan eski alanlar temizlenir (contract).

Geriye dönük uyumluluk kontrolü

Bu yaklaşımın özü, bir süre boyunca iki şema sürümünü aynı anda desteklemektir. Örneğin yeni kolon nullable eklenir, uygulama önce “yaz, ama okumayı eskiden de yap” şeklinde güncellenir. Veri dolduktan sonra kısıtlar sıkılaştırılır. Bu sayede, dağıtım anında sürüm karışıklığı yaşansa bile sistem ayakta kalır.

Dağıtım sırasında uygulama sürümleri arasında uyumluluk için şema genişletme ve daraltma adımlarını anlatan düzen

Üretim dağıtımı: Script tabanlı yaklaşım ve idempotent script

Neden üretimde “dotnet ef database update” riskli olabilir?

Üretimde doğrudan uygulama sunucusundan migration çalıştırmak, kimlik bilgileri, ağ erişimi, hata yönetimi ve gözlemlenebilirlik gibi konularda risk doğurur. Ayrıca bir hata durumunda hangi adımın nerede başarısız olduğunu anlamak zorlaşır. Bu nedenle birçok ekip, üretim için migration’ı script tabanlı bir artefakt olarak üretir ve kontrollü şekilde uygular.

Idempotent script ile tekrarlanabilirlik

Idempotent script, daha önce uygulanmış migration’ları tekrar uygulamadan, yalnızca eksik olanları çalıştırır. Bu, staging/production gibi ortamlarda “hangi migration kaldı?” sorusunu daha yönetilebilir kılar. EF Core, bu ihtiyaca yönelik script üretimini destekler.

// Idempotent script üretimi (SQL Server örneği)
dotnet ef migrations script --idempotent --output artifacts/migrations.sql

// Belirli aralık için script
dotnet ef migrations script 202602010915_AddCustomerStatus 202602021030_IndexOrdersCreatedAt --output artifacts/range.sql

Script üretimini CI sürecine bağlamak, sürüm artefaktlarıyla şema değişikliklerini aynı paket içinde taşımayı sağlar. Böylece “uygulama sürümü X, şema sürümü Y” uyumsuzluklarının erken yakalanma şansı artar.

Veri dönüşümleri: Backfill, büyük tablolar ve performans tuzakları

Migration içinde veri güncellemesi ne zaman sorun çıkarır?

Migration içinde veri taşıma/backfill yapmak, küçük tablolarda pratik görünse de büyük veri hacminde kilitlenmelere ve uzun süren transaction’lara neden olabilir. Üretimde bu tür işlemler, uygulama trafiğini etkileyebilir. Bu yüzden veri dönüşümlerini “şema değişikliği”nden ayrı bir operasyon olarak planlamak çoğu zaman daha güvenlidir.

Örnek: Yeni kolonu ekle, sonra kontrollü backfill yap

Tipik güvenli senaryo: Önce nullable bir kolon eklersiniz. Uygulama yeni kayıtlarda bu alanı doldurmaya başlar. Eski kayıtlar için backfill işlemini, ayrı bir job veya bakım penceresiyle yönetirsiniz. Son aşamada kolon üzerinde kısıtları sıkılaştırırsınız.

public partial class AddCustomerStatus : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<string>(
            name: "Status",
            table: "Customers",
            type: "nvarchar(32)",
            nullable: true);

        // Büyük tabloda backfill'i migration içine gömmek yerine
        // uygulama/iş job'u ile kademeli doldurma tercih edilir.
        migrationBuilder.Sql(
            "UPDATE Customers SET Status = 'Active' WHERE Status IS NULL AND IsDeleted = 0");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropColumn(
            name: "Status",
            table: "Customers");
    }
}

Yukarıdaki örnek, küçük/orta ölçekli veri için kabul edilebilir olabilir; ancak büyük tablolar için transaction süresi, lock davranışı ve indeks etkileri mutlaka değerlendirilmelidir. Alternatif olarak “batch update”, kuyruk tabanlı işlem veya arka plan job yaklaşımı daha sağlıklı olabilir.

Rollback planı: Geri dönüş stratejisini baştan tasarlamak

“Down” metodu her zaman güvenilir geri dönüş değildir

EF Core migrations içinde Down metodu bulunsa da, üretimde “geri dönüş” çoğu zaman yalnızca şemayı geriye almak değildir. Veri kaybı içeren değişikliklerde (kolon silme, tip daraltma) Down çalışsa bile veri geri gelmez. Bu nedenle rollback planı, şema ve veri açısından ayrı düşünülmelidir.

Pratik yaklaşım: Geriye uyumlu adımlar + feature flag

Kesintiyi azaltmanın etkili yolu, riskli davranış değişimlerini feature flag ile yönetmektir. Önce şemayı genişletirsiniz, sonra kodu deploy edersiniz; fakat yeni davranışı kademeli açarsınız. Problem çıkarsa, flag kapatılarak hızlıca “işlevsel rollback” sağlanır. Bu, tam bir şema geri dönüşünden daha hızlı ve daha az risklidir.

Migration squashing ve baseline: Tarihçeyi sadeleştirmek

Ne zaman squash mantıklıdır?

Uzun yaşayan projelerde migration sayısı yüzleri bulabilir. Yeni bir ortam kurulumunda tüm migration’ların tek tek çalışması zaman alır. Bu noktada migration squashing yaklaşımıyla, belirli bir noktaya kadar olan zinciri “baseline” olarak birleştirmek mantıklı olabilir. Ama bu işlem, özellikle çoklu ortamlar ve üretimde devam eden dağıtımlar varken dikkat ister.

Baseline yaklaşımı nasıl kurgulanır?

Yaygın model: Üretimde belirli bir sürümde sabitlenirsiniz, o sürüme kadar olan migration’ları yeni bir başlangıç migration’ına dönüştürürsünüz. Yeni ortamlarda baseline uygulanır, sonra sadece yeni migration’lar devreye girer. Burada kritik olan, baseline noktası ile üretimdeki migration tablosunun tutarlılığıdır.

CI/CD ve kalite kapıları: Migration’ı test eden otomasyon

Pipeline’da hangi kontroller değer üretir?

Şema değişikliği güvenliği, yalnızca migration üretmekle tamamlanmaz. CI/CD hattında birkaç basit kalite kapısı bile büyük kazanç sağlar: migration dosyası var mı, snapshot tutarlı mı, migration çalışıyor mu, idempotent script üretilebiliyor mu, ve integration test’ler gerçek veritabanında geçiyor mu.

  1. Geçici bir veritabanı oluştur ve migration’ları uygula.
  2. Uygulama başlatma testinde DbContext ile temel sorguları çalıştır.
  3. Idempotent script üret ve artefakt olarak sakla.
  4. Şema değişikliğini etkileyen sorgular için performans testini tetikle.

Bu kontroller, “migration production’da patladı” senaryosunu erken aşamada yakalar. Ayrıca denetlenebilirlik artar: hangi sürümde hangi şema değişikliği yapıldı, hangi script üretildi, hangi pipeline çalıştırdı gibi sorulara cevap üretirsiniz.

CI hattında migration doğrulaması, idempotent script üretimi ve veritabanı testlerinin akış halinde gösterildiği düzen

Gerçek hayat senaryoları: Çoklu servis ve çoklu tenant

Birden fazla servis aynı veritabanını paylaşıyorsa

Mikro servis mimarisinde ideal olan her servisin kendi veritabanına sahip olmasıdır; fakat geçiş dönemlerinde veya raporlama ihtiyaçlarında paylaşımlı veritabanı görülebilir. Bu durumda migrations stratejisi daha kritik olur: hangi servis “şema sahibi”dir, migration’ı kim uygular, diğer servisler hangi sözleşmeye uyar?

Bu senaryoda, şema sahipliğini netleştirmek ve migrations’ı tek bir “owner” servise bağlamak genellikle daha güvenlidir. Diğer servisler, şemaya erişirken sürüm uyumluluğunu test etmekle sorumlu olur.

Multi-tenant yaklaşımda sürüm yönetimi

Multi-tenant yapılarda, her tenant’ın aynı anda güncellenmesi mümkün olmayabilir. Tenant bazlı migration takibi, sürüm sapmalarını yönetmeyi gerektirir. Burada idempotent script, kademeli rollout ve tenant bazlı sağlık kontrolleri öne çıkar. Ayrıca “her tenant aynı şemada mı?” sorusu için izlenebilir metrikler gerekir.

Özet: Sürdürülebilir bir migrations stratejisi için kontrol listesi

Tekrarlanabilirlik, gözlemlenebilirlik, geri dönüş

Sürdürülebilir bir strateji, her ortamda aynı şekilde uygulanabilen ve sonucu gözlemlenebilen bir süreçtir. Veritabanı versiyonlama yaklaşımı, uygulama sürümüyle şema sürümünü birlikte yönetmeyi hedefler. Bu da ekiplerin teslimat hızını düşürmeden güvenliği artırır.

  • Expand-contract ile geriye uyumlu değişiklikler tasarla.
  • Migration üretimini PR incelemesine dahil et; snapshot çatışmalarını bilinçli çöz.
  • Üretimde script tabanlı yaklaşımı tercih et, idempotent script üret.
  • Veri dönüşümlerini ayrı operasyon olarak planla; büyük tabloları koru.
  • Rollback’i yalnızca Down’a bırakma; feature flag ve uyumlu adımlarla kurgula.
  • CI/CD’de migration doğrulaması ve gerçek veritabanı testlerini standardize et.

Bu çerçeveyle, EF Core migrations yalnızca bir araç değil, şema değişikliklerini güvenle taşıyan bir teslimat pratiğine dönüşür. Böylece hem ekip içi koordinasyon güçlenir hem de üretimde sürprizlerin payı azalır.

 VERİ AKADEMİ