Web-Performance ist nicht "soll gut sein", sondern eine Zahl, die Entscheidungen beeinflusst. 2026 führt INP – die Nachfolgemetrik von FID – dazu, dass mobile Conversions um 15–20 % sinken, wenn sie 200 ms überschreitet (Google Chrome UX Report 2025 Kohorte). Um diesen Level zu halten, brauchst du keine Vermutungen, sondern automatisierte Kontrolle in der CI-Pipeline. Lighthouse CI, RUM und ein Regressions-Alarmsystem – welche Schwellwerte bindest du wo ein, und wo steht jede Metrik im Entscheidungsprozess? Dieser Artikel zeigt die Architektur, Performance-Budgets vom Test zur Geschäftsentscheidung zu verbinden – mit konkreten Zahlen.

Performance Budget: Definition und Sprint-Planung

Ein Performance Budget setzt die Obergrenze für Seitenladezeit, Bundle-Größe und Runtime-Metriken. Total Bundle ≤ 250 KB, FCP ≤ 1,2 s, INP ≤ 200 ms – das sind Budget-Werte. Sie werden vor dem Sprint definiert und zum Merge-Kriterium. Überschreitet ein PR diese Grenzen, entweder: Code refaktorieren, Feature verschieben oder Budget erhöhen und den Conversions-Verlust akzeptieren.

Budget-Definition nutzt drei Quellen: (1) Googles Core Web Vitals Schwellwerte (LCP <2,5 s, INP <200 ms, CLS <0,1), (2) p75 Benchmark aus RUM-Daten (75 % deiner Nutzer bleiben unter diesem Level), (3) Conversions-Korrelationsbericht (jede 100 ms LCP-Anstieg senkt Conversions um −2 %). Budget ist keine einzelne Zahl, sondern metrik-basiert aufgeschlüsselt:

MetrikSchwellwertQuelle
LCP<2,5 sCWV offiziell
INP<200 msCWV 2024+
CLS<0,1CWV offiziell
Total JS<300 KB gzipHTTP Archive p75
FCP<1,8 sInternal RUM

Diese Tabelle schreibst du in performance.config.json, Lighthouse CI liest die Datei und blockiert PR-Merges bei Überschreitung.

Lighthouse CI: Performance als Merge-Kriterium

Lighthouse CI führt bei jedem PR einen Lighthouse-Audit aus und vergleicht die Ergebnisse mit deinem Performance-Budget (Open-Source-Tool von Google). Es integriert sich mit GitHub Actions, GitLab CI und CircleCI. Grundablauf: (1) PR öffnet sich, (2) CI baut Artefakte, (3) lhci autorun startet ein Lighthouse-Audit in der Test-Umgebung, (4) Ergebnisse werden gegen performance.config.json validiert, (5) Budget-Verletzung = PR blockiert, Merge unmöglich.

Beispiel-Konfiguration (.lighthouserc.json):

{
  "ci": {
    "collect": {
      "url": ["http://localhost:3000/", "http://localhost:3000/product/sample"],
      "numberOfRuns": 3
    },
    "assert": {
      "preset": "lighthouse:no-pwa",
      "assertions": {
        "largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
        "interactive": ["error", {"maxNumericValue": 3500}],
        "cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
        "total-byte-weight": ["warn", {"maxNumericValue": 307200}]
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}

LCP > 2,5 s blockiert den PR (error), Total Bytes > 300 KB gibt nur eine Warnung (warn). 3 Durchläufe, um Netzwerk-Variance auszugleichen – eine einzelne Messung hat zu viel Noise. Der Tradeoff: Lighthouse CI läuft auf einem lokalen Dev-Server, kann keine Production-CDN simulieren. Die Ergebnisse sind konservativ, Production ist normalerweise besser – aber Budget-Grenzen sollten trotzdem nicht überschritten werden.

Lighthouse CI + Vercel Preview: Real-Staging-Test

Auf Vercel/Netlify wird automatisch eine Preview-URL für jeden PR erstellt. Wenn du Lighthouse CI auf diese Preview-URL zeigst, testest du in einer Production-ähnlichen Umgebung. GitHub Actions Beispiel:

- name: Run Lighthouse CI
  env:
    LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_TOKEN }}
  run: |
    npm install -g @lhci/cli
    lhci autorun --collect.url=${{ steps.vercel.outputs.preview-url }}

steps.vercel.outputs.preview-url kommt von der Vercel-Aktion. So testest du CDN-Caching, Edge-SSR und Image-Optimierung. Bei Budget-Überschreitung kommentiert CI automatisch auf dem PR, optional Slack-Benachrichtigung.

RUM: Performance-Budget aus echten Nutzerdaten kalibrieren

Lighthouse CI ist synthetisches Testen – kontrollierte Bedingungen, immer gleiche Netzwerk-Simulation. RUM (Real User Monitoring) sammelt Metriken von echten Besuchern. Der Unterschied ist kritisch: Lighthouse simuliert 3G, RUM sieht 4G/5G/Fiber-Mix; Lighthouse testet kalten Cache, RUM erfasst Repeat-Visitor-Caching. Budget nur nach Lighthouse zu setzen bedeutet, echte Nutzererfahrung zu verpassen.

Als RUM-Collector nutzt man Googles Web Vitals Library. Sie misst CWV-Metriken auf jeder Seite und sendet sie an einen Beacon-Endpoint. Implementierungsbeispiel:

import {onCLS, onINP, onLCP} from 'web-vitals';

function sendToAnalytics(metric) {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    id: metric.id,
    rating: metric.rating
  });
  navigator.sendBeacon('/analytics', body);
}

