Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Yazı Tipi BoyutlandırıcıAa
  • Anasayfa
  • Teknoloji
    • Siber Güvenlik
    • Yapay Zeka
    • Donanım
    • Bilim
  • Yazılım
  • Savunma & İstihbarat
  • Oyun
  • Yaşam
    • Finans
    • Sinema
    • Dünyadan Haberler
  • İş Birliği
Okuma: Üretimin Bana Öğrettiği, Hiçbir Eğitimin Veremediği Yedi Şey
Paylaş
Yazı Tipi BoyutlandırıcıAa
Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Ara
Bizi Takip Et
  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti
© 2026 Teknomers. All Rights Reserved.

Anasayfa » Üretimin Bana Öğrettiği, Hiçbir Eğitimin Veremediği Yedi Şey

Yazılım

Üretimin Bana Öğrettiği, Hiçbir Eğitimin Veremediği Yedi Şey

teknomers
Son güncelleme: 14 Temmuz 2026 22:44
teknomers
Paylaş
Paylaş

Son birkaç ayımı Shippo üzerinde bir gönderi entegrasyonu oluşturmakla geçirdim ve bu süreçte aynı dersi tekrar tekrar yaşadım: Laravel kodu yazmanın “hızlı” yolu genellikle işe yarar — ta ki yaramadığı ana kadar. Bu bir teorik yazı değil. Aşağıdaki her desen, gerçekten bozuldu ya da neredeyse bozuldu bir kod tabanından geldi.

<p>Bir önceki versiyonuma, başka bir gönderi kontrol cihazı yazmadan önce söylemek istediğim yedi şey var.</p>

<p><strong>1. Üçüncü taraf API'lerine ham diziler göndermeyi bırakın.</strong><br/>Görev "Shippo gönderi oluşturma işlemini bağla" diyor. En hızlı çözüm, siparişten birkaç alan almak, bunları bir diziye koymak ve HTTP istemcisine göndermek. İlk denemede çalışıyor. Demo da çalışıyor. O zaman bunda yanlış olan ne?</p>

<p>Hiçbir şey — ta ki altı ay sonra, Shippo belgelerine hiç göz atmamış yeni bir geliştirici metodunuzu çağırıp, parçaların distance_unit anahtarını unutana kadar, stajda 422 hatasıyla karşılaşana kadar. Dizi yaklaşımı, hatanın meydana geldiği yerde başarısız olmaz. Hata, ağ sınırında, HTTP istemcinize işaret eden bir yığın izleme ile başarısız olur.</p>

<p>İlk instinctim "Shippo bu şekli sahip olduğu için, bir DTO sadece onların API'sini yeniden tanımlamak." Bu ters. DTO, Shippo'yu kısıtlamak için değil — beni kısıtlamak için var. Shippo'ya bozuk bir şey göndermeyi yapı olarak imkansız hale getirir ve sonraki kişiye "bir gönderi isteği tam olarak neye ihtiyaç duyar?" sorusunun yanıtını veren tek bir dosya sunar.</p>

<pre><code>final readonly class ShippoParcel

{
public function __construct(
public float $lengthCm,
public float $widthCm,
public float $heightCm,
public float $weightKg,
) {}
public function toPayload(): array
{
return [
‘length’ => (string) $this->lengthCm,
‘width’ => (string) $this->widthCm,
‘height’ => (string) $this->heightCm,
‘distance_unit’ => ‘cm’,
‘weight’ => (string) $this->weightKg,
‘mass_unit’ => ‘kg’,
];
}
}

<p>toPayload() belirli bir işlev görüyor: Shippo'nun distance_unit ve mass_unit'u ayrı string anahtarlar olarak istediklerini bilen tek yer. Bu bir Shippo özelliği, bir alan kavramı değil ve sadece bir kez — kenarda — yaşaması gerekiyor; her yerde bir parsel dizisi oluşturmak için kopyalanmaktansa.</p>

<p>Yanıt tarafında da aynı şeyi yapın. Başarılı bir işlem, üç seviye derinlikte gömülü bir tracking_number içeriyorsa, her tüketicinin bunu ezberlemesine gerek yok. Bunu bir yanıt DTO'suna sarın ve bir alanın eksik olması durumunda orada bir alan dışı istisna fırlatın; aksi halde bir şey null-dereference olduğunda üç çağrıda keşfetmek zorunda kalırsınız.</p>

