Performance Budget für SaaS: Ziele definieren und automatisch durchsetzen

Ein praxisorientierter Leitfaden zur Implementierung von Performance-Budgets für SaaS-Anwendungen – von der Metrikauswahl über Schwellenwerte bis hin zu CI/CD-Durchsetzung und Dashboard-Monitoring.

Alex Chen
Alex Chen
16. Juni 202610 min read
Auch verfügbar auf:English · 中文

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.

FunktionJS hinzugefügtLatenz hinzugefügtKumulativ (6 Monate)
Analytics-Dashboard18 KB+100ms+100ms
Team-Kollaboration25 KB+200ms+300ms
Datei-Upload-Oberfläche12 KB+80ms+380ms
Datenexport8 KB+50ms+430ms
Chat-Integration35 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

typescript
// 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:

yaml
# .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:

json
{
  "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:

typescript
// 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:

typescript
// 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:

typescript
// 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

SignalMaß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 % SpielraumBudget verschärfen – Sie können aggressiver werden
Neue Funktion erfordert Budget-ErhöhungBudget erhöhen, aber ein Datum für Optimierung und erneute Reduktion setzen
Core Web Vitals-Ranking verschlechtert sichUntersuchen 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.