Von der Baseline-Messung bis zum CI-Gate
Ein Performance-Budget setzt klare Obergrenzen für Ladezeit, Bundle-Größe und Anzahl der Requests, bevor ein Shop langsam wird, statt erst danach zu reagieren. Wer Budgets aus echten Baseline-Messungen ableitet und im Build automatisch prüft, verhindert, dass einzelne Deployments oder ein zusätzliches Skript die Performance schleichend verschlechtern, ohne dass es jemand bemerkt.
Inhaltsverzeichnis
- 1. Was ein Performance-Budget wirklich ist
- 2. Baseline messen statt Zahlen raten
- 3. Size-Budgets definieren: JS, CSS und Bilder
- 4. Timing-Budgets definieren: LCP, TTI und Co.
- 5. Quantity-Budgets: Requests, Fonts, Third-Party-Skripte
- 6. Budgets in CI durchsetzen: Build failed bei Regression
- 7. Tooling: Lighthouse CI und webpack Performance Hints
- 8. bundlesize und size-limit im npm-Workflow
- 9. Kommunikation an Stakeholder: Budgets schützen
- 10. Zusammenfassung
- 11. FAQ
1. Was ein Performance-Budget wirklich ist
Ein Performance-Budget ist eine verbindliche Obergrenze für einen messbaren technischen Wert, keine vage Zielvorgabe wie "der Shop soll schnell sein". Es gibt drei Kategorien: Size-Budgets begrenzen die Dateigröße von JavaScript, CSS und Bildern in Kilobyte, Timing-Budgets setzen Obergrenzen für Metriken wie LCP oder TTI in Millisekunden, und Quantity-Budgets beschränken die Anzahl von Requests, Fonts und Third-Party-Skripten pro Seite.
Der entscheidende Unterschied zu einer reinen Empfehlung: Ein Budget wird maschinell geprüft und blockiert im Idealfall den Build, sobald es überschritten wird. Ohne diese Durchsetzung bleibt ein Performance-Budget eine gute Absicht, die beim nächsten Termindruck stillschweigend ignoriert wird. Budgets machen Performance zu einer nicht verhandelbaren Qualitätsanforderung, genau wie ein fehlschlagender Unit-Test im gleichen Build.
2. Baseline messen statt Zahlen raten
Ein Performance-Budget, das nicht auf einer echten Baseline-Messung beruht, ist reine Willkür. Der richtige Startpunkt ist eine Messreihe über mehrere Tage mit Lighthouse CI oder WebPageTest auf den wichtigsten Seitentypen: Startseite, Kategorie, Produktdetail und Checkout. Aus Median und 75. Perzentil dieser Messungen entsteht ein realistisches Bild dessen, was der Shop aktuell liefert, statt eines Wunschwerts aus einem Blogartikel.
Das Budget selbst setzt man dann leicht über dem aktuellen Bestwert an, nicht am theoretischen Optimum. Ein Shop mit 2,8 Sekunden LCP bekommt zunächst ein Budget von 2,9 Sekunden, nicht 1,5 Sekunden. So bleibt der Build grün, während gezielt optimiert wird, und das Budget sinkt schrittweise mit jeder erfolgreichen Verbesserung, statt von Anfang an unerreichbar zu sein.
#!/usr/bin/env bash
# capture-baseline.sh - measure current performance to derive realistic budgets
set -euo pipefail
readonly URLS=(
"https://shop.example.com/"
"https://shop.example.com/damen/kleider.html"
"https://shop.example.com/produkt-slug.html"
)
readonly RUNS=5
readonly OUT_DIR="baseline/$(date +%Y%m%d)"
mkdir -p "$OUT_DIR"
for url in "${URLS[@]}"; do
slug=$(echo "$url" | sed 's#[^a-zA-Z0-9]#_#g')
echo "[INFO] Measuring $url ($RUNS runs)"
npx lighthouse "$url" \
--preset=desktop \
--output=json \
--output-path="$OUT_DIR/${slug}.json" \
--chrome-flags="--headless" \
--throttling-method=simulate \
--quiet
done
# Aggregate median + p75 per metric from the JSON reports
node scripts/aggregate-baseline.js "$OUT_DIR" > "$OUT_DIR/baseline-summary.json"
echo "[OK] Baseline written to $OUT_DIR/baseline-summary.json"
3. Size-Budgets definieren: JS, CSS und Bilder
Size-Budgets begrenzen die komprimierte Dateigröße pro Ressourcentyp, meist getrennt nach JavaScript, CSS und Bildern. Entscheidend ist, mit welcher Kompression gemessen wird: Ein Budget auf Basis der unkomprimierten Dateigröße ist irrelevant, weil der Browser die gzip- oder brotli-komprimierte Version über das Netzwerk lädt. Ein realistisches Budget für ein Hyvä-Shop-Bundle liegt oft bei 150 bis 200 KB komprimiertem JavaScript für den kritischen Entry-Point.
Sinnvoll ist außerdem, zwischen Entry-Point-Budget und Gesamt-Budget zu unterscheiden: Das Entry-Point-Budget begrenzt, was für den ersten Render geladen werden muss, das Gesamt-Budget begrenzt alles, was eine Seite im Laufe der Interaktion nachlädt. Für Bilder empfiehlt sich ein Budget pro einzelnem Bild, etwa 200 KB für ein Hero-Bild, statt eines Gesamtbudgets über alle Bilder einer Seite, weil einzelne überdimensionierte Dateien sonst im Durchschnitt untergehen.
4. Timing-Budgets definieren: LCP, TTI und Co.
Timing-Budgets setzen Obergrenzen für Nutzer-wahrgenommene Metriken wie LCP, TTI und Total Blocking Time. Anders als Size-Budgets lassen sich Timing-Werte nicht direkt aus dem Code ablesen, sondern nur durch eine simulierte oder reale Messung ermitteln. Damit die Werte in CI reproduzierbar bleiben, muss die Messung unter fester Netzwerk- und CPU-Drosselung laufen, etwa mit dem Slow 4G-Profil und vierfacher CPU-Verlangsamung, wie es Lighthouse standardmäßig simuliert.
Ein Timing-Budget sollte sich an den bekannten Schwellenwerten der Core Web Vitals orientieren, aber immer an die eigene Baseline angepasst werden. Ein Wert von 2,5 Sekunden für LCP ist als generelle Zielmarke sinnvoll, aber ein Shop, der aktuell bei 4 Sekunden liegt, braucht Zwischenziele, sonst schlägt jeder Build sofort fehl und das Budget wird ignoriert oder ganz entfernt.
5. Quantity-Budgets: Requests, Fonts, Third-Party-Skripte
Quantity-Budgets begrenzen nicht die Größe, sondern die Anzahl einzelner Ressourcen, weil jeder zusätzliche Request eigene Latenz-Kosten durch DNS-Lookup, TLS-Handshake und Verbindungsaufbau verursacht. Ein typisches Budget begrenzt die Gesamtzahl der Requests auf einer Produktseite auf etwa 60 bis 80, die Anzahl geladener Web-Fonts auf maximal zwei Font-Familien mit je zwei Schnitten, und die Anzahl aktiver Third-Party-Skripte auf eine feste Zahl, oft nicht mehr als fünf.
Gerade Third-Party-Skripte sind der häufigste Grund für schleichende Regression, weil sie einzeln jeweils klein wirken, sich aber addieren: ein Tracking-Pixel hier, ein Chat-Widget dort, ein zusätzliches A/B-Testing-Tool. Ein Quantity-Budget fängt genau dieses Muster von vielen kleinen Ergänzungen ab, das ein reines Size-Budget übersehen kann, wenn jedes einzelne Skript für sich unter dem Schwellenwert bleibt.
6. Budgets in CI durchsetzen: Build failed bei Regression
Ein Performance-Budget entfaltet erst Wirkung, wenn es den Build fehlschlagen lässt, sobald ein Schwellenwert überschritten wird, genau wie ein fehlgeschlagener Test. Lighthouse CI liest dafür eine budget.json, in der resourceSizes, resourceCounts und timings pro Ressourcentyp definiert sind. Der Befehl lhci autorun lädt die Seite in der Pipeline, vergleicht die Ergebnisse gegen die Budgets und gibt einen Nicht-Null-Exit-Code zurück, sobald ein Wert außerhalb der Toleranz liegt.
Wichtig ist, das Budget als Gate vor dem Merge zu platzieren, nicht als nachträgliche Reporting-Stufe. Ein Job, der nur eine Warnung im Log hinterlässt, wird in der Praxis ignoriert, sobald der Termindruck steigt. Ein Job, der den Merge blockiert, zwingt zur bewussten Entscheidung: entweder die Regression beheben oder das Budget mit einer dokumentierten Begründung explizit anheben.
[
{
"path": "/*",
"resourceSizes": [
{ "resourceType": "script", "budget": 180 },
{ "resourceType": "stylesheet", "budget": 60 },
{ "resourceType": "image", "budget": 300 },
{ "resourceType": "font", "budget": 100 },
{ "resourceType": "total", "budget": 700 }
],
"resourceCounts": [
{ "resourceType": "script", "budget": 12 },
{ "resourceType": "third-party", "budget": 5 },
{ "resourceType": "font", "budget": 4 },
{ "resourceType": "total", "budget": 75 }
],
"timings": [
{ "metric": "interactive", "budget": 3500 },
{ "metric": "largest-contentful-paint", "budget": 2900 },
{ "metric": "total-blocking-time", "budget": 300 }
]
}
]
# .github/workflows/performance-budget.yml
name: Performance Budget
on:
pull_request:
branches: [main]
jobs:
lighthouse-budget:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build production assets
run: npm run build
- name: Run Lighthouse CI against budget.json
run: npx lhci autorun --config=./lighthouserc.json
env:
LHCI_BUILD_CONTEXT__CURRENT_HASH: ${{ github.sha }}
# lhci exits non-zero when a budget assertion fails,
# which fails this job and blocks the merge automatically
7. Tooling: Lighthouse CI und webpack Performance Hints
Neben Lighthouse CI bringt webpack ein eigenes, sofort einsatzbereites Budget-Feature mit: die Konfiguration performance im webpack.config.js. Mit performance.maxAssetSize und performance.maxEntrypointSize lassen sich Obergrenzen in Byte für einzelne Assets und für den gesamten Entry-Point setzen. Der Parameter performance.hints steuert, ob eine Überschreitung nur als Warnung im Build-Log erscheint oder mit 'error' den Build-Prozess tatsächlich abbricht.
Der Vorteil dieses Ansatzes: Das Budget wird direkt im Build-Tool geprüft, nicht erst nachgelagert in einer separaten CI-Stufe, wodurch Entwickler die Überschreitung schon lokal beim Bauen sehen, bevor der Code überhaupt gepusht wird. Der Nachteil: webpack prüft nur Bundle-Größen, keine Timing- oder Quantity-Metriken. Für ein vollständiges Bild müssen webpack Performance Hints und Lighthouse CI kombiniert, nicht ersetzt werden.
// webpack.config.js - fail the build on oversized bundles
module.exports = {
// ...
performance: {
hints: 'error', // 'warning' | 'error' | false
maxAssetSize: 180 * 1024, // 180 KB per individual asset
maxEntrypointSize: 250 * 1024, // 250 KB for the critical entry point
assetFilter: function (assetFilename) {
// Only enforce the budget for JS and CSS, not source maps or fonts
return /\.(js|css)$/.test(assetFilename) && !/\.map$/.test(assetFilename);
}
}
};
8. bundlesize und size-limit im npm-Workflow
Für Projekte ohne komplexe Lighthouse-Pipeline sind die npm-Pakete size-limit und bundlesize ein schnellerer Einstieg in durchgesetzte Size-Budgets. Beide lesen eine Konfiguration direkt aus package.json, in der pro Datei oder Glob-Pattern ein maximaler Wert in Kilobyte hinterlegt wird. Der Befehl npx size-limit in der CI-Pipeline vergleicht die gebauten Assets gegen diese Werte und beendet sich mit einem Fehlercode, sobald ein Limit überschritten wird.
size-limit hat gegenüber bundlesize den Vorteil, dass es zusätzlich die Ausführungszeit im Browser simulieren kann, nicht nur die reine Dateigröße, was es näher an ein echtes Timing-Budget heranbringt. Für ein Hyvä-Theme lohnt sich ein Eintrag pro kritischem Bundle, etwa dem Alpine.js-Bundle und dem Tailwind-CSS-Output, damit eine versehentlich importierte schwere Library sofort auffällt, statt erst im nächsten Lighthouse-Report Wochen später.
{
"name": "hyva-theme-assets",
"scripts": {
"size": "size-limit",
"size:ci": "size-limit --json > size-limit-report.json"
},
"size-limit": [
{
"name": "Alpine.js bundle",
"path": "pub/static/frontend/**/js/alpine.min.js",
"limit": "45 KB",
"gzip": true
},
{
"name": "Tailwind CSS output",
"path": "pub/static/frontend/**/css/styles.css",
"limit": "60 KB",
"gzip": true
},
{
"name": "Total critical entry point",
"path": "pub/static/frontend/**/js/critical.min.js",
"limit": "180 KB",
"gzip": true
}
],
"devDependencies": {
"@size-limit/preset-app": "^11.0.0",
"size-limit": "^11.0.0"
}
}
9. Kommunikation an Stakeholder: Budgets schützen
Das robusteste CI-Gate nützt wenig, wenn ein Product Owner oder das Marketing-Team das Budget bei Bedarf einfach umgehen lässt, weil "nur ein weiteres Skript" dringend gebraucht wird. Der wirksamste Hebel gegen dieses Muster ist, das Performance-Budget nicht als technisches Detail zu kommunizieren, sondern als feste Kapazität, vergleichbar mit einem Gewichtslimit beim Transport: Jedes Kilobyte, das ein neues Skript hinzufügt, muss an anderer Stelle eingespart werden, es gibt keinen unbegrenzten Speicher.
In der Praxis funktioniert das über zwei Maßnahmen. Erstens werden KB und Millisekunden in Geschäftskennzahlen übersetzt, etwa "jede zusätzliche Sekunde LCP kostet laut unseren Daten X Prozent Conversion", statt abstrakte technische Werte zu zeigen. Zweitens braucht jede Ausnahme vom Budget eine dokumentierte, sichtbare Freigabe im selben Pull-Request-Prozess wie eine Sicherheitsausnahme, nicht eine stille Änderung der budget.json. So wird jede Überschreitung eine bewusste, nachvollziehbare Entscheidung statt eines schleichenden Kompromisses.
Der Unterschied zwischen einem dokumentierten und einem tatsächlich durchgesetzten Performance-Budget zeigt sich am deutlichsten im Ergebnis, wenn ein Team unter Zeitdruck steht.
| Aspekt | Ohne durchgesetztes Budget | Mit durchgesetztem Budget | Enforcement-Punkt |
|---|---|---|---|
| JS-Bundle-Größe | Wächst schrittweise unbemerkt | Bleibt innerhalb definierter KB-Grenze | webpack performance.maxAssetSize |
| LCP-Zeit | Verschlechtert sich über Monate | Bleibt stabil im Zielkorridor | Lighthouse CI budget.json (timings) |
| Anzahl Third-Party-Skripte | Steigt mit jeder Marketing-Anfrage | Erfordert Freigabe pro neuem Skript | Quantity-Budget + Code-Review |
| Regression-Erkennung | Erst im nächsten Audit sichtbar | Build schlägt sofort fehl | CI-Pipeline (lhci autorun) |
| Kommunikation mit Stakeholdern | Technische Diskussion nach dem Launch | Dokumentierte Freigabe vor dem Merge | Pull-Request-Prozess |
Mironsoft
Performance-Budgets, CI/CD-Integration und Hyvä-Optimierung für Magento-Shops
Performance-Budgets professionell einführen?
Wir messen die Baseline eures Magento-Shops, definieren realistische Size-, Timing- und Quantity-Budgets und integrieren sie als Gate in eure CI-Pipeline, damit jede Regression vor dem Merge auffällt, nicht erst im nächsten Lighthouse-Report.
Baseline-Audit
Mehrtägige Messreihe mit Lighthouse CI und WebPageTest als Grundlage für realistische Budgets
CI-Integration
budget.json, webpack Performance Hints und size-limit als Merge-Gate in eurer Pipeline
Stakeholder-Playbook
Kommunikationsvorlagen, damit Budgets nicht durch ein weiteres Skript unterlaufen werden
10. Zusammenfassung
Ein Performance-Budget löst ein wiederkehrendes Problem: Ohne verbindliche Obergrenze wachsen Bundle-Größe, Ladezeit und Anzahl der Requests schleichend, bis ein Shop spürbar langsamer geworden ist, ohne dass ein einzelner Commit dafür verantwortlich gemacht werden kann. Size-Budgets begrenzen JS, CSS und Bilder in Kilobyte, Timing-Budgets begrenzen wahrgenommene Metriken wie LCP und TTI, und Quantity-Budgets begrenzen die Anzahl von Requests, Fonts und Third-Party-Skripten. Alle drei Budget-Typen sollten aus einer echten Baseline-Messung abgeleitet werden, nicht aus willkürlichen Zielwerten.
Wirkung entfaltet ein Budget erst als durchgesetztes Gate in der CI-Pipeline: budget.json für Lighthouse CI, performance.maxAssetSize in webpack und size-limit im npm-Workflow lassen den Build fehlschlagen, sobald ein Schwellenwert überschritten wird. Genauso wichtig ist die Kommunikation an nicht-technische Stakeholder: Wer Performance-Budgets als feste Kapazität statt als technisches Detail vermittelt und jede Ausnahme dokumentiert freigeben lässt, verhindert, dass Budgets bei Termindruck stillschweigend umgangen werden.
Performance-Budgets definieren und durchsetzen - Das Wichtigste auf einen Blick
Size-Budgets
Komprimierte Größe pro Ressourcentyp begrenzen, getrennt nach JS, CSS und Bildern, mit separatem Entry-Point-Budget.
Timing- & Quantity-Budgets
LCP/TTI unter fester Drosselung messen, Requests und Third-Party-Skripte pro Seite zählen und begrenzen.
CI-Durchsetzung
budget.json, webpack performance.hints und size-limit lassen den Build bei Überschreitung fehlschlagen.
Baseline & Kommunikation
Budgets aus Median und 75. Perzentil echter Messungen ableiten, Ausnahmen dokumentiert freigeben lassen.