<p><strong>2. Bağlamsal bağlama: sınıflarınızın içine ortam kontrolleri koymayı bırakın.</strong></p>

<p>Çoğu zaman, tek bir global konteyner bağı bir şekilde tamdır. Gerekiyorsa bağlamsal bağlamaya ulaşmayın — bu kendi başına aşırı mühendislik. Ancak, bunun büyüdüğü konusunda bir belirti var: yapılandırma kontrolü yapan ya da kendi davranışını belirlemek için instanceof kontrolü yapan bir hizmet sınıfı. Bu sınıf, hangi uygulamanın kullanılacağını belirleme gibi ikinci bir iş almış olur ve bu görev konteynere aittir.</p>

<pre><code>$this->app->when(QaShipmentController::class)
->needs(ShippoClient::class)
->give(SandboxShippoHttpClient::class);

$this->app->when(SupportLabelController::class)
->needs(ShippoClient::class)
->give(ShippoHttpClient::class);

<p>Her iki kontrol cihazı da ShippoClient'ı yalnızca tip işaretler. Hiçbiri bir if dalı yok. Hiçbiri yanlış kimlik bilgilerine kaymamış, çünkü bir ortam kontrolünü unuttunuz.</p>

<p>Bir şeyi işaretlemek gerekir: bir bölme formunu kullanarak verdiğinizde, bir bölgeye özgü depolama sürücüsü çözümlemek için, eşleşmeyen durumun sessizce varsayılan bölgeye geri düşmesini sağlamak yerine bir hata atın.</p>

<pre><code>$this->app->when(LabelArchiver::class)
->needs(StorageDriverInterface::class)
->give(function ($app) {
    return match (config('services.region')) {
        'eu' => $app->make(S3EuStorageDriver::class),
        'us' => $app->make(S3UsStorageDriver::class),
        default => throw new RuntimeException('Unconfigured region: no storage driver bound.'),
    };
});</code></pre>

<p>Yanlış bir yapılandırmanın başlamada yüksek sesle başarısız olması, beş dakikalık bir düzeltmedir. Aynı yanlış yapılandırma, üç hafta boyunca AB müşteri verilerini bir ABD kovasına sessizce arşivlemek, hukuki olarak çok farklı bir konuşma gerektirir.</p>

<p><strong>3. API sürümleme: veritabanının bir görüşü yoktur</strong></p>

<p>Bir mobil uca değişiklik yapmanız gerektiğinde, cazip kısayol: sadece değiştirin, yeni bir uygulama sürümü gönderin, geç kalanlara güncellemeleri söyleyin. Bu, yalnızca tek bir alan farkı için paralel yollar ve kaynaklar kurmaktan daha az iş. Fakat bu, mobil istemciler ile yerde buluştuğunda geçerli değildir — güncellemeyi zorlayamazsınız. Bir web dağıtımı, dakikalar içinde herkese ulaşır. On dört aylık eski uygulamanızla dolabında bekleyen bir telefon, yarın API'nize bugünkü yapılarla aynı güvenle erişir. O istemciyi onaramazsınız — sadece önceden, onunla konuşmaya devam etmekten ne kadar istekli olduğunuzu karar vermeniz gerekir.</p>

<p>Artık kendime koyduğum kural: sınırda sürümleyin, asla iş mantığı içinde. Bir sürüm kontrolü, bir servis sınıfı içinde göründüğü anda, yazıldığı sürümü aşan bir süre boyunca yaşamak zorunda kalacak.</p>

<pre><code>final class ShipmentController extends V1\ShipmentController

{
protected function resource(Shipment $shipment): JsonResource
{
return ShipmentResource::make($shipment);
}
}

<p>eğer v2 sadece yanıt biçimini değiştiriyorsa, tüm kontrol cihazını dallandırmak yerine kaynağı dallandırın ve v2 geri kalanını miras almasına izin verin. Tüm kontrol cihazını kopyalamak, o esnada daha güvenli görünse de, şimdi her hata düzeltmesinin iki kez uygulanması gerekiyor, çünkü iki dosyanın bulunduğunu hatırlaması gereken birine bu yapılacaktır.</p>

<p>Ve istemcilere bir sürümün sona erdiğini makine okunabilir bir şekilde bildirin, istemci tarafında okunmayan bir değişiklik günlüğü yerine:</p>

<pre><code>$response->headers->set('Deprecation', 'true');

$response->headers->set(‘Sunset’, ‘Wed, 01 Oct 2026 00:00:00 GMT’);

