u
This commit is contained in:
87
.cursor/rules/no-silent-fallbacks.mdc
Normal file
87
.cursor/rules/no-silent-fallbacks.mdc
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
description: Keine Fallbacks, keine alternativen API-Felder oder Endpoints
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Keine Fallbacks, keine alternativen APIs
|
||||
|
||||
Stille Fallbacks und parallele API-Verträge verschleiern Fehler und entwerten Tests. Fehlende oder falsche Daten müssen **sichtbar** scheitern — nicht durch Ersatzwerte kaschiert werden.
|
||||
|
||||
## Verboten
|
||||
|
||||
### Alternative Feldnamen / Response-Formen
|
||||
|
||||
```js
|
||||
// ❌
|
||||
const nodes = data?.tree ?? data?.categories ?? [];
|
||||
const target = article.sku ?? article.slug ?? article.id;
|
||||
const rootId = data.rootCategoryId ?? companyRootCategoryId ?? null;
|
||||
|
||||
// ✅
|
||||
requireObject(data, 'category/tree');
|
||||
const nodes = requireArray(data.tree, 'category/tree.tree');
|
||||
const rootId = data.rootCategoryId ?? companyRootCategoryId;
|
||||
requireDefined(rootId, 'rootCategoryId');
|
||||
```
|
||||
|
||||
Ein Endpoint, ein Shape, ein Feldname — dokumentiert in `src/api/index.js`.
|
||||
|
||||
### Alternative Endpoints für denselben Zweck
|
||||
|
||||
```js
|
||||
// ❌ Zwei Wege für dieselben Daten
|
||||
category.getTree();
|
||||
http('/api/article/byCategory?...');
|
||||
|
||||
// ✅ Nur der vereinbarte Endpoint
|
||||
category.getArticles(categoryId);
|
||||
```
|
||||
|
||||
Neue Endpoints nur nach expliziter Vereinbarung; alte nicht parallel als Fallback behalten.
|
||||
|
||||
### Default-Werte beim Parsen / in Selectors
|
||||
|
||||
```js
|
||||
// ❌
|
||||
selectCompanyIso639 = (s) => s.company.current?.iso639 ?? 'de';
|
||||
name: raw.name ?? 'Unbenannt';
|
||||
|
||||
// ✅
|
||||
selectCompanyIso639 = (s) => s.company.current?.iso639;
|
||||
// UI zeigt leer oder Fehler, wenn null
|
||||
```
|
||||
|
||||
### Display-Platzhalter für fehlende Daten
|
||||
|
||||
```js
|
||||
// ❌
|
||||
<TableCell>{row.cName || '—'}</TableCell>
|
||||
label={`Kategorie ${id}`}
|
||||
|
||||
// ✅
|
||||
<TableCell>{row.cName}</TableCell>
|
||||
// oder gar nicht rendern
|
||||
```
|
||||
|
||||
### Ersatz-Identifikatoren in URLs
|
||||
|
||||
```js
|
||||
// ❌
|
||||
/artikel/${sku ?? slug ?? id}
|
||||
|
||||
// ✅
|
||||
/artikel/${sku} — nur wenn sku gesetzt, sonst disabled / kein Link
|
||||
```
|
||||
|
||||
## Erlaubt
|
||||
|
||||
- **Formular-Startwerte** beim Öffnen eines leeren Dialogs (z. B. `defaultIso639()` für Neuanlage)
|
||||
- **Explizite Fehlerbehandlung:** `requireObject` / `requireArray` / `requireDefined` aus `src/utils/apiShape.js` → Thunk rejected → `BackendError`
|
||||
- **Optionale Vertragsfelder** nur wenn im API-Vertrag als optional dokumentiert (z. B. `sort` in Sort-Comparator)
|
||||
|
||||
## Bei neuem Code
|
||||
|
||||
1. API-Vertrag in `src/api/index.js` kommentieren
|
||||
2. Antwort mit `apiShape`-Helfern validieren
|
||||
3. Kein `??`-Ketten für „vielleicht heißt es anders“
|
||||
4. Fehler im UI anzeigen, nicht durch Defaults überdecken
|
||||
Reference in New Issue
Block a user