Geliştirme22 Ağustos 20248 dakikalık okuma#nextjs#architecture#performance

Next.js ile Dayanıklı Sistemler İnşa Etmek

Modern web uygulamalarında yüksek kullanılabilirlik ve performans için en iyi uygulamalar.

TL;DR5
  1. Araçlardan önce mimari: dayanıklılık bir tasarım özelliğidir
  2. stale-while-revalidate'li ISR, backend kesintilerinde sayfaları ayakta tutar
  3. Retry'lar backoff ve jitter gerektirir — circuit breaker kademeli hataları keser
  4. Error boundary'ler hatayı sayfaya değil, bozulan widget'a izole eder
  5. Fallback UI ve offline toleransı yama değil, birinci sınıf özelliklerdir
Next.js ile Dayanıklı Sistemler İnşa Etmek

Önce Mimari

Next.js çoğu zaman modern web uygulamalarının omurgası olarak hizmet eder. Dayanıklılığı sağlamak iyi mimari kararlar ile doğru araçların karışımını gerektirir.

Server Components’leri ve doğru error boundary’leri kullanarak, tamamen çökmek yerine zarif bir şekilde degrade olan son derece dayanıklı web uygulamaları inşa edebilirsiniz.

Static Generation vs SSR

Mümkün olduğunda Incremental Static Regeneration (ISR) varsayılan olsun. Bu, backend API’leriniz geçici olarak kullanılamaz olsa bile kullanıcıların en son başarıyla build edilmiş sürümü görmesini garanti eder.

Ancak tradeoff gerçek: ISR tazelik pahasına dayanıklılık verir. SSR tazelik verir ama backend’e sert bir bağımlılık yaratır.

// app/blog/[slug]/page.tsx
export const revalidate = 300; // 5 dakika

export default async function Post({ params }) {
  const post = await fetchPost(params.slug); // cache'lenmiş + revalidate edilmiş
  return <Article post={post} />;
}

Geçen yıl bir Cloudflare olayında origin API’m 40 dakika çöktüğünde, y7.hk’daki tüm ISR sayfaları eski ama geçerli HTML sunmaya devam etti. Aynı sitenin SSR sayfaları 500 döndürdü. Bu deney bana aylık trafiğin %4’üne mal oldu — ve rendering stratejisi hakkındaki düşüncemi kalıcı olarak değiştirdi.

Pratik kural: 5–60 dakika eskimiş olabilecek her şey için ISR (blog yazıları, ürün sayfaları, pazarlama). Eskimişlik ürünü bozduğunda SSR (canlı dashboard’lar, auth durumu, gerçek zamanlı veri).

Error Boundary’ler İzolasyon Bölmeleridir

Boundary olmadan, tek bir başarısız widget tüm sayfayı düşürür. Boundary ile her bölüm bağımsız olarak başarısız olur.

// app/components/Section.tsx
'use client';
import { Component, ReactNode } from 'react';

export class Section extends Component<
  { children: ReactNode; fallback?: ReactNode },
  { hasError: boolean }
> {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback ?? <div className="opacity-50">Bölüm kullanılamıyor</div>;
    }
    return this.props.children;
  }
}

Her bağımsız birimi — sidebar widget’ları, yorum bölümleri, analitik dashboard’ları — kendi boundary’si içine sarın. Kullanıcı boş ekran yerine widget’ın olduğu yerde gri bir kutu görür.

Bunu bir Web3 dashboard’unda, tek bir RPC timeout’unun tüm portföy görünümünü çökertmesiyle acı şekilde öğrendim. Price ticker’ın etrafındaki tek bir boundary oturumu kurtarırdı.

Retry’lar Backoff Gerektirir

Kör retry’lar hatayı büyütür. Jitter’lı üstel backoff yükü dağıtır ve upstream’e toparlanma alanı verir.

async function fetchWithRetry<T>(
  fn: () => Promise<T>,
  attempts = 3,
  baseDelay = 300
): Promise<T> {
  for (let i = 0; i < attempts; i++) {
    try {
      return await fn();
    } catch (err) {
      if (i === attempts - 1) throw err;
      const jitter = Math.random() * baseDelay;
      await new Promise(r => setTimeout(r, baseDelay * 2 ** i + jitter));
    }
  }
  throw new Error('unreachable');
}

İki kurala uyarım:

  1. Asla 4xx retry etme. 404, retry ile 200 olmaz.
  2. Toplam beklemeyi sınırla. Kullanıcı 8+ saniyedir spinner’a bakıyorsa, fallback UI göster ve devam etmesine izin ver.

Circuit Breaker’lar Kademeli Hataları Keser

Retry’lar geçici hataları çözer. Circuit breaker’lar kalıcı hataları çözer. Backend 30 saniyedir başarısızsa, onu dövmeyi bırak — circuit’i aç ve hemen degrade yanıtlar sun.