onCLS(sendToAnalytics);
onINP(sendToAnalytics);
onLCP(sendToAnalytics);

Dein Backend schreibt diese Daten in BigQuery (oder dein Data Warehouse). Dann berechnest du p75:

SELECT
  APPROX_QUANTILES(value, 100)[OFFSET(75)] AS p75_lcp
FROM metrics
WHERE name = 'LCP' AND date >= DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY);

Wenn p75_lcp = 2,8 s und dein Budget 2,5 s ist, liegt das Budget unter 75 % deines echten Traffics – entweder Budget auf 2,8 s anheben (mit Conversions-Impakt) oder Code optimieren. p75 ist Standard, weil Google CWV auch nach p75 bewertet.

RUM + Segmentierung: Device- und Region-basierte Budgets

Nicht ganzer Traffic mit einem Budget. Mobile-LCP ist ~40 % höher als Desktop (Chrome UX Report 2025), Indien-Traffic ist ~60 % langsamer als USA. RUM-Daten segmentieren, Budget differenzieren:

SegmentLCP BudgetINP Budget
Desktop2,2 s180 ms
Mobile3,0 s220 ms
Indien3,5 s250 ms

Dafür erweiterst du den RUM-Beacon um deviceType und country (GeoIP Backend-seitig), analysierst in BigQuery mit GROUP BY device. Lighthouse CI Multi-Config nicht nativ, aber separate Workflows (lhci-mobile.json + lhci-desktop.json).

Regressions-Alarm: Wenn Performance sinkt, Slack sofort

Budget definiert, CI kontrolliert, RUM läuft – aber Post-Deploy sinkt die Performance in Production? Dann musst du das in 5 Minuten wissen, nicht in 3 Stunden. Ein Job analysiert RUM-Daten alle 5 Minuten (Cloudflare Workers Cron, AWS Lambda EventBridge, GCP Cloud Scheduler) und triggert Alarme.

Beispiel-Logik (Pseudo-Code):

// Alle 5min ausgeführt
async function checkRegression() {
  const current = await query('SELECT AVG(value) FROM metrics WHERE name="LCP" AND timestamp > NOW() - INTERVAL 5 MINUTE');
  const baseline = await query('SELECT AVG(value) FROM metrics WHERE name="LCP" AND timestamp BETWEEN NOW() - INTERVAL 1 DAY AND NOW() - INTERVAL 1 HOUR');
  
  if (current > baseline * 1.15) { // 15% Anstieg
    await sendSlack({
      text: `🚨 LCP Regression: ${current}ms (Baseline ${baseline}ms)`,
      channel: '#performance-alerts'
    });
  }
}

Baseline 1 Stunde zurück, weil der Deploy gerade sein könnte. 15 % Schwellwert ist kalibrierbar – 10 % zu sensitiv (False Positives), 25 % zu spät. Optional: PagerDuty, Opsgenie für On-Call-Integration. Team entscheidet Rollback oder Hotfix.

Root Cause mit Lighthouse Diff

