TL;DR: Ein Performance Budget ist eine Sammlung vereinbarter Schwellenwerte, die Ihre Anwendung nicht überschreiten darf – Bundle-Größe, Ladezeit, Bildgewicht, API-Latenz. Ohne ein Budget verschlechtert sich die Performance mit jeder hinzugefügten Funktion schleichend. Dieser Leitfaden zeigt, wie Sie Performance-Budgets definieren, messen und in der CI/CD-Pipeline durchsetzen.
Warum Performance-Budgets wichtig sind
Die Performance verschlechtert sich im Verborgenen. Jede Funktion fügt JavaScript, Bilder und API-Aufrufe hinzu. Für sich genommen, ist jede Ergänzung unmerklich. Kumulativ jedoch – über einen Zeitraum von 6–12 Monaten – wird eine Seite, die in 1,5 Sekunden lud, zu einer Seite, die 4 Sekunden braucht.
| Funktion | JS hinzugefügt | Latenz hinzugefügt | Kumulativ (6 Monate) |
|---|---|---|---|
| Analytics-Dashboard | 18 KB | +100ms | +100ms |
| Team-Kollaboration | 25 KB | +200ms | +300ms |
| Datei-Upload-Oberfläche | 12 KB | +80ms | +380ms |
| Datenexport | 8 KB | +50ms | +430ms |
| Chat-Integration | 35 KB | +300ms | +730ms |
Ohne ein Budget bleibt die kumulative Verschlechterung um 730 ms unbemerkt – bis Benutzer sich beschweren oder Ihr Core Web Vitals-Score sinkt und Ihre SEO-Rankings einbrechen.
Budgets definieren
Metrik-Kategorien
// performance-budget.config.ts
export const performanceBudget = {
// Mengen-Budgets – werden beim Build erfasst
javascript: {
totalInitial: 150 * 1024, // 150 KB (komprimiert)
routeLevel: 50 * 1024, // 50 KB pro Route-Chunk
},
css: {
totalInitial: 30 * 1024, // 30 KB
criticalPath: 15 * 1024, // 15 KB inline eingebettet
},
images: {
heroImage: 200 * 1024, // 200 KB
thumbnail: 50 * 1024, // 50 KB
totalPageWeight: 500 * 1024, // 500 KB gesamt
maxImageCount: 15, // Max. Bilder pro Seite
},
fonts: {
total: 40 * 1024, // 40 KB
maxCustomFonts: 2, // Max. Schriftfamilien
},
// Timing-Budgets – werden in CI und RUM erfasst
timing: {
lcp: 2500, // < 2,5s
cls: 0.1, // < 0,1
inp: 200, // < 200ms
ttfb: 800, // < 800ms
firstContentfulPaint: 1800, // < 1,8s
timeToInteractive: 3500, // < 3,5s
},
// Netzwerk-Budgets
network: {
totalRequests: 50, // Max. HTTP-Anfragen
thirdPartyScripts: 5, // Max. Drittanbieter-Skripte
blockingScripts: 0, // Keine render-blockierenden Skripte
},
}
CI/CD-Durchsetzung mit Lighthouse CI
Die Integration von Performance-Budgets in Ihre CI-Pipeline stellt sicher, dass kein Pull Request gemergt werden kann, ohne die definierten Schwellenwerte zu erfüllen. Der folgende GitHub Actions-Workflow führt Lighthouse CI und Bundle-Größenprüfungen bei jedem Pull Request aus:
# .github/workflows/performance.yml
name: Performance Budget
on:
pull_request:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npm run build
- run: npm run start:preview &
- name: Run Lighthouse CI
run: |
npm install -g @lhci/cli
lhci autorun --config=lighthouserc.json
- name: Check Bundle Size
run: |
npm run build -- --report
node scripts/check-bundle-size.mjs ./dist/report.html
- name: Check Image Budget
run: |
node scripts/check-image-budget.mjs ./dist/public
- name: Comment PR on Failure
if: failure()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Performance budget check failed. See details in the workflow run.'
})
Lighthouse CI-Konfiguration
Lighthouse CI-Assertions definieren, was für jede Performance-Metrik als Bestanden oder Nicht-bestanden gilt. Diese Konfiguration erzwingt Schwellenwerte für LCP, CLS, INP und weitere Core Web Vitals, mit unterschiedlichen Schweregraden für verschiedene Metriken:
{
"ci": {
"collect": {
"numberOfRuns": 3,
"staticDistDir": "./dist/public",
"settings": {
"throttlingMethod": "devtools",
"formFactor": "desktop"
}
},
"assert": {
"assertions": {
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
"interaction-to-next-paint": ["error", { "maxNumericValue": 200 }],
"total-blocking-time": ["error", { "maxNumericValue": 200 }],
"max-potential-fid": ["warn", { "maxNumericValue": 100 }],
"uses-responsive-images": "error",
"offscreen-images": "error",
"unminified-javascript": "error",
"unminified-css": "error",
"render-blocking-resources": ["warn", { "maxLength": 3 }]
}
},
"upload": {
"target": "temporary-public-storage"
}
}
}
Durchsetzung der Bundle-Größe
Die JavaScript-Bundle-Größe ist einer der wirkungsvollsten Hebel für die Performance-Kontrolle. Das folgende Skript prüft Ihre Build-Ausgabe gegen definierte Größen-Budgets und bricht den CI-Build ab, sobald ein Bundle seinen Grenzwert überschreitet:
// scripts/check-bundle-size.mjs
import { readFile } from "fs/promises"
import path from "path"
const BUDGET = {
"main": 150 * 1024, // Haupteinstiegspunkt: 150KB
"vendor": 50 * 1024, // Vendor-Chunk: 50KB
}
async function checkBundleSize() {
const source = path.join(process.cwd(), "dist", "report.html")
const content = await readFile(source, "utf-8")
const failures = []
for (const [key, max] of Object.entries(BUDGET)) {
const size = extractBundleSize(content, key)
if (size && size > max) {
failures.push(`${key}: ${formatSize(size)} > ${formatSize(max)}`)
}
}
if (failures.length > 0) {
console.error("Bundle size budget failed:")
failures.forEach((f) => console.error(` ${f}`))
process.exit(1)
}
console.log("Bundle size budget passed")
}
Automatisierte Warnungen bei Budgetüberschreitung
Über die CI-Durchsetzung hinaus hilft das Echtzeit-Monitoring von Produktions-Performance-Daten dabei, Regressionen zu erkennen, die nur unter realen Nutzerbedingungen auftreten. Diese Server-Funktion fragt RUM-Daten und Build-Artefakte ab, um Performance-Warnungen zu generieren:
// server/monitoring/performance-alerts.ts
export const checkPerformanceAlert = createServerFn({ method: "GET" }).handler(
async ({}, { context }) => {
const results = []
// Timing-Budgets aus RUM-Daten prüfen
const vitals = await context.env.DB.prepare(`
SELECT name, AVG(value) as avg, PERCENTILE(value, 95) as p95
FROM web_vitals
WHERE created_at > datetime('now', '-1 hour')
GROUP BY name
`).all()
const timingBudget = {
LCP: 2500,
CLS: 0.1,
INP: 200,
TTFB: 800,
}
for (const row of vitals.results) {
const threshold = timingBudget[row.name as keyof typeof timingBudget]
if (threshold && Number(row.p95) > threshold) {
results.push({
type: "performance_regression",
metric: row.name,
p95: row.p95,
threshold,
severity: "warning",
})
}
}
// Bundle-Größe aus Build-Artefakten prüfen
const bundleSize = await context.env.DB.prepare(`
SELECT route, SUM(size) as total
FROM bundle_sizes
WHERE version = (SELECT MAX(version) FROM bundle_sizes)
GROUP BY route
`).all()
const bundleBudget = 150 * 1024 // 150KB
const oversized = bundleSize.results.filter(
(r) => Number(r.total) > bundleBudget
)
if (oversized.length > 0) {
results.push({
type: "bundle_size_exceeded",
routes: oversized.map((r) => `${r.route}: ${formatSize(r.total)}`),
severity: "warning",
})
}
return results
}
)
Budget-Visualisierungs-Dashboard
Ein Echtzeit-Dashboard hält Ihr Team über den aktuellen Performance-Status auf dem Laufenden. Diese React-Komponente zeigt jede Metrik mit ihrem Budget-Schwellenwert und farbcodierten Statusindikatoren an:
// Dashboard component showing budget status
function PerformanceBudgetDashboard() {
const { data: budgets } = useQuery({
queryKey: ["performance-budget"],
queryFn: () => checkPerformanceAlert(),
refetchInterval: 60_000, // Alle 60 Sekunden aktualisieren
})
return (
<div className="grid grid-cols-3 gap-4">
{Object.entries(timingBudget).map(([metric, budget]) => (
<BudgetCard
key={metric}
metric={metric}
current={currentValues[metric]}
budget={budget}
/>
))}
</div>
)
}
function BudgetCard({ metric, current, budget }: BudgetCardProps) {
const ratio = current / budget
const status = ratio > 1 ? "failing" : ratio > 0.8 ? "warning" : "passing"
return (
<div className={`budget-card status-${status}`}>
<div className="metric-name">{metric}</div>
<div className="current-value">{formatMetric(metric, current)}</div>
<div className="budget-bar">
<div className="bar-fill" style={{ width: `${Math.min(ratio * 100, 100)}%` }} />
</div>
<div className="budget-limit">Limit: {formatMetric(metric, budget)}</div>
</div>
)
}
Budget-Pflege: Wann Anpassungen sinnvoll sind
| Signal | Maßnahme |
|---|---|
| Budget scheitert konstant um < 10 % | Budget um 5 % reduzieren, um der tatsächlichen Performance zu entsprechen |
| Budget scheitert konstant um > 20 % | Grundursache untersuchen – erst beheben, dann Budget anpassen |
| Budget besteht mit > 50 % Spielraum | Budget verschärfen – Sie können aggressiver werden |
| Neue Funktion erfordert Budget-Erhöhung | Budget erhöhen, aber ein Datum für Optimierung und erneute Reduktion setzen |
| Core Web Vitals-Ranking verschlechtert sich | Untersuchen und beheben – Budget nicht anpassen, um zu passen |
Team-Workflow
Entwickler öffnet PR
→ CI führt Bundle-Analyse + Lighthouse aus
→ Alle Budgets bestanden → Auto-Merge aktiviert
→ Budget scheitert → PR-Kommentar mit konkreten Fehlern
→ Entwickler prüft und optimiert
→ Oder: Regression bestätigt, Performance-Debt-Label hinzugefügt
→ Performance-Review am Freitag vor dem Deploy
Checkliste zur Implementierung von Performance-Budgets
- Bundle-Größen-Budgets für JS (initial), CSS und pro Route definiert
- Bild-Budgets definiert (Gesamtseitengewicht, Maximalanzahl)
- Timing-Budgets definiert (LCP, CLS, INP, TTFB)
- CI-Pipeline erzwingt Budgets bei jedem PR
- Bundle-Analyzer in die Build-Ausgabe integriert
- Lighthouse CI läuft bei jedem PR mit Assertion-Fehlern
- Performance-Budget-Dashboard in Ihr Monitoring-Tool eingebunden
- Automatisierte Warnungen bei Überschreitung von Produktions-Budgets
- Quartalsweise Budget-Überprüfung und Anpassung
- Performance-Regression-Label in GitHub für bestätigte Regressionen
Fazit
Ein Performance Budget geht es nicht um Perfektion – sondern darum, eine schleichende Verschlechterung zu verhindern. Indem Sie explizite Schwellenwerte definieren und diese in der CI durchsetzen, fangen Sie Performance-Regressionen ab, bevor sie in die Produktion gelangen.
Die zentrale Erkenntnis: Jede Funktion kostet Performance. Wenn Sie diesen Preis im Voraus einpreisen – während des Code-Reviews und nicht nach Nutzerbeschwerden – wird Performance vom nachträglichen Einfall zur Design-Nebenbedingung erster Klasse.
Der kleine Basis-Bundle von TanStack Start (~85 KB) bietet einen starken Ausgangspunkt, aber echte Performance erfordert kontinuierliche Disziplin. Für grundlegende Metriken und Optimierungstechniken empfehlen wir Ihnen unseren Leitfaden zur Core Web Vitals-Optimierung, den Leitfaden zum Real User Monitoring für Produktionsmessungen, die Tipps zur Lighthouse 95+-Optimierung für Best Practices bei Audits sowie die Edge Caching-Strategien für Performance-Gewinne auf CDN-Ebene.