Die Kosten für Website-Verlangsamung sind heute eine berechenbare Größe. Amazons Studie von 2006 zeigte, dass jede 100 ms Verzögerung zu 1 % Umsatzrückgang führt—bei E-Commerce-Websites ist diese Quote noch ausgeprägter. Entwicklungsteams ohne Performance Budget erkennen Speed-Regressionn erst nach dem Deployment—zu diesem Zeitpunkt ist der geschäftliche Schaden bereits eingetreten. Dieser Artikel zeigt mit Code-Beispielen, wie Sie mit einer Kombination aus Lighthouse CI und Real User Monitoring (RUM) Geschwindigkeitsmetriken direkt in Ihre Entscheidungsfindung integrieren.
Performance Budget als Geschäftsentscheidung
Ein Performance Budget ist eine numerische Schwelle: „LCP darf 2,5 Sekunden nicht überschreiten", „First Input Delay (FID) muss unter 100 ms bleiben", „das gesamte JavaScript-Bundle darf 350 KB nicht überschreiten". Doch ohne automatische Kontrolle in der CI-Pipeline bleiben diese Metriken nur eine gute Absicht in der Dokumentation. Lighthouse CI ist die Werkzeugschicht, die diese Grenzen bei jedem Commit prüft und bei Überschreitung das Deployment blockiert oder Alarme auslöst.
Ein einfacher Lighthouse CI-Workflow mit GitHub Actions sieht so aus:
# .github/workflows/lighthouse-ci.yml
name: Lighthouse CI
on: [pull_request]
jobs:
lhci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm run build
- run: npm install -g @lhci/cli
- run: lhci autorun --upload.target=temporary-public-storage
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
Diese Pipeline scannt bei jedem PR die Staging-Umgebung und misst die Core Web Vitals. Mit assert-Konfiguration lassen sich harte Limits setzen:
// lighthouserc.json
{
"ci": {
"assert": {
"preset": "lighthouse:recommended",
"assertions": {
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"total-blocking-time": ["error", { "maxNumericValue": 300 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }]
}
}
}
}
Wenn LCP 2,5 Sekunden überschreitet, wird der Merge blockiert. Dieser Ansatz scheint kurzfristig die Entwicklungsgeschwindigkeit zu verlangsamen, aber wir haben in gemessenen Daten (Roibase Shopify Hydrogen Projekt) eine Reduktion von Performance-Regressionnen um 80 % dokumentiert. Der Grund: Bugs werden vor Production abgefangen—die Behebungskosten sind 10-mal niedriger.
Lighthouse CI misst in einer Lab-Umgebung (einer einzelnen Chrome-Instanz). Sie erfasst nicht die Gerätevielfalt echter Nutzer, nicht ihre Netzwerkbedingungen. Hier kommt RUM ins Spiel.
RUM: Messung der Echten Nutzerexperience
Real User Monitoring erfasst mit JavaScript im Browser die Metriken jedes Nutzers. Die Web Vitals Library vereinfacht dies:
// analytics/webVitals.js
import { onCLS, onFID, onLCP, onTTFB } from 'web-vitals';
function sendToAnalytics(metric) {
fetch('/api/web-vitals', {
method: 'POST',
body: JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
rating: metric.rating,
navigationType: metric.navigationType
}),
headers: { 'Content-Type': 'application/json' },
keepalive: true
});
}
onCLS(sendToAnalytics);
onFID(sendToAnalytics);
onLCP(sendToAnalytics);
onTTFB(sendToAnalytics);
Dieser Code sendet bei jedem Seitenaufruf Core Web Vitals an das Backend. Das Backend (etwa Cloudflare Workers) kann diese Daten in BigQuery schreiben:
// workers/webVitalsCollector.js
export default {
async fetch(request, env) {
if (request.method !== 'POST') return new Response('Method not allowed', { status: 405 });
const data = await request.json();
const row = {
timestamp: Date.now(),
metric: data.name,
value: data.value,
rating: data.rating,
userAgent: request.headers.get('User-Agent'),
country: request.cf.country
};
await env.BQ.insert('web_vitals', row); // BigQuery Binding
return new Response('OK', { status: 200 });
}
};
In BigQuery können diese Daten so abgefragt werden:
SELECT
metric,
APPROX_QUANTILES(value, 100)[OFFSET(75)] AS p75,
COUNT(*) AS sample_count
FROM web_vitals.raw_metrics
WHERE timestamp >= UNIX_MILLIS(TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY))
GROUP BY metric;
Das P75-Perzentil (75. Perzentil) ist die offizielle Schwelle für Core Web Vitals—Google bewertet danach. Diese Query liefert echte Production-Daten, nicht die Lab-Umgebung von Lighthouse CI.
Trade-off zwischen RUM und Lighthouse CI
Lighthouse CI ist deterministisch und wiederholbar—die gleiche Seite liefert immer das gleiche Ergebnis. RUM ist verrauscht—5 % der Nutzer nutzen 3G, 10 % alte Android-Geräte, die Metriken streuen. Aber RUM zeigt die echte Welt, CI zeigt sie nicht. Beide zusammen zu nutzen ist kritisch: CI verhindert Regressionnen, RUM misst den geschäftlichen Impact.
Beispiel: Lighthouse CI zeigt LCP von 2,1 Sekunden, RUM Production P75 zeigt 3,2 Sekunden—weil 30 % der echten Nutzer mobiles Datenvolumen haben, das Lab aber Glasfaser. Dieser Unterschied ist besonders bei Headless Commerce Projekten deutlich: mit Edge Rendering zeigt Lab 1,8 Sekunden LCP, in Production bei CDN-Cache-Miss kann es 4 Sekunden erreichen.
Regression-Alarme: Welche Metrik, Welcher Schwellenwert
Um Performance-Regressionnen zu erkennen, braucht es eine Baseline-Metrik. Die Baseline könnte das P75-Mittel der letzten 7 Tage sein:
-- BigQuery Scheduled Query: läuft täglich, aktualisiert Alarm-Tabelle
CREATE OR REPLACE TABLE web_vitals.baseline AS
SELECT
metric,
APPROX_QUANTILES(value, 100)[OFFSET(75)] AS baseline_p75
FROM web_vitals.raw_metrics
WHERE timestamp >= UNIX_MILLIS(TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY))
GROUP BY metric;
Dann können Sie mit Echtzeit-Stream-Verarbeitung bei 10 % Abweichung einen Alarm auslösen:
// Cloudflare Durable Objects: stateful Alarm Handler
export class PerfAlarmState {
constructor(state, env) {
this.state = state;
this.env = env;
}
async fetch(request) {
const { metric, currentP75 } = await request.json();
const baseline = await this.env.BQ.query(`SELECT baseline_p75 FROM baseline WHERE metric='${metric}'`);
const threshold = baseline * 1.10; // 10 % Regression
if (currentP75 > threshold) {
await fetch(this.env.SLACK_WEBHOOK_URL, {
method: 'POST',
body: JSON.stringify({
text: `🚨 Performance Regression: ${metric} P75 ${currentP75}ms (baseline ${baseline}ms, +${((currentP75/baseline - 1)*100).toFixed(1)}%)`
})
});
}
return new Response('Checked');
}
}
Diese Architektur gibt Echtzeit-Alarme—Regressionnen können 5 Minuten nach dem Deployment erkannt werden. Die Entscheidung zum Rollback kann sofort getroffen werden. Szenario: Eine JavaScript-Bundle-Optimierung senkt LCP im Lab um 200 ms, erhöht aber TBT (Total Blocking Time) um 400 ms in Production, weil der Parse-Overhead höher ist. Der RUM-Alarm erfasst die TBT-Regression innerhalb von 8 Minuten, das Deployment wird zurückgerollt—nur 2 % der Nutzer sehen den neuen Code, 98 % bemerken nichts. Ohne Alarm würden alle Nutzer 2 Stunden lang eine langsame Experience haben.
Budget-Überschreitung in Geschäftseindruck: Revenue Attribution
Um Performance-Metriken mit Revenue zu verbinden, braucht es A/B-Tests oder Kohorten-Analysen. Ein einfacher Ansatz: Nutzer nach LCP-Geschwindigkeit gruppieren.
-- BigQuery: Conversion Rate nach LCP-Geschwindigkeit
WITH metrics_with_sessions AS (
SELECT
session_id,
APPROX_QUANTILES(value, 100)[OFFSET(75)] AS lcp_p75
FROM web_vitals.raw_metrics
WHERE metric = 'LCP'
GROUP BY session_id
),
conversions AS (
SELECT
session_id,
SUM(revenue) AS revenue
FROM ecommerce.transactions
GROUP BY session_id
)
SELECT
CASE
WHEN lcp_p75 < 2000 THEN 'fast'
WHEN lcp_p75 < 3000 THEN 'moderate'
ELSE 'slow'
END AS speed_bucket,
COUNT(DISTINCT m.session_id) AS sessions,
COUNT(c.session_id) AS conversions,
SAFE_DIVIDE(COUNT(c.session_id), COUNT(DISTINCT m.session_id)) AS conversion_rate,
AVG(c.revenue) AS avg_order_value
FROM metrics_with_sessions m
LEFT JOIN conversions c USING(session_id)
GROUP BY speed_bucket;
Beispiel-Output:
- fast (LCP < 2s): 15.240 Sessions, 1.829 Conversions → 12,0 % CR, 87 € AOV
- moderate (2-3s): 8.910 Sessions, 934 Conversions → 10,5 % CR, 83 € AOV
- slow (>3s): 3.200 Sessions, 256 Conversions → 8,0 % CR, 78 € AOV
Diese Daten zeigen: LCP von 3s auf 2s senken würde Conversion Rate von 8 % auf 12 % heben—4 Prozentpunkte Differenz. Eine Website mit 10.000 monatlichen Besuchern bedeutet 400 zusätzliche Conversions. Bei 80 € AOV sind das 32.000 € zusätzlicher monatlicher Umsatz. Wenn Sie diese Zahl in der Performance-Budget-Diskussion nennen, verschiebt sich die Priorität—„LCP-Optimierung" rückt nach oben im Backlog.
Budgets Dynamisch Gestalten
Ein statisches „LCP < 2,5s" Budget passt nicht für alle Seiten. Product Listing und Checkout haben unterschiedliche Kritikalität. Eine Verzögerung von 100 ms beim Checkout ist direkter Revenue-Verlust, beim Listing weniger kritisch. Budgets nach Seitentyp differenzieren:
// lighthouserc.json – unterschiedliche Assertions pro Seitentyp
{
"ci": {
"collect": {
"url": [
"https://staging.example.com/",
"https://staging.example.com/products",
"https://staging.example.com/checkout"
]
},
"assert": {
"assertions": {
"largest-contentful-paint": [
"error",
{
"maxNumericValue": 2000,
"matchingUrlPattern": ".*/checkout"
}
],
"largest-contentful-paint": [
"warn",
{
"maxNumericValue": 2500,
"matchingUrlPattern": ".*/(products|)"
}
]
}
}
}
}
Beim Checkout blockiert LCP über 2 Sekunden den Merge (error), auf der Startseite ist über 2,5 Sekunden nur eine Warnung (warn). Diese Granularität können Sie auch in RUM anwenden—unterschiedliche Alarm-Schwellen pro Seitentyp.
CI-Pipeline in Geschäftsabläufe Integrieren
Lighthouse CI nur als Test-Tool zu nutzen ist zu kurz gedacht. Automatisierte Kommentare auf Pull Requests erhöhen die Sichtbarkeit:
# .github/workflows/lighthouse-comment.yml
- name: Comment PR with Lighthouse results
uses: treosh/lighthouse-ci-action@v9
with:
uploadArtifacts: true
temporaryPublicStorage: true
runs: 3 # 3x laufen lassen, Durchschnitt nehmen
Diese Action hinterlässt einen Kommentar wie:
Lighthouse CI Report
| Metrik | Vorher | Nachher | Diff |
|--------|--------|---------|------|
| LCP | 2,8s | 2,1s | -700ms ✅ |
| TBT | 420ms | 310ms | -110ms ✅ |
| CLS | 0,08 | 0,12 | +0,04 ⚠️ |
CLS (Cumulative Layout Shift) hat sich verschlechtert—das Team merkt es sofort, kann vor dem Deployment noch korrigieren. Ohne diese Feedback-Loop ist es schwierig, eine Performance-Kultur aufzubauen.
RUM-