<p>Sonra, gerçek bir hesaplama yapmak için eski sürüm üzerindeki istekleri sayın. Bu sayım, "bunu kapatabilir miyiz" sorusunun tek dürüst yanıtıdır — destek taleplerinden bir tahmin değil.</p>

<p>Gerçekten takımları yakan kısım yönlendirme katmanı değil; göçlerdir. Yeni sütunları nullable olarak ekleyin, geçiş sırasında hem eski hem yeni sütunlara yazın, eski sürümün üzerindeki trafiğin gerçekten sıfıra düştüğünü onaylayın, ve ancak sonra eski sütunu ayrı bir dağıtımda kaldırın. "Düşük" trafik sıfır trafik değildir — bir kurumsal müşterinin depo tarayıcısının her gece v1'in üzerine yazması, bir rutin temizliği bir çağrı durumu olayı haline getirmek için yeterlidir.</p>

<p><strong>4. Gerçek zamanlı panolar: yayınladığınız demoların güzelliği ve ardından yalan söylemesi</strong></p>

<p>WebSockets'ı kullanmadan önce — yalnızca polling yapın. setInterval, bir fetch çağrısı, tamam. Birçok iç dashboard için bu gerçekten doğru cevap, daha azı değil. Broadcast, güncelleme hacmi ve izleyici sayısı arttığında karmaşaya neden olur.</p>

<p>“bir trait ekle, bir modeli kaydet, canlı güncellemeleri izle” demomuzun gösteremediği şey: sessizce hiç ulaşmayan yayınlar, sıranın dışındaki olaylar ve dizüstü bilgisayarları iki dakika uykuya dalan istemcilerin kaçırdığı tüm güncelleme pencereleri.</p>

<p>Bir gönderi takip panosu, tüm bu üç durumu karşılar çünkü "etiket satın alındı" ve "taşınmada" webhook'ları milisaniyeler arayla düşebilir ve normal ağ jitter, bu durumu tarayıcıya ulaşmadan önce atlayabilir. Çözüm, istemcinin karşılaştırabileceği bir sürüm alanıdır:</p>

<pre><code>public function broadcastWith(string $event): array

{
return match ($event) {
‘updated’ => [
‘id’ => $this->id,
‘status’ => $this->status,
‘version’ => $this->updated_at->getTimestampMs(),
],
default => [],
};
}
jsEcho.private(shipments.${shipmentId}).listen(‘.ShipmentUpdated’, (e) => {
if (e.version > lastSeenVersion[e.id]) {
lastSeenVersion[e.id] = e.version;
updateDashboard(e.status, e.trackingNumber);
}
});

<p>Herkesin atladığı tek şey: yeniden bağlantılar. Kesik bir soket, kaçırdığı şeylerin hiçbiri hakkında bellek yok — tekrar bağlanınca, mevcut durumu basit bir REST uç noktasından yeniden almak gerekir. Bunu atlarsanız, sonunda "panonun durumu beklemede, ama gerçekte saatler önce gönderildi" biçiminde bir hata raporu alırsınız ve bunun bir yayın hatası değil, bir kayıp REST çağrısı olduğunu anlamak bir dakika sürecektir.</p>

<p><strong>5. N+1 sorguları belirleyiciler ardında gizlenir, belirgin döngülerde değil</strong></p>

<p>Kimse belirgin bir N+1 döngüsü yazmaz ve gözden kaçırmaz. Gönderilerin yalnızca belirli bir alt kümesi için ateşlenen bir dal içinde iki ya da üç seviye derinlikte gizli olanlar gönderilir:</p>

<pre><code>$shipments = Shipment::with('customer')->get();

foreach ($shipments as $shipment) {
if ($shipment->customer->isHighVolume()) {
// N+1 buradan açılır – parseller hiç önceden yüklenmedi
$shipment->parcels->each(fn ($p) => $p->carrier->name);
}
}

<p>with('customer') yalnızca bir seviye derinliğe ulaşır. Gözden geçirmede görünmez, çünkü birisi her dalı iki ilişkiye kadar zihinsel olarak izlemese, ve yerel geliştirme ortamında, on tane tohumlanmış gönderinize on tane sorgu çalışır, on bin değil. Bu durum, üretimde bağlantı havuzu tüketimi sırasında gösteriliyor, en yoğun saatiniz — bir PR’da yorum olarak değil.</p>

