# Deep Code Review — Kochwas **Datum:** 2026-04-18 **Stand:** commit `5283ab9` auf `main` **Testsuite beim Start:** 158/158 grün **Scope:** `src/` (~97 Dateien), Migrations, Tests, Docker-Setup, alle Docs unter `docs/` --- ## TL;DR Der Code ist **gesund**. Keine toten Pfade, keine broken Features, keine strukturellen Fehlentscheidungen. Die vier auffälligsten Themen sind alle **Natural-Growth-Pressure** aus der v1.x-Phase, keine Fehler: 1. **Ein echter Doku-Bug:** `docs/ARCHITECTURE.md:55` sagt `recipe_ingredient` + `recipe_step` — die Tabellen heißen in Wirklichkeit `ingredient` / `step` (siehe `001_init.sql`). 5-Minuten-Fix. 2. **API-Handler duplizieren `parseId`** neunmal. Kandidat #1 für einen `src/lib/server/api-helpers.ts`. 3. **Page-Komponenten sind groß** geworden (`+page.svelte` 808 Zeilen, `recipes/[id]/+page.svelte` 757 Zeilen). Solange du allein dran arbeitest: akzeptabel. Sobald jemand mitprogrammiert: refactor. 4. **`yauzl` / `@types/yauzl` sind installiert, aber nicht importiert.** Reserviert für den noch fehlenden ZIP-Backup-Import. Entweder im Session-Handoff verankert lassen oder als Phase ziehen. Keine Sicherheits- oder Performance-Probleme im Code-Review aufgetaucht. Keine Reviewer-Korrekturen an der Architektur-Grundlinie (Server/Client-Trennung, Repository-Pattern, Runes-Stores). --- ## Quick-Wins (≤ 30 min pro Stück) | # | Titel | Aufwand | Wert | |---|---|---|---| | 1 | `ARCHITECTURE.md:55` auf `ingredient` + `step` korrigieren | 2 min | hoch (sonst debuggt jemand Geisterschema) | | 2 | `OPERATIONS.md:135` `IMAGES_PATH` → `IMAGE_DIR` | 2 min | niedrig, aber trivial | | 3 | `parseId` zentralisieren (`src/lib/server/api-helpers.ts`) | 20 min | hoch — 9 Call-Sites | | 4 | Unit-Test für `parseId`-Helper | 10 min | hoch — fängt zukünftige Regressionen | | 5 | `requireProfile()`-Helper in `recipes/[id]/+page.svelte` (Zeilen 124/143/166/188 räumen 4×7 Zeilen weg) | 15 min | mittel | | 6 | Timeout-Magic-Numbers nach `src/lib/constants.ts` (1500 ms, 30-min SW-Poll) | 10 min | mittel | | 7 | Deutsche Fehler-Texte in `api/recipes/[id]/image/+server.ts` englisch ziehen (Konsistenz) | 5 min | kosmetisch | | 8 | Im Session-Handoff `/api/recipes/[id]/image` (POST/DELETE) nachtragen | 5 min | niedrig | Summe: unter 90 Minuten — und du hast den Großteil der Haut-Irritationen unten. --- ## Größere Refactor-Kandidaten ### A. API-Endpoints entkoppeln (HIGH, 1–2 Std) Extrahiere `src/lib/server/api-helpers.ts` mit: - `parsePositiveIntParam(raw: string, field: string): number` — wirft via SvelteKit `error(400, …)` - `validateBody(body: unknown, schema: ZodSchema): T` — ersetzt die `safeParse()`-Loops in 8+ Handlern - gemeinsame `ErrorResponse`-Shape (aktuell mal `{message}`, mal `{message, issues}`) Nach dem Helper-Refactor sollten die Handler nur noch echtes Business-Logik enthalten und je 30–50 Zeilen kürzer werden. ### B. Search-State aus `+page.svelte` ziehen (HIGH, halber Tag) `+page.svelte` trägt 20+ `$state`-Variablen (`query`, `hits`, `webHits`, `searching`, `webError` …) und duplizierte Search-UI in `+layout.svelte`. Vorschlag: `src/lib/client/search.svelte.ts` mit `search()`, `loadMore()`, `clear()`. Danach ist das Page-File halbiert und der Layout-Nav-Search nutzt denselben Store. ### C. `RecipeEditor` / `RecipeView` in Sub-Components zerlegen (MEDIUM, halber Tag) Kandidaten: `IngredientRow.svelte`, `StepList.svelte`, `TimeDisplay.svelte`, `ImageUploadBox.svelte`. Vorteile: isoliert testbar, wiederverwendbar in Preview-Seite. **Aber:** keine Eile, solange niemand sonst drin arbeitet. ### D. Ingredient-Parser-Edge-Cases (HIGH, 2–3 Std) Der Parser (`src/lib/server/parsers/ingredient.ts`) und seine Tests decken ASCII-Ganzzahlen + Dezimal + Brüche ab. Fehlt: - Unicode-Brüche (½, ⅓, ¼) - führende Nullen, wissenschaftliche Notation - Locale-Kommadezimal (deutsche Rezepte!) - 0-Portionen, negative Mengen Parametrisierte Tests anlegen, dann Parser ggf. mit Zod-Refinement absichern. --- ## Einzelbefunde im Detail ### Dead Code - Unused Deps: `yauzl`, `@types/yauzl` (absichtlich für Phase 5b; Entscheidung treffen: behalten oder entfernen bis Phase kommt). - `RequestShape` (`src/lib/sw/cache-strategy.ts:3`) und `ManifestDiff` (`src/lib/sw/diff-manifest.ts:4`) sind exportiert, aber nur intern benutzt — `export` weg oder im Test importieren. - Alle 97 Source-Files erreichbar, keine orphan-Assets, keine TODO/FIXME/HACK-Marker, keine großen auskommentierten Blöcke. ### Redundanzen - `parseId`/`parsePositiveInt` — 9 Sites: `api/recipes/[id]/`, `…/favorite`, `…/rating`, `…/cooked`, `…/comments`, `…/image`, `api/profiles/[id]/`, `api/domains/[id]/`, `api/wishlist/[recipe_id]/` - Fetch-try/catch-alert-Pattern in 5 Svelte-Komponenten: `recipes/[id]/+page.svelte` (2×), `admin/domains/+page.svelte` (2×), `admin/profiles/+page.svelte` - Zod-`safeParse` + gleicher Error-Throw in 12+ Endpoints - `parseQty` + Zutat-Reassembly in `RecipeEditor` dupliziert Logik aus `parseIngredient` — könnte über `src/lib/shared/` geteilt werden - Profile-Guard (`if (!profile.active) alert(…)`) 4× identisch in `recipes/[id]/+page.svelte` ### Struktur - Große Dateien: `+page.svelte` (808), `recipes/[id]/+page.svelte` (757), `+layout.svelte` (678), `RecipeEditor` (630), `recipes/+page.svelte` (539). Keine davon ist kaputt; alle sind Wachstum unter Last. - API-Error-Shape: mehrheitlich `{message}`, `profiles/+server.ts` gibt zusätzlich `{issues}` aus (Zod-Details). Festschreiben. - Store-Init-Races: `profile.svelte.ts` und `search-filter.svelte.ts` laden bei erstem Zugriff. Komponenten sehen ggf. Leer-State vor Fetch. Optional `loading`-Flag. - Konsolen-Logs: 6 Stück in Prod-Build (`service-worker.ts` 2×, `searxng.ts` 3×, `sw-register.ts` 1×). Vermutlich Absicht; als Dok-Kommentar festhalten oder in `if (DEV)`-Guards packen. - Svelte-5-Runes-Stores sind konsistent, keine God-Stores. - TypeScript: `strict` an, 0× `any`, 0× Server-Import-in-Client — bestätigt die CLAUDE.md-Regel. ### Docs-vs-Code-Mismatches | Fundstelle | Fix | |---|---| | `ARCHITECTURE.md:55` — `recipe_ingredient` + `recipe_step` | `ingredient` + `step` | | `OPERATIONS.md:135` — `IMAGES_PATH` | `IMAGE_DIR` | | `session-handoff-2026-04-17.md:46` — fehlt `/api/recipes/[id]/image` (POST/DELETE) | ergänzen | | Alle Gotchas in `CLAUDE.md` | ✓ verifiziert, stimmen | | Alle Claims im offline-PWA-Spec | ✓ verifiziert, alle in Code vorhanden | --- ## Was bleibt wie es ist - **Migrationen:** 001–011 sind historisch sauber. 008 + 010 löschen beide den Thumbnail-Cache — Feature-Iteration, kein Bug. **Keine** bestehende Migration anfassen (das ist ohnehin die dokumentierte Regel). - **Service-Worker:** Zombie-Cleanup-Logik (`pwa.svelte.ts`) ist Kunst, aber funktioniert und ist kommentiert. Unit-Tests decken beide Zweige (Zombie vs alter SW) ab. - **Repository-Pattern:** Cleane Schichtung. Nicht refactoren. - **Test-Suite:** 23 Dateien, 158 Tests, volle Integration inkl. DB/HTTP/Import/SearXNG. Leichte Lücken bei Parser-Edge-Cases (siehe oben). --- ## Ampel | Dimension | Status | |---|---| | Architektur & Schichten | 🟢 gesund | | Dead Code | 🟢 minimal | | Redundanzen | 🟡 adressierbar, nicht dringend | | Datei-/Komponenten-Größen | 🟡 zwei Pages ≥ 750L | | Tests | 🟢 stark, Edge-Cases ausbaufähig | | Doku | 🟡 1 inhaltlicher Fehler + 1 ENV-Tippfehler, sonst stabil | | Sicherheit/Perf | 🟢 keine Funde im statischen Review | --- ## Vorgeschlagene Reihenfolge 1. Heute (10 min): Quick-Wins 1 + 2 (ARCHITECTURE-Tabellen, OPERATIONS-ENV). 2. Nächste Session (2 h): `api-helpers.ts` + `parseId`-Consolidation + Tests (Refactor A). 3. Bei Zeit: Search-State-Store (Refactor B) — bringt beim nächsten Feature sofort Dividende. 4. Phase-5b: `yauzl` einsetzen ODER Deps entfernen. --- ## Teilreports Die vollständigen Agent-Befunde liegen daneben: - `docs/superpowers/review/dead-code.md` - `docs/superpowers/review/redundancy.md` - `docs/superpowers/review/structure.md` - `docs/superpowers/review/docs-vs-code.md` Review-Metadaten: 4 parallele Explore-Agenten, jeweils read-only, Summen manuell gegen Code verifiziert (Line-Counts, Tabellen-Namen, ENV-Namen, `parseId`-Sites).