Zum Hauptinhalt springen

Team Self-Assessment: Frontend-Reaktivitäts- und Architekturcheck

Ziel

Diese Checkliste identifiziert typische Frontend-Probleme (Paradigmen-Mismatch, imperatives Angular, State-Spiegelung, Vererbungs-Hierarchien) und macht sie messbar.

Dauer: 30–60 Minuten
Teilnehmer: 2–6 Devs (optional: Tech Lead/Architect)
Output: Score + Top-5 Maßnahmenliste


Scoring

Jede Aussage wird bewertet mit:

  • 0 = Nein / trifft nicht zu
  • 1 = teilweise / inkonsistent
  • 2 = Ja / konsequent umgesetzt

Zusätzlich gibt es pro Abschnitt Red Flags (sofortige Maßnahmen, unabhängig vom Score).


Abschnitt A – Datenfluss & State

A1: Single Source of Truth

  • (0/1/2) Pro Feature gibt es eine klare Quelle für State (Signal/Store/Stream).
  • (0/1/2) Es gibt keine Doppelhaltung (z.B. users$ und users: User[]).
  • (0/1/2) UI-State wird nicht implizit in Services mutiert (z.B. service.currentUser = ...).

Beobachtbarer Hinweis:
Wenn man fragt „wo kommt der Wert her?“, gibt es eine Antwort.

Typisches Gegenbeispiel:
„Das kommt aus dem Service, aber die Komponente cached es nochmal…“


A2: Ableitung statt Mutation

  • (0/1/2) Ableitungen werden explizit modelliert (computed, map, vm), nicht implizit in Methoden oder Templates.
  • (0/1/2) Collections werden überwiegend immutabel behandelt ([...], map, filter statt push/splice).
  • (0/1/2) DTOs werden beim Mapping nicht mutiert (kein „DTO als Domain Model“).

Red Flag:
users.push(...), dto.foo = ..., Object.assign(existing, ...) im UI-State.


A3: ViewModel-Disziplin

  • (0/1/2) Templates hängen primär am VM (vm, vm$, computed vm).
  • (0/1/2) Das Template enthält keine komplexen Berechnungen.
  • (0/1/2) API/DTO-Strukturen werden nicht direkt im Template verwendet.

Definition (normativ):

Das Template kennt das VM – nicht die Datenquellen.


Abschnitt B – Subscriptions, Async, Lifecycle

B1: Subscription Hygiene

  • (0/1/2) Komponenten enthalten keine subscribe() außer für echte Side-Effects.
  • (0/1/2) Unsubscribe wird nicht manuell gehandhabt (kein destroy$, kein Subscription-Feld).
  • (0/1/2) Promise-Konvertierung (firstValueFrom, toPromise) ist selten und begründet.

Red Flags:

  • Mehr als 3 subscribe() in einer Komponente.
  • destroy$ als Standardpattern in jeder Komponente.
  • Observable → Promise → imperatives Feld.

B2: Lifecycle Disziplin

  • (0/1/2) ngOnInit() enthält keine Orchestrierungskaskaden (load/setup/register).
  • (0/1/2) ngOnDestroy() enthält keine fachliche Logik.
  • (0/1/2) Keine Vererbungsabhängigkeit in Lifecycle Hooks (super.ngOnInit() etc.).

Heuristik:
ngOnInit sollte „langweilig“ sein.


Abschnitt C – Struktur, Kopplung, Wiederverwendung

C1: Keine God Base Component

  • (0/1/2) Es gibt keine Basisklasse, die viele Komponenten erben.
  • (0/1/2) Cross-Cutting Concerns werden nicht per extends geteilt.
  • (0/1/2) Wiederverwendung erfolgt über Komposition (DI, utilities, operators).

Red Flag:
Eine Basisklasse enthält: loading, error, permissions, routing helpers, subscriptions…


C2: Services sind nicht „Global State“

  • (0/1/2) Services enthalten keine öffentlichen mutable Felder als State.
  • (0/1/2) Services sind klar klassifiziert: API-Adapter, Facade, Store, Utility.
  • (0/1/2) Datenflüsse sind sichtbar, nicht durch Side Effects versteckt.

C3: Validierung & Form-Architektur

  • (0/1/2) Validierung ist deklarativ (Validators / schema / derived state), nicht „if-Kaskaden“.
  • (0/1/2) Validierungsregeln sind testbar und wiederverwendbar.
  • (0/1/2) Form-Status wird abgeleitet statt manuell gesetzt.

Red Flag:
if (!form.valid) { set error; return; } als primäres Validierungsmodell.


Abschnitt D – Nebenwirkungen & Fehlerbehandlung

D1: Side-Effects sind isoliert

  • (0/1/2) Side Effects sind klar lokalisiert (effect / tap / handler).
  • (0/1/2) Kein Side-Effect in map / computed.
  • (0/1/2) Navigation/Toasts/Logging passieren nicht „zufällig“ in Mappingketten.

D2: Fehlerbehandlung ist systemisch

  • (0/1/2) HTTP-Fehlerbehandlung erfolgt konsistent (Interceptor/Fassade/VM).
  • (0/1/2) UI behandelt Fehlerzustände als State (errorState), nicht als try/catch Orchestrierung.
  • (0/1/2) Loading/Error/Empty sind als States modelliert.

Abschnitt E – Teampraktiken

E1: Code Review Leitplanken

  • (0/1/2) subscribe() in Komponenten ist ein Review-Trigger.
  • (0/1/2) Vererbung in UI ist ein Review-Trigger.
  • (0/1/2) Doppelter State ist ein Review-Trigger.
  • (0/1/2) Team hat ein gemeinsames Zielbild (RxJS-first, Signals-first oder Hybrid).

Ergebnisinterpretation

0–20 Punkte: Imperativ dominiert

  • Hohe Lifecycle-Kopplung
  • Viele Spiegelzustände
  • Manuelles Ressourcenmanagement

Empfehlung:
Stabilisieren durch 3 Regeln:

  1. kein subscribe() außer Side-Effects
  2. VM/Computed einführen
  3. keine UI-Vererbung

21–35 Punkte: Hybrid & inkonsistent

  • Reaktivität vorhanden
  • aber inkonsistent
  • hohe kognitive Last

Empfehlung:
Team-Konventionen + Refactoring-Routen definieren.


36–50 Punkte: Reaktiv & deklarativ

  • Klarer Datenfluss
  • geringere Kopplung
  • gute Testbarkeit

Empfehlung:
Signals/VM konsequent standardisieren, Metriken einführen.


50+ Punkte: High Maturity

  • Signal/VM-Ableitungen dominieren
  • Side-Effects isoliert
  • geringe architektonische Reibung

Empfehlung:
Guardrails automatisieren (Lint/PR templates), Wissen multiplizieren.


Sofortmaßnahmen (Top 5)

Unabhängig vom Score:

  1. Subscribe in Komponenten verbieten (außer Side-Effects).
  2. Doppelten State eliminieren (eine Quelle).
  3. VM-Pattern standardisieren (vm / vm$ / computed vm).
  4. UI-Vererbung verbieten (Komposition statt BaseComponent).
  5. Side-Effects isolieren (effect/tap/handler, nicht in map/computed).

Optional: Quick Audit (15 Minuten)

Zählt in einem Feature-Ordner:

  • Anzahl subscribe(
  • Anzahl firstValueFrom( / toPromise(
  • Anzahl extends Base
  • Anzahl mutable service fields (public foo: ...)
  • Anzahl .push( / .splice(

Wenn ihr hier hohe Werte habt, seid ihr sehr wahrscheinlich in Stufe 0–2.