<p>Gerçek düzeltme "önceden yükleme yapmayı hatırlamak" değil — bellek bir mühendislik kontrolü değildir. Çerçeve, hatayı gönderime izin vermemelidir:</p>

<pre><code>Model::preventLazyLoading(!$this->app->isProduction());

Model::handleLazyLoadingViolationUsing(function (Model $model, string $relation): void {
logger()->warning(“Model [“. get_class($model) .”] üzerinde [{$relation}] tembel yükleme girişimi.”);
});

<p>Yerel ve staj ortamlarında atılan bir hata, bir hata olarak durumu gereksiz kılacaktır. Üretimde, tembel bir yüklemeyi atmak, bir performans hatasını bir arıza haline getirecektir. Ve bunu test paketinizde de etkinleştirin — bir gönderiyi bir parsel ile test eden bir test hiç bir N+1 başlatmayacaktır, çünkü çarparak çarpan bir şey yoktur.</p>

<p>Düzeltilmiş seçenek için, bir yere önceden yükleme yapmak yerine loadMissing()’e ulaşın; eğer arayan zaten doğru şeyi yaptıysa bunun maliyeti yoktur ve Controllers/job sınırında yapmadılarsa bir güvenlik ağı işlevi görür.</p>

<p><strong>6. Cache::flush() bir çekişiyle mızraklı bir çekiçtir</strong></p>

<p>Bir taşıyıcının müzakere oranları değişiyor, bir şey geçersiz kılma gerektiriyor ve Cache::flush() hemen orada — bir satır, garantili doğru, yanlış yapması imkansız. Ayrıca, uygulamanızdaki her alakasız önbelleklenmiş değeri siler ve her eş zamanlı istek bir anda veritabanına çarpışarak gitmesine neden olur.</p>

<pre><code>Cache::tags(['rates', "carrier:{$carrierId}"])->put(
"rate.{$originZip}.{$destZip}", $rateQuote, now()->addHours(6)

);

Cache::tags([“carrier:{$carrierId}”])->flush();

<p>Tamamlanmış bir inşa ile doğru olması için flush'u model yaşam döngüsüne bağlayın, böylece her geliştiricinin yazma her zaman geçersiz kılma satırını eklemesini hatırlamasındansa, oluşturma yoluyla doğru olur:</p>

<pre><code>protected static function booted(): void

{
static::saved(function (CarrierAccount $account): void {
if ($account->wasChanged(‘negotiated_rate_table’)) {
Cache::tags([“carrier:{$account->id}”])->flush();
}
});
}

<p>Bu yapılara dair bilmeniz gereken iki şey: önbellek etiketleri Redis veya Memcached gerektirir — dosya ve veritabanı sürücüsü çalışma zamanında hata verir, zarif bir şekilde başarısız olmaz. Ve işleminiz tamamlandığında iyileştirin, önceden değil. Çok erken temizlerseniz, o pencereye düşen bir istek güncelleyerek mevcut satırı okur, bunu önbelleğe alır ve şimdi TTL'nin tamamı için bozuk veriler sunuyorsunuz.</p>

<p>Laravel 13, ayrıca Cache::touch() ekliyor, kesin kesin durumlar için de bilmekte fayda var: bir anahtar hala geçerli, ve tek istediğiniz yalnızca TTL'yi uzatmak.</p>

<p><strong>7. Model::all() tablonuz büyüdüğünde iyi çalışır</strong></p>

<p>Bu basit bir çağrı, düzgün okuyabilir ve her eğitimde doğru cevaptır. Birkaç yüz satırda, gerçekten doğrudur. Açık gönderilerle dolu bir tabloda, bu bir OOM hatasıdır; döngünüz çalışmadan önce bir tek iterasyonda — ve başarısızlık sorgu seviyesinde olur.</p>

<p>Seçim iki soruya dayanıyor: döngünüz filtrelediğiniz sütunu değiştiriyor mu, ve her iterasyonda yavaş I/O mu yapıyor?</p>

<ul>
    <li>Okuma-yalnız, hızlı → chunk()</li>
    <li>Filtrelenmiş sütunu değiştirir → chunkById() — birincil anahtar üzerinden sayfalandığı için, bir satır durum değiştirdiğinde, kesilerek neyin sonraki sayfayı alacağını değiştirmez (bu en çok insanları dikenli hale getirir: değişim altında chunk() hata vermez, yalnızca satırları sessizce atlar)</li>
    <li>Yavaş çıkış I/O → lazyById() — cursor() ile aynı üreteç ergonomisi, ancak her dış API çağrısı için bir veritabanı bağlantısını açık tutmaz</li>
