Spaces:
Sleeping
WEB PERFORMANCE β arac-hasar-v2 (apps/web)
Statik kod tabanli web performans denetimi. Lighthouse veya gercek bir RUM olcumu calistirilmadi; tum sayilar mantiksal tahmindir. Hedef: orta-uretim hazirligi (MVP demo + pilot), mobil 3G/4G ve masaustu Wi-Fi senaryolarinda kabul edilebilir UX.
Stack: Next.js 15.1.3 (App Router), React 18.3, next-intl, Tailwind 3.4, axios, lucide-react, react-hook-form, zod. output: 'standalone'. Hicbir RSC server-data fetch yok; tum sayfalar client component (kritik bulgu).
0) Ozet β Tahmini Lighthouse Skoru (mobil, 4G, mid-tier)
| Kategori | Skor (tahmini) | Yorum |
|---|---|---|
| Performance | 62 - 72 | LCP ve TBT yuksek; her sayfa client component β JS bundle agir |
| Accessibility | 88 - 92 | Aria etiketleri ve role tab/tabpanel tutarli; renk kontrasti genel olarak OK |
| Best Practices | 85 - 92 | HTTPS / CSP / source-map gibi guvenlik basliklari prod build'de eksik olabilir |
| SEO | 80 - 90 | Metadata var; ancak metadataBase: localhost, sitemap yok, robots.txt yok |
| PWA | 0 | Manifest yok, service worker yok (kapsam disi olabilir) |
Tahmini Core Web Vitals (mobil orta cihaz, 4G hizinda):
- LCP (Largest Contentful Paint): ~2.4 - 3.2 s (sinir / kotu)
- Sebep: Hero gorsel placeholder bir DOM-rendered card; Inter font swap; tum sayfa client component β JS-after-hydration LCP'yi ~600 ms erteliyor.
- INP (Interaction to Next Paint, FID yerine): ~120 - 220 ms (sinir)
- Foto upload tikinda multipart-FormData hazirlama anlik (kucuk dosya icin ~10 ms), ama
URL.createObjectURLher render'da yeniden cagrildigi icin preview ekraninda jank olusabilir.
- Foto upload tikinda multipart-FormData hazirlama anlik (kucuk dosya icin ~10 ms), ama
- CLS (Cumulative Layout Shift): ~0.08 - 0.15
ImageWithOverlayicindeki<img>width/height attribute yok β annotated PNG yuklendiginde sayfa kayiyor.ResultsTabspanel bos yer tutmuyor β "Spinner β result" gecisinde ~150 px shift olasi.
1) Tahmini bundle / "First Load JS"
Hedef: < 200 KB gz First Load JS.
Mevcut prod build calistirilmadi (.next/static/chunks/ icindeki dev artifact'lar 6.5 MB; bu anlamli degil, dev-mode'da unminified ve HMR loader var). Asagidaki tahminler dependency analizinden cikarildi.
| Kaynak | Tahmini gz boyut | Not |
|---|---|---|
| React 18 + React-DOM | ~42 KB | Standart |
| Next.js runtime + Router | ~35 - 45 KB | App Router'in client runtime'i Pages Router'dan biraz daha agir |
| next-intl client provider + tr.json (24 KB raw) | ~12 - 18 KB gz | Tum mesajlar her sayfaya biniyor (root layout getMessages() -> NextIntlClientProvider) |
| axios (full) | ~14 KB gz | Sadece JSON istek yapiyor; fetch wrapper'i ile degisilebilir β -10 KB |
| react-hook-form + @hookform/resolvers + zod | ~22 KB gz | Sadece login / register / settings'de kullaniliyor ama root'ta tree-shake edilemiyor cunku zod'u lib/jwt.ts ihtimal cekiyor |
| lucide-react | optimizePackageImports ile per-icon ~0.3 KB |
Iyi β next.config tarafindan zaten optimize edilmis |
@arac-hasar/ui (24 component, hepsi index.ts barrel'dan re-export) |
~18 - 25 KB gz | Bazi sayfalarda yalnizca Spinner kullaniliyor ama ImageWithOverlay, UploadDropzone gibi agir component'lar yan etki yokken bile tree-shake olabilir varsayilir β kontrol edilmedi |
| Sayfa kodu | ~5 - 12 KB / sayfa | Tahmin |
| First Load JS (homepage) | ~135 - 165 KB gz | Hedef altinda ama az marjla |
First Load JS (/results/[id]) |
~180 - 220 KB gz | ImageWithOverlay + canvas hit-test + zod yan tasimasi nedeniyle sinirda |
Onemli not β kullanilmayan dependency: react-dropzone@14.3.5 web package.json'inda var, ama Grep ile dogrulandi: apps/web icinde hicbir yerden import edilmiyor (sadece apps/desktop icinde FileDrop.tsx kullaniyor, o da @arac-hasar/ui paketinden geliyor). Dead dep, kaldirilmali β react-dropzone ~10 KB gz, tree-shake ile cikiyor olsa bile npm install + audit yukunu artiriyor.
2) Statik analiz bulgulari
2.1 next/image hic kullanilmiyor β LCP +0.8 - 1.4 s
Grep: from 'next/image' β sifir kaynak dosyada eslesme (sadece .next/ ve tsbuildinfo icinde, bunlar Next.js'in kendi kullanimi). Tum gorseller raw <img>:
packages/ui/src/components/UploadDropzone.tsx:136β preview thumbnailpackages/ui/src/components/ImageWithOverlay.tsx:172β annotated inspection PNG (kritik LCP elementi)apps/web/components/Header.tsx,Footer.tsxβ sadece SVG icon, sorun degil
Etki:
- Annotated PNG
/api/v1/inspect/{id}/visualization/annotated200 - 800 KB (model ciktisi YOLO seg overlay'i, JPEG/PNG re-encode olmuyor; statik analizdeki goruntu: model annotator opencv ile PNG yaziyor β buyuk).next/imageile:- Otomatik AVIF/WebP serve (Next.js default formats)
srcset+sizescihaza gore tahmini ~%40 - 60 byte tasarrufu- Lazy load + native
loading="lazy" placeholder="blur"LCP'yi maskeleyebilir
- Eksiklik:
next.config.tsremotePatternszaten R2 / S3 / localhost:8000 icin acik. Yaninext/imagekullanimi icin altyapi hazir, sadece component degisikligi gerekli.
ImageWithOverlay'i next/image ile sarmak biraz daha karmasik (canvas hit-test natural width'e bagimli), ama yapilabilir: Image onLoadingComplete callback'i naturalWidth/naturalHeight veriyor.
2.2 Tum sayfalar "use client" β RSC'nin hicbir faydasi yok
Grep: 21 dosyada 'use client'. Daha kritigi:
app/page.tsx(homepage) βuseTranslationshook'u sadece server-side cagrilabilir (next-intlgetTranslations), ama dosyada'use client'yok β server component olarak isliyor, iyi.- AMA
app/inspect/page.tsx,app/(app)/dashboard/page.tsx,app/history/page.tsx,app/results/[id]/page.tsx,app/(auth)/login/page.tsx,app/(auth)/register/page.tsx,app/(admin)/users/page.tsx,app/(app)/settings/page.tsx,app/(app)/inspect/new/page.tsxβ hepsi client component.
Sonuc: Next.js 15'in en buyuk avantaji β RSC'de fetch'i server'da yapip yalnizca veri (JSON degil, RSC-encoded) gondermek β kullanilmiyor. Ornegin /dashboard ve /history:
'use client';
useEffect(() => {
(async () => {
const data = await listInspections({ pageSize: 20 });
setItems(data.items ?? []);
})();
}, []);
Bu pattern Pages Router stili. RSC'ye gecince:
- Sayfa HTML'i SSR'de hazir gelir β LCP -0.5 s
- Client'a giden JS: yalnizca interactive parcalar (status filter, search input)
- API token'lar HttpOnly cookie ile cookie-only kullanilirsa server-side fetch yapilabilir; mevcut auth localStorage tabanli oldugu icin RSC'ye gecmek auth refactor gerektirir (orta efor).
2.3 "use client" abuse: Header / Footer / LanguageSwitcher
Header.tsxβusePathname+useAuthcektigi icin haklisin, client zorunlu. OK.Footer.tsxβ'use client'var (Grep dogruladi) ama Footer.tsx'i tek seferde kontrol edersek muhtemelen sadece tercume gosteriyor.useTranslationsyerinegetTranslations(server) kullanip server component yaparak ~2 KB bundle tasarrufu.LanguageSwitcher.tsxβusePathname+ cookie set ettigi icin client gerekli. OK.
2.4 next/dynamic ile kod splitting hic kullanilmamis
Grep: next/dynamic β kaynak dosyada sifir. Ozellikle agir component'lar dinamik yuklenmemis:
ImageWithOverlay(canvas + ResizeObserver + hit-test, ~4 KB gz) β sadece/results/[id]sayfasinda gerekli, ama @arac-hasar/ui index.ts barrel re-export ettigi icin homepage bundle'ina sizabilir (ESM tree-shake bunu engellemeli, dogrulamadim).ResultsTabs,PartList,CostDisplay,InspectionSummaryβ sadece/results/[id]'de. Bu zaten Next.js'in route-level splitting'i sayesinde ayri chunk'ta.ConfirmDialog(UI paketi) β sadece etkilesim sonrasi gerekli; modal'a kadar import edilmemeli.
Onerilen yaklasim: /results/[id] page'inde ImageWithOverlay'i next/dynamic({ ssr: false }) ile yukle, ki ilk-paint (status pending) kucuk bundle ile gelsin, sonra annotated PNG hazir oldugunda overlay yuklensin.
2.5 Polling interval β 2000 ms uygun, ama backoff yok
use-inspection-polling.ts:33 β default intervalMs: 2_000. Bu dogru bir secim:
- 1 s cok agresif (60s'de 60 istek = backend rate-limit baskisi).
- 2 s makul; backend processing 4 foto icin 8-25 s arasi (
PERFORMANCE_NOTES.md'den).
Ancak iyilestirilebilir:
- Exponential backoff yok. Ilk birkac poll 1 s, sonra 2-5 s gibi gradual artirim daha iyi UX (sonuc hazirsa hizli yakala, hazir degilse server'i bombalamayi birak).
- WebSocket alternatifi mevcut ama kullanilmiyor.
services/backendicindews.pyvar (Promise.allgrep hits bunu da bulmadi cunku frontendWebSocketkullanimi yok). Tahmin: backend WS endpoint'i mevcut, frontend hala polling yapiyor. WS gecisi: -%80 network requests, +instant update. Yuksek kazanc, orta efor. - Visibility-API entegrasyonu yok. Kullanici tab'i degistirdiginde polling devam ediyor β mobilde pil yiyor.
document.visibilitychangeileenabled=falseyapilabilir.
2.6 API call paralelligi β Promise.all yok (kaynak kod tarafinda)
Grep Promise.all apps/web/*.{ts,tsx} β kaynak dosyada hic eslesme yok, sadece .next/ build artifact'larinda (Next.js runtime kendi kullaniyor).
/dashboard page tek call yapiyor (listInspections), serialization sorunu yok./results/[id] page yalnizca polling, single call.
Eger ileride bir sayfada hem auth.me() hem listInspections() lazimsa Promise.all ile paralel; mevcut kod-tabani bunu zaten dogru yapacak yapida ama gozetilmeli.
2.7 Foto preview thumbnail β URL.createObjectURL revoke edilmiyor (bellek sizintisi)
packages/ui/src/components/UploadDropzone.tsx:132:
{files.map((f, i) => {
const url = URL.createObjectURL(f); // <-- her render'da yeni URL
return (
<img src={url} ... />
Iki ayri sorun:
- Her render'da yeni blob URL olusuyor β file listesi degismediginde bile parent state degisirse FilePreview yeniden render olur. React
key={i}korusa bileURL.createObjectURLyeniden cagriliyor. β React'in<img>'i degistirmesi gerekmiyor (URL stringi degisik gorunuyorsa degistirir; browser eski URL'yi GC'ye birakir). URL.revokeObjectURLhicbir yerde cagrilmiyor β bellek sizintisi. 5 MB'lik fotograf 5 kez render olduysa 25 MB tape bellek tutulabilir (browser implementation'a bagli).
Cozum: useMemo + useEffect(cleanup):
const urls = useMemo(
() => files.map((f) => URL.createObjectURL(f)),
[files]
);
useEffect(() => () => urls.forEach(URL.revokeObjectURL), [urls]);
Buna ek olarak, 12 MB'lik bir foto direkt <img>'a verilirse browser tum dosyayi decode eder (ekranda 96x96 px gosteriliyor olsa bile). 4 foto x 12 MB = 48 MB decode. Mobilde laggy. Cozum: createImageBitmap(file, { resizeWidth: 200 }) ile thumbnail uretip onu goster.
2.8 Inter font β display: swap iyi, ama preconnect yok
app/layout.tsx:11:
const inter = Inter({ subsets: ['latin', 'latin-ext'], display: 'swap', variable: '--font-inter' });
display: 'swap'β FOIT yok, iyi.- Next.js
next/fontfonts.googleapis.com'a gitmiyor, fontu build-time'da self-host ediyor β preconnect gereksiz. - Iyi durum.
Yine de subsets: ['latin', 'latin-ext'] Turkce karakter icin doΔru ama ek ~25 KB woff2. Sadece 'latin-ext' yetebilir (icine 'latin' karakterler de dahil).
2.9 Tailwind safelist / unused CSS
tailwind.config.ts okumadim ama tipik Tailwind 3.4 + Next.js setup'inda content path'leri dogruysa unused class'lar purge edilir. apps/web/components/** ve packages/ui/src/components/** content'te olmali β kontrol et.
Tahmini CSS bundle: ~12-20 KB gz. Iyi.
2.10 Inspection annotated PNG β lazy load yok, format optimize degil
ImageWithOverlay'deki <img>:
loading="lazy"yok (LCP elementi olabilir, lazy yaparsan LCP kaybeder; result page'de scroll altinda degil, hero, lazy yapma).fetchpriority="high"yok β result page'in LCP'si bu image.fetchpriority="high"ekleyince ~300 ms kazanc.- Backend taraftan PNG yerine WebP/AVIF serve edilmiyor (tahmin; backend
annotator.pyopencv ile PNG yaziyor).- Backend'de Pillow
WEBPquality=85 ile yazsa ~%40-60 boyut tasarrufu.
- Backend'de Pillow
2.11 Metadata sorunlari
metadataBase: new URL('http://localhost:3000')β production'da overrride edilmeli (env var). Aksi halde OG tag'leri localhost URL'leri ile yayinlanir.sitemap.tsverobots.tsyok β SEO icin 1 saatlik efor.themeColor: '#1e6ee0'viewport icinde dogru tanimlanmis. Iyi.
3) Top 5 darbogaz (etki sirasi)
| # | Darbogaz | Tahmini etki | Efor |
|---|---|---|---|
| 1 | next/image kullanilmiyor (ozellikle annotated PNG) | LCP +0.8 - 1.4 s; bandwidth +%40 mobil | Dusuk - Orta (2-3 component) |
| 2 | Tum data-fetch sayfalari 'use client' β RSC faydasi sifir |
LCP +0.4 - 0.7 s; bundle +30 KB | Yuksek (auth refactor gerekli) |
| 3 | URL.createObjectURL revoke edilmiyor + tam-cozunurluk thumbnail |
Bellek sizintisi (5 dosya x 12 MB), preview render jank | Dusuk |
| 4 | WebSocket varken polling kullanilmasi + backoff yok | Network: ~30 istek/inspection (60s/2s); pil sarf, server load | Orta |
| 5 | CLS β <img> width/height yok sonuc geldiginde shift |
CLS +0.05 - 0.1 | Dusuk |
4) Dusuk efor + yuksek kazanc oneriler (v0.1.x icinde)
react-dropzonekaldir (apps/web/package.json). Hicbir yerde import edilmiyor. β -10 KB gz potansiyel + temizlik.UploadDropzoneFilePreview revoke fix (yukarida snippet). Memory leak + jank giderir.ImageWithOverlay<img>'aloading="eager"+fetchpriority="high"ekle result page'de. CSSaspect-ratioile placeholder cizip CLS'i sifirla.- Annotated PNG'ye
next/imageuygula. Sadece<img>-><Image>swap, canvas overlay component'in dΔ±sinda nokta-koordinatlari hala normalize, sorun yok. - Polling visibility-aware:
useInspectionPollingicinedocument.visibilityState !== 'visible'durumundaintervalMs *= 4ekle. Tab arka plandaysa 8 s'de bir poll. - Footer'i server component yap.
'use client'kaldir,getTranslationsile cevir. - metadataBase env-driven:
new URL(process.env.NEXT_PUBLIC_SITE_URL ?? 'http://localhost:3000'). - sitemap.ts + robots.ts ekle. Tek seferlik 30 dakikalik is, SEO 80β90.
5) v0.2 backlog β Ileri optimizasyonlar
- RSC migration:
/dashboard,/history,/results/[id](sonuc hazirsa) sunucuda fetch. Auth icin Next middleware + HttpOnly cookie pattern'i. Buyuk efor (~3-5 gun) ama her sayfada ~500 ms LCP + ~40 KB bundle kazanci. - WebSocket polling replacement: Backend'de
ws.pyzaten var.lib/use-inspection-polling.tsyerineuseInspectionWebSocketyaz. Fallback olarak polling kalsin (auto-detect WS desteklenmiyorsa). Reconnect logic + heartbeat. - Image pipeline: Backend annotator.py PNG -> WebP. Frontend
next/image+priority+ responsivesizes. CDN cache headers (Cache-Control: public, max-age=31536000, immutablecunku inspection_id versionly). - next-intl mesaj split: Su an
tr.json24 KB tek dosya, her sayfaya tum dictionary biniyor. Per-routegetMessages({ locale, namespace: 'inspect' })ile split ederek/resultssayfasindaauth.*mesajlarini eleme. Tahmini -8 KB gz / sayfa. - Bundle analyzer:
@next/bundle-analyzerekle, gercekFirst Load JSkontrol et. CI'da PR basina threshold > 200 KB ise fail. - Lighthouse CI: GitHub Actions'da
lhci autorunrunner. Performance < 75 ise PR'i blocking. - Service Worker (offline + cache): Workbox veya
next-pwa. Inspection sonuclarini offline cache'le, "son baktigin sonuc" pattern'i. - Edge runtime:
app/api/*route'lari (varsa, kontrol edilmedi) edge'e tasi. Auth middleware'i zaten edge-compatible olabilir. createImageBitmapthumbnail decode worker thread'de. Buyuk foto preview'lerini decode etmek main thread'i bloklamasin.- Resource hints:
<link rel="dns-prefetch" href="//api.hasari.app">+<link rel="preconnect" href="https://r2.cloudflarestorage.com">annotated image domain'i icin.
6) Olcum oneri (gercek lighthouse calistirilabilirse)
# Chromium gerektirir
pnpm dlx lighthouse http://localhost:3000 --output=html --output-path=./lighthouse-home.html --view --preset=desktop
pnpm dlx lighthouse http://localhost:3000/results/demo-001 --output=html --output-path=./lighthouse-results.html --preset=mobile --throttling.cpuSlowdownMultiplier=4
Mobil ozellikle --throttling.cpuSlowdownMultiplier=4 ile gercek bir orta cihaz simule eder. Bu doknman sadece kaynak okumaya dayali olsa da, asagidaki 3 metric mutlaka olcum sonrasi dogrulanmali:
/LCP β hero card ya da Inter font swap mi?/inspectINP β drop event handler'i 200 ms'in altinda mi?/results/[id]CLS β annotated PNG yuklenince layout kayiyor mu?
Sonuc: MVP icin kullanilabilir ama hicbir RSC / next/image / WebSocket optimizasyonu yok. Top 5 fix'i v0.1.1'de uygulayinca tahmini Performance 72 β 84 - 88 bandina ciker. v0.2'de RSC migration ile 90+ erisilir.