Bir web uygulaması yavaşladığında ilk tepki çoğu zaman aynıdır: sorumlu bileşen aranır, profiler açılır ve tespit edilen şey optimize edilir. Yaklaşımın kendisinde sorun yok; sorun, yapılan iyileştirmenin ardından bir sınırın konmamasında. Birkaç sprint geçtikten sonra aynı uygulama yeniden ağırlaşır, çünkü kimse "bu bundle 400 KB'ı geçemez" kuralını kod tabanına yazmamıştır.

Performance budget, performans hedeflerini somut sayılara bağlayan bir sözleşmedir. LCP için üst sınır, TBT için tolerans, ana bundle için kilobayt eşiği - bunlar proje başında belirlenir ve CI pipeline'ına gömülür. Sınır aşıldığında build durur; tartışma sonradan değil, aşma anında gerçekleşir.

Takım içinde uygulamak, bireysel optimizasyon yapmaktan farklı bir iş gerektirir. Teknik taraf görece basittir: araçlar mevcuttur, entegrasyon adımları belgelidir. Asıl çalışma, sayıların nereden geleceğini ve kimin sahipleneceğini netleştirmekte yatar.

Performance Budget Neden Soyut Kalır?

Çoğu ekip performans bütçesinden haberdar olur, niyetle başlar, sonra sessizce terk eder. Bunun birkaç somut nedeni vardır. İlki, bütçenin kayda geçirilmemesi: "Lighthouse skoru 90 altına düşmesin" gibi bir kural kod tabanında yazmıyorsa yeni gelen geliştirici onu bilemez. Geliştirici bilse bile otomatik zorunluluk yoksa kural önerilmiş bir istek olarak kalır; bir PR'da 50 KB fazladan vendor kodu geçerken kimse fark etmez.

Bir de ölçüm ortamı sorunu var. Kendi makinesinde Lighthouse çalıştırmak üretim koşullarını yansıtmaz. Throttling ayarları tutarsız, cihaz farklı, network simülasyonu değişkendir. Kişisel ölçüm alışkanlığına dayanan bütçe bu yüzden zayıftır. CI ortamında sabit koşullarla tekrar eden ölçüm, bütçeyi güvenilir kılan unsurdur.

Soyutlukla mücadelenin yolu sayıyı netleştirmek ve onu otomatik kontrolün parçası haline getirmektir. Hangi metriği bütçeye almak gerektiği bu yolun ilk adımıdır.

Hangi Metrikler Bütçeye Alınmalı?

Her metriği bütçelemek sürdürülemez. Seçim yaparken kullanıcı deneyimine en doğrudan etkiyi yapan ve ölçümü güvenilir olan metrikleri öne almak gerekir.

Bundle boyutu, JavaScript ve CSS için ayrı ayrı, en kolay izlenen bütçe türüdür. Build çıktısında dosya boyutları sabit ve deterministiktir; gzip veya brotli sonrası transfer boyutunu ayrıca ölçebilirsiniz. Ana bundle, vendor chunk ve sayfa başına düşen lazy chunk'ları ayrı eşiklerle tanımlamak, hangi parçanın şiştiğini anında gösterir.

LCP (Largest Contentful Paint), ilk ekranın ne kadar hızlı hazır olduğunu ölçer. Yavaş bir font yüklemesi, geç gelen bir hero görseli veya uzun bir sunucu yanıt süresi LCP'yi doğrudan etkiler. 2,5 saniye kullanıcı deneyimi açısından iyi kabul edilen eşiktir; projenin hedef kitlesine ve cihaz profiline göre bu sayı aşağı çekilebilir.

TBT (Total Blocking Time), uzun JavaScript görevlerinin ana iş parçacığını bloke ettiği toplam süreyi ölçer. 200 milisaniyenin altı genellikle kabul edilebilirdir; yoğun JavaScript gerektiren uygulamalarda 300 milisaniye gerçekçi bir başlangıç noktası olabilir. Eşiğin belirlenmesi projeye özeldir, standart bir sayı yoktur.

