Merhaba arkadaşlar, bugün Laravel'de migration yazarken başıma gelen ve bir deployment'ı neredeyse mahvedecek bir durumu nasıl önlediğimi anlatacağım. Hepimiz yeni bir tablo eklerken veya bir kolonu değiştirirken heyecanla `php artisan migrate` yazıyoruz. Peki ya bir şeyler ters giderse ve `php artisan migrate:rollback` çalıştırmamız gerekirse? İşte o zaman işin rengi değişiyor. Ben de bu yüzden artık her migration'ı yazarken, onun nasıl geri alınacağını (rollback) düşünerek yazıyorum.
Neden Rollback Planı Şart?
Geçenlerde production sunucusunda bir migration çalıştırdım. Migration, mevcut bir tabloya yeni bir foreign key constraint'i ekliyordu. Her şey yolunda gibi görünüyordu, ta ki bu constraint'in hatalı bir veri yapısı nedeniyle başarısız olduğunu görene kadar. Panikle `migrate:rollback` yazdım ve... hiçbir şey olmadı! Migration dosyasının `down` metodu neredeyse boştu. Çünkü migration'ı yazarken sadece ileri yönü (`up`) düşünmüştüm. O an anladım ki, rollback planı olmayan bir migration, geri dönüşü olmayan bir adım gibi.
Uyguladığım Temel Best Practice'ler
Artık her migration dosyamı açtığımda, ilk olarak `down` metodunu yazmaya başlıyorum. `up` metodunda ne yapıyorsam, `down` metodunda tam tersini yapmayı prensip edindim. İşte benim için artık altın kural haline gelen maddeler:
1. Tabloyu sil (drop) ettiysen, down metodunda tekrar oluştur (create). Ama dikkat! Sadece `Schema::create` demek yetmez, tablonun orijinal kolonlarını da aynen tanımlamalısın.
2. Bir tabloya kolon eklediysen (addColumn), down metodunda o kolonu sil (dropColumn).
3. Foreign key ekliyorsan, onu silmeyi unutma. Bu en çok unutulanlardan biri.
İşte basit bir örnek:
Gördüğünüz gibi, `up`'ta `user_id` ekleyip foreign key atıyorum. `down` metodunda ise önce foreign key'i (çakışma yaşanmaması için), sonra kolonu siliyorum.
İleri Seviye Senaryolar ve Tuzaklar
Bazen işler bu kadar basit olmuyor. Örneğin, mevcut bir kolonun veri tipini değiştirdiğinizde (`string`'den `text`'e), rollback'te eski haline döndürmeniz gerekir. Bu durumda veri kaybı yaşanabilir! Bu yüzden böyle kritik migration'ları yazmadan önce mutlaka veritabanını yedekliyorum.
Bir diğer tuzak, çok büyük tablolarda index eklemek/silmek. Production'da bu işlem çok uzun sürebilir ve timeout'a neden olabilir. Bu tarz migration'ları gece saatlerinde veya düşük trafikte çalıştırmak için planlama yapıyorum. Rollback'in de aynı şekilde uzun süreceğini unutmamak lazım.
Sonuç ve Tavsiyem
Artık her migration dosyamı yazdıktan sonra son bir kontrol yapıyorum: "Acaba bu migration'ı `rollback` etmek istesem, `down` metodu beni tam olarak migration öncesi haline geri götürür mü?" Eğer cevap evet değilse, migration'ı göndermiyorum.
Bu disiplini oturtmak ilk başta ekstra zaman gibi görünse de, bir kere canınız yandığında ne kadar değerli olduğunu anlıyorsunuz. Siz de Laravel'de migration yazarken rollback'i baştan düşünüyor musunuz? Ya da başınıza gelen ve sizi bu konuda "ehil" eden ilginç rollback hikayeleriniz var mı? Yorumlarda paylaşalım!
Geçenlerde production sunucusunda bir migration çalıştırdım. Migration, mevcut bir tabloya yeni bir foreign key constraint'i ekliyordu. Her şey yolunda gibi görünüyordu, ta ki bu constraint'in hatalı bir veri yapısı nedeniyle başarısız olduğunu görene kadar. Panikle `migrate:rollback` yazdım ve... hiçbir şey olmadı! Migration dosyasının `down` metodu neredeyse boştu. Çünkü migration'ı yazarken sadece ileri yönü (`up`) düşünmüştüm. O an anladım ki, rollback planı olmayan bir migration, geri dönüşü olmayan bir adım gibi.
Artık her migration dosyamı açtığımda, ilk olarak `down` metodunu yazmaya başlıyorum. `up` metodunda ne yapıyorsam, `down` metodunda tam tersini yapmayı prensip edindim. İşte benim için artık altın kural haline gelen maddeler:
1. Tabloyu sil (drop) ettiysen, down metodunda tekrar oluştur (create). Ama dikkat! Sadece `Schema::create` demek yetmez, tablonun orijinal kolonlarını da aynen tanımlamalısın.
2. Bir tabloya kolon eklediysen (addColumn), down metodunda o kolonu sil (dropColumn).
3. Foreign key ekliyorsan, onu silmeyi unutma. Bu en çok unutulanlardan biri.
İşte basit bir örnek:
PHP:
<?php
// ... migration sınıf tanımı
public function up()
{
Schema::table('orders', function (Blueprint $table) {
$table->foreignId('user_id')->constrained()->onDelete('cascade');
});
}
public function down()
{
Schema::table('orders', function (Blueprint $table) {
// FOREIGN KEY ismini belirtmek çok önemli!
$table->dropForeign(['user_id']);
$table->dropColumn('user_id');
});
}
Gördüğünüz gibi, `up`'ta `user_id` ekleyip foreign key atıyorum. `down` metodunda ise önce foreign key'i (çakışma yaşanmaması için), sonra kolonu siliyorum.
Bazen işler bu kadar basit olmuyor. Örneğin, mevcut bir kolonun veri tipini değiştirdiğinizde (`string`'den `text`'e), rollback'te eski haline döndürmeniz gerekir. Bu durumda veri kaybı yaşanabilir! Bu yüzden böyle kritik migration'ları yazmadan önce mutlaka veritabanını yedekliyorum.
Bir diğer tuzak, çok büyük tablolarda index eklemek/silmek. Production'da bu işlem çok uzun sürebilir ve timeout'a neden olabilir. Bu tarz migration'ları gece saatlerinde veya düşük trafikte çalıştırmak için planlama yapıyorum. Rollback'in de aynı şekilde uzun süreceğini unutmamak lazım.
Artık her migration dosyamı yazdıktan sonra son bir kontrol yapıyorum: "Acaba bu migration'ı `rollback` etmek istesem, `down` metodu beni tam olarak migration öncesi haline geri götürür mü?" Eğer cevap evet değilse, migration'ı göndermiyorum.
Bu disiplini oturtmak ilk başta ekstra zaman gibi görünse de, bir kere canınız yandığında ne kadar değerli olduğunu anlıyorsunuz. Siz de Laravel'de migration yazarken rollback'i baştan düşünüyor musunuz? Ya da başınıza gelen ve sizi bu konuda "ehil" eden ilginç rollback hikayeleriniz var mı? Yorumlarda paylaşalım!