</ul>

<pre><code>Shipment::where('status', 'in_transit')
->lazyById(500)
->each(function (Shipment $shipment): void {
    $shipment->refreshTrackingStatus();
    $shipment->save();
});</code></pre>

<p>İkinci soruyu yanlış alırsanız, bir OOM'yi, tablonuzu gizlice alt işlemden çıkararak değiştirirsiniz — bunu düşünmekten daha kötü, çünkü bunun olduğunu bildirmek için hiçbir şey yoktur.</p>

<p>Bu örneklerin hiçbiri egzotik değil. Hepsi Laravel'in sunduğu kutudan çıkan özellikler. Tüm yedide bir tema varsa, o da şu: yerel geliştirme ve küçük tohum veri seti, bu hataların hepsini sizden gizleyecek ve üretim, her birini en kötü zamanda ortaya çıkaracaktır. Koruma inşa etmek — bir DTO, bir konteyner bağlama, tembel yükleme istisnası, bir sürüm başlığı — hatayı yapmamayı hatırlamaktan daha iyidir.</p>

<p>Bu kurallardan herhangi birini farklı bir versiyonda deneyimlediyseniz, duymak isterim — lütfen yorumda paylaşın.</p>

Kaynak: Orijinal Makale

Ham JSON Kayıtlarını Durdurmak: Filament PHP için Görsel Denetim İzini Nasıl Oluşturdum
Laravel admin araçlarında sürükle-bırak sıralama, göründüğünden daha hızlı karmaşık hale geliyor.
Çoğu Tutku Projesi Kötü Olduğu İçin Başarısız Olmaz, Başlangıç Nedenimizi Yavaşça Unuttuğumuz İçin Başarısız Olur.
RAM Kullanımını Azaltma: Laravel’de Okuma-Only Sorgularını Ustalaşma
Laravel 12 + React 18 + TypeScript ile Üretim Seviyesinde Çok Kiracılı SaaS Başlangıç Kiti Nasıl Oluşturdum?
Bu Makaleyi Paylaş
Facebook Bağlantıyı Kopyala Yazdır
Paylaş
Önceki Makale Spotify, AI Destekli Yeni Müzik Asistanıyla Kullanıcı Deneyimini Geliştiriyor
Sonraki Makale Kritik SAP Yamanması: CVSS 9.9 ile Veri Açıklarını Kapatın!

Sanal Medya

FacebookBeğen
452Takip Et
PinterestSabitle
237Takip Et

Son Eklenenler

DOGE’nin AI Kullanımı: Konut Politikalarında Gizlilik Sorunları
Genel
Superhuman’ın yeni otomatik taslak özelliği AI yanıtlarını sevdirebilir mi?
Genel
Hinge kurucusu yeni AI flört servisi Overtone için 18M $ topladı
Yapay Zeka
İspanyol Polisi Açıkladı: 140 Milyon Euro’luk Siber Dolandırıcılık Çöktü!
Siber Güvenlik
Kritik: Chrome’daki Claude Hatası, İzni Olmadan Gmail Okutuyor!
Siber Güvenlik
Meta Tarafından Hedeflenen Yanlı AI Kullanımı İddia Ediliyor
Liste
//

Siber güvenlik, yapay zeka ve savunma sanayiinden; finans ve sinema dünyasına uzanan geniş bir yelpaze. Teknomers; teknoloji, strateji ve yazılım dünyasını sade bir dille sizlerle buluşturuyor.

Kurumsal

  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti

Kategoriler

  • Teknoloji
  • Oyun
  • Sinema
  • Siber Güvenlik
  • Bilim
  • Finans
  • Dünyadan Güncel Haberler

Populer

  • TV'de Ücretsiz İzlenebilen Şifresiz Erotik Kanallar (2025 Güncel Frekans Listesi)

  • The Last of Us PC Kontrolleri: Hızlı Silah Değiştirme ve Tüm Tuşlar (2025)

  • Hogwarts Legacy'de Odaklanma İksiri Nasıl Yapılır?

Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Bizi Takip Et
© 2026 Teknomers. All Rights Reserved.
Welcome Back!

Sign in to your account

Kullanıcı Adı veya E-posta Adresi
Şifre

Şifrenizi mi unuttunuz?