Daha az ölçülen ama değerli olan iki ek bütçe türü vardır: HTTP request sayısı ve third-party script boyutu. Her ikisi de kontrolsüz büyüdüğünde LCP ve TBT'yi dolaylı olarak etkiler. Third-party script bütçesi özellikle pazarlama ekiplerinin sık tag eklediği projelerde önem kazanır.

Eşik Değerleri Nasıl Belirlenir?

Sayıları havadan belirlemek bütçeyi kırılgan yapar. Çok sıkı eşikler her PR'ı bloke eder; çok gevşek eşikler anlamsız olur. İkisi arasındaki denge projeye özgüdür.

Başlangıç için mevcut ölçüm esas alınır. Projenin güncel LCP değeri 3,1 saniyeyse ilk eşik bu sayıyı kötüleştirmeyecek biçimde, örneğin 3,2 saniye olarak belirlenir. Ardından her sprint döngüsünde iyileştirme yapıldıkça eşik sıkılaştırılır. "Bugünkü değerden daha kötü olmasın" kuralı, sıfırdan ideal bir hedef belirlemeye çalışmaktan çok daha sürdürülebilir bir giriş noktasıdır.

Yeni projeler için sektör referansları kullanılabilir. Google'ın Core Web Vitals eşikleri - LCP için 2,5 s, INP için 200 ms, CLS için 0,1 - yaygın kabul görmüş başlangıç noktalarıdır. Bundle boyutu için standartlaşmış bir referans yoktur; gzip sonrası 170-200 KB'ın altında bir ana bundle, düşük bant genişliğinde kabul edilebilir ilk yükleme sağlar.

Eşiği belirlerken hedef cihazı ve ağ koşulunu da netleştirin. Masaüstü odaklı bir kurumsal uygulama ile düşük uçlu Android cihazlarda çalışması beklenen bir e-ticaret sitesi farklı eşikler gerektirir. Lighthouse'un "Simulated Slow 4G, Moto G4" profili, düşük uçlu hedef kitleyi baz alan projeler için tutarlı bir CI ölçüm ortamı oluşturur.

Bundle Boyutunu CI'da Sınırlamak

Bundle boyutu bütçesi, diğer metriklerden farklı olarak build anında kontrol edilebilir. Ağ isteğine gerek yoktur; bu da onu CI entegrasyonu için en hızlı geri bildirim sağlayan bütçe türü yapar.

bundlesize aracı, webpack veya herhangi bir build sisteminin çıktısını alır ve her dosya için tanımlanan eşiklerle karşılaştırır. Yapılandırma package.json içine yazılır:

"bundlesize": [
  { "path": "./dist/main.*.js", "maxSize": "150 kB" },
  { "path": "./dist/vendor.*.js", "maxSize": "200 kB" },
  { "path": "./dist/*.css", "maxSize": "30 kB" }
]

npx bundlesize komutu eşik aşıldığında sıfırdan farklı exit kodu döner; bu da CI'ın adımı başarısız saymasını sağlar. Boyutlar varsayılan olarak gzip sonrası karşılaştırılır, gerçek transfer boyutu baz alınır.

webpack-bundle-analyzer bundle içeriğini görselleştirir ve hangi paketin ne kadar yer kapladığını gösterir. Sınır aşıldığında nedenini bulmak için kullanılır. CI'a entegre edilmez; geliştirme sürecinde inceleme aracı olarak çalışır. İki araç birbirini tamamlar: biri kapıyı kapatır, diğeri sebebi gösterir.

Vite kullanan projeler için rollup-plugin-visualizer benzer görselleştirmeyi sağlar. Sınır denetiminde ise bundlesize, Vite'ın dist çıktısı üzerinde doğrudan çalışabilir.

LCP ve TBT için Lighthouse CI Entegrasyonu