Alarm kommt rein, LCP ist gestiegen – warum? Lighthouse CI zeigt nur Schwellwert-Verletzung, nicht Root Cause. Mit lhci compare vergleichst du zwei Audit-Läufe:

lhci compare --base=build-1234 --head=build-1235 --preset=lighthouse:all

Output: "unused-javascript increased by 45 KB", "server-response-time +120 ms". Das grenzt Root Cause ein. Bundle-Analyzer (webpack-bundle-analyzer, Next.js analyze) zeigt, woher 45 KB stammen; Server-Logs zeigen, welche Operation 120 ms länger dauert.

Performance an Conversions binden: Attribution

Budget ist Technik, aber um es ins Entscheidungs-System zu binden, braucht es Business-Metrik. "LCP von 2,5 s auf 3,0 s erhöhen senkt Conversions um 4 %" – dieser Report muss aus A/B-Test oder Kohorten-Analyse kommen. A/B-Test: 50 % Traffic bekommt absichtlich langsamere Version (500 ms künstliche Verzögerung), Conversions vergleichen. Kohorten-Analyse: RUM-Daten splitten – Conversions-Rate für LCP <2 s vs. LCP >3 s.

Google Analytics 4 + BigQuery Export zur Korrelation:

SELECT
  CASE 
    WHEN lcp < 2000 THEN 'fast'
    WHEN lcp BETWEEN 2000 AND 4000 THEN 'medium'
    ELSE 'slow'
  END AS lcp_bucket,
  COUNT(DISTINCT user_pseudo_id) AS users,
  COUNTIF(event_name = 'purchase') / COUNT(DISTINCT session_id) AS conversion_rate
FROM analytics_events
LEFT JOIN rum_metrics ON analytics_events.session_id = rum_metrics.session_id
GROUP BY lcp_bucket;

Ergebnis-Tabelle:

LCP BucketConversions-Rate
fast4,2 %
medium3,6 %
slow2,9 %

LCP von 3,0 s auf 2,5 s senken = +16,7 % Conversions-Lift. Bei 100K monatliche Besucher = +1670 Conversions; mit AOV $50 = +$83K Revenue. Dieser Report geht nicht an den CTO, sondern an den CFO – hier wird Sprint-Priorität gesetzt.

Budget-Überschreitung: Tradeoff-Entscheidung

Sprint-Feature landet, Bundle wächst um 50 KB, Budget bricht. Drei Wege: (1) Code refaktorieren (Code-Splitting, Lazy Loading), (2) Budget erhöhen + Conversions-Verlust akzeptieren, (3) Feature verschieben. Entscheidung zahlgesteuert: 50 KB extra = +200 ms LCP (Lighthouse Trace), +200 ms LCP = −2 % Conversions (RUM-Korrelation). Wenn Feature selbst +5 % Lift bringt, netto +3 % – weitermachen. Wenn nur +1 %, netto −1 % – verschieben.

Dafür bauen wir intern einen "performance cost estimator" (Tabelle/Tool). Input: Bundle-Size-Delta; Output: geschätztes LCP-Delta + Conversions-Impact. Modell aus deinen RUM-Daten: jede 10 KB Bundle ≈ +30 ms LCP, jede 100 ms LCP ≈ −0,8 % Conversions. Zeige das PM, die setzen Feature-Priorität.

Headless Commerce: Budget für API-Latenz

E-Commerce = Performance = Revenue. Headless Commerce (Shopify Hydrogen, Remix, Next.js) gibt dir Frontend-Bundle-Kontrolle, aber Backend-API-Latenz zählt auch zum Budget. Shopify Storefront API durchschnittlich 150 ms, das muss ins Budget: LCP = TTFB (150 ms) + FCP (800 ms) + LCP-Delta (600 ms) = 1550 ms. Budget 2500 ms = 950 ms Puffer.

Regressions-Ursachen in Headless meist: (1) API-Query-Komplexität steigt (GraphQL-Depth +2 Level = +50 ms), (2) SSR-Component-Count (20 Components = +100 ms Hydration), (3) Third-Party-Scripts (Analytics-Tag = +200 ms). Lighthouse CI unterscheidet das nicht – RUM-Trace nötig. Next.js Middleware mit Server-Timing Header:

export function middleware(req) {
  const start = Date.now();
  const res = NextResponse.next();
  res.headers.set('Server-Timing', `api;dur=${Date.now() - start}`);
  return res;
}

Im Chrome Dev