# Next.js ile Dayanıklı Sistemler İnşa Etmek > Modern web uygulamalarında yüksek kullanılabilirlik ve performans için en iyi uygulamalar. > > 2024-08-22 — y7.hk ## Ö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. ```tsx // 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
; } ``` 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. ```tsx // 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 ??
Bölüm kullanılamıyor
; } 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. ```ts async function fetchWithRetry( fn: () => Promise, attempts = 3, baseDelay = 300 ): Promise { 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. ```ts class Breaker { private failures = 0; private openUntil = 0; constructor(private threshold = 5, private cooldown = 30_000) {} async call(fn: () => Promise): Promise { 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. ```ts // 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 `` 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.