Bundle boyutu kontrolü build anında çalışır; LCP ve TBT ölçümü ise bir sayfanın gerçekten render edilmesini gerektirir. Lighthouse CI bu boşluğu kapatır.

GitHub Actions ile temel entegrasyon şöyle kurulur:

name: Performance Budget
on: [pull_request]

jobs:
  budget:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm run build
      - name: Bundle size check
        run: npx bundlesize
      - name: Lighthouse CI
        run: |
          npm install -g @lhci/cli
          lhci autorun

Eşikler lighthouserc.js dosyasına yazılır:

module.exports = {
  ci: {
    collect: { staticDistDir: './dist' },
    assert: {
      assertions: {
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'total-blocking-time': ['error', { maxNumericValue: 300 }],
        'interactive': ['warn', { maxNumericValue: 5000 }]
      }
    }
  }
};

warn seviyesi başarısız saymaz, yalnızca bildirir. error seviyesi build'i durdurur. Yeni bir projeye bütçe uygulamaya başlarken tüm kuralları warn ile açmak takımı kademeli alıştırır; kararlı olan metrikler birkaç sprint içinde error'a yükseltilir.

PR içinde Lighthouse skoru değişimini yorumla görmek için Lighthouse CI'ın GitHub Actions entegrasyonu kullanılabilir. Sayı soyut bir "build kırmızı" bildiriminden "LCP 2,3 s'den 2,8 s'ye çıktı" gibi somut bir bilgiye dönüşür; bu fark PR tartışmasını doğrudan nedenin üzerine taşır.

Takım İçinde Sahiplenme

Araçlar kurulduktan sonra gerçek çalışma başlar: takımın bu sayıları sahiplenmesi. Bütçeyi yalnızca bir geliştirici takip ederse o kişi tatildeyken kural gevşer. Sayıların koda gömülü olması bu riski azaltır, ancak ortadan kaldırmaz.

Eşiklerin nerede yaşadığı önemlidir. package.json içindeki bundlesize yapılandırması veya lighthouserc.js dosyası, değiştirildiğinde PR geçmişinde görünür. Bütçe güncellemeleri koda karışmaz; gerekçe commit mesajına veya PR açıklamasına yazılır. Hangi kararın ne için alındığı izlenebilir kalır.

Sprint demosu veya haftalık teknik toplantıda bundle boyutu ve LCP skorlarına kısa bir yer ayırmak, sayıları görünür kılar. Kimin önce fark ettiği değil, hangi PR'ın nasıl bir etkisi olduğu konuşulur. Bu alışkanlık birkaç ay içinde yerleşir; yeni katılan geliştiriciler metriklere doğal olarak dikkat etmeye başlar.

Bütçeyi ihlal etmek utandırıcı değil, öğretici olmalıdır. Bir PR vendor bundle'ı 40 KB artırdıysa, nedenini anlamak ve alternatifi tartışmak konuşmanın içeriği olur. Sıkı bütçe haklı gerekçeyle gevşetilebilir; önemli olan bu kararın kayıt altında olmasıdır.

Performance budget, tek seferlik bir kurulum değildir. Proje büyüdükçe yeni sayfa tipleri eklenir, third-party entegrasyonlar çoğalır, bundle chunk yapısı değişir. Bu değişiklikler olduğunda bütçenin gözden geçirilmesi, araçların kendisi kadar önemlidir.

Bütçenin gerçek değeri, değişimi engellemekte değil, değişimi görünür kılmaktadır. Bir özellik 30 KB getiriyorsa ve ekip bunu bilerek kabul ediyorsa karar bilinçli alınmıştır; aynı 30 KB fark edilmeden sürünüyorsa tartışma hiç gerçekleşmemiştir. Bütçe bu iki durumu birbirinden ayırır. Kural eski projeden kalan bir kalıntıya dönüştüğünde içi boşalır; güncel ve tartışmalı tutulduğunda takım için ortak bir dil olmaya devam eder.