class Breaker {
  private failures = 0;
  private openUntil = 0;
  constructor(private threshold = 5, private cooldown = 30_000) {}

  async call<T>(fn: () => Promise<T>): Promise<T> {
    if (Date.now() < this.openUntil) throw new Error('Circuit open');
    try {
      const result = await fn();
      this.failures = 0;
      return result;
    } catch (e) {
      if (++this.failures >= this.threshold) {
        this.openUntil = Date.now() + this.cooldown;
      }
      throw e;
    }
  }
}

Breaker olmadan, yavaş bir upstream takılmış Node süreçleri filosu haline gelir. Breaker ile hızlı hatalar ve upstream döndüğü anda toparlanan bir sistem elde edersiniz.

stale-while-revalidate ile Edge Caching

Cache-Control başlığı sahip olduğunuz en ucuz dayanıklılık katmanıdır. stale-while-revalidate, edge’in arka planda taze bir sürüm alırken son cache’lenmiş kopyayı sunmasına izin verir.

Cache-Control: public, s-maxage=60, stale-while-revalidate=600

Bu, “API çöktü, site çöktü” ile “API çöktü, bazı kullanıcılar 10 dakikaya kadar eski veri görüyor” arasındaki farktır.

Fallback UI Bir Özelliktir

Fallback’ler bir şey bozulduğunda gösterdiğiniz şey değil — tasarımın parçasıdır. Her async bölümün üç durumu vardır:

  • Yükleniyor: son layout’a uyan skeleton (layout shift yok).
  • Başarılı: gerçek içerik.
  • Hata: kullanıcının hâlâ işini yapabildiği degrade versiyon.

Fiyat widget’ınız başarısız olursa kırmızı hata göstermeyin — son cache’lenmiş fiyatları “12 dk önce güncellendi” rozetiyle gösterin. Yorum sistemi çökerse, salt okunur thread’i gösterin ve input kutusunu gizleyin.

Offline Toleransı

Service Worker’lar sadece PWA’lar için değil. App shell’i ve son 5 ziyaret edilen sayfayı cache’leyen minimal bir SW, otel wifi’ı ve 10.000 metre yükseklikte gerçek offline toleransı sağlar — ki benim için bu browsing’in önemsiz olmayan bir yüzdesi.

// sw.ts — statik için cache-first, sayfalar için network-first
self.addEventListener('fetch', (e) => {
  const url = new URL(e.request.url);
  if (url.pathname.startsWith('/_next/static/')) {
    e.respondWith(caches.match(e.request).then(r => r ?? fetch(e.request)));
  }
});

Yaşadığım Gerçek Hatalar

  • Cloudflare edge KV kesintisi: KV’ye bağlı her sayfa 500 döndürdü. ISR sayfaları hayatta kaldı.
  • Upstream CMS, trafik pikinde beni rate-limit’ledi. Breaker yoktu; Next.js sert IP ban alana kadar CMS’i dövmeye devam etti. Artık her dış çağrı 30 saniyelik cooldown’lu bir breaker’dan geçiyor.
  • WebSocket düştüğünde tek bir <PriceTicker> tüm dashboard’u çökertti. Boundary eklendikten sonra UI’nin geri kalanı canlı kalırken ticker sessizce yeniden bağlandı.

Dayanıklılık Checklist’i

  • 5+ dakika eskimişliğe toleranslı içerik için ISR/SSG
  • Her cache’lenebilir rotada stale-while-revalidate
  • Her bağımsız widget’ın etrafında error boundary
  • Retry’larda üstel backoff + jitter; 4xx’i asla retry etme
  • Her dış servis çağrısında circuit breaker
  • Her async UI için skeleton + fallback + hata durumu
  • App shell’i cache’leyen service worker
  • Kritik olmayan özellikler (analitik, öneriler) için belgelenmiş kill-switch

Özet

Dayanıklılık sona bırakılan bir özellik değildir. Mimarinin kendisidir. Her katman — rendering, caching, fetching, UI — baştan tasarlanmış bir failure mode alır. Bunları erken inşa ederseniz, kesinti olabilecek olaylar kimsenin fark etmediği sessiz degrade’lere dönüşür.

Animasyonlu avatar videosu
Yakushev Hermann
Vizyoner Kurucu · Kıdemli Agentic AI Mimarı · Networking

Blogdan daha fazlası

y7.hk'da Modern Mühendislik Yazısının Anatomisi

y7 okuma motorunun stres testi: callout'lar, diff blokları, KaTeX formülleri, yuvarlak avatar videosu, figure galerisi, spoiler'lar ve font değiştirici.

15 Eki 2026 · 5 dakikalık okuma

Saat Dilimleri Arasında Yaşamak

67 ülke ve dört ana üssün derin çalışma, jet lag ve kimsenin istemediği ürünleri geliştirme hakkında öğrettikleri.

18 Tem 2026 · 6 dakikalık okuma