Zum Inhalt springen

Performance-Budgets: Standards festlegen und durchsetzen

Wie Sie Performance-Budgets definieren, messen und durchsetzen, um zu verhindern, dass Ihre Webanwendung mit der Zeit unbemerkt langsamer wird.

3 Min. Lesezeit
Dashboard mit Web-Performance-Metriken und Budgetgrenzwerten

Jede Webanwendung startet schnell. Dann fügt jemand eine Carousel-Bibliothek hinzu. Dann ein Analytics-Skript. Dann einen Datepicker, der gleich das komplette Moment.js mitbringt. Sechs Monate später braucht die Landingpage 8 Sekunden zum Laden, und niemand kann auf einen einzelnen Commit zeigen, der das verursacht hat.

Performance-Budgets machen dieses schleichende Wachstum sichtbar und verhinderbar. Es sind harte Zahlen — maximale Bundle-Größe, maximaler Wert für Time to Interactive, maximale Anzahl an Requests —, die der Build-Prozess automatisch durchsetzt.

Was budgetiert werden sollte

Performance-Budgets lassen sich in drei Kategorien einteilen: Timing-Metriken, Größen-Metriken und Zähl-Metriken. Jede erkennt eine andere Art von Regression.

ymlyaml
# performance-budget.yml
timing:
  first-contentful-paint: 1800  # ms
  largest-contentful-paint: 2500  # ms
  time-to-interactive: 3500  # ms
  cumulative-layout-shift: 0.1  # unitless
 
size:
  total-bundle: 250  # KB (compressed)
  javascript: 170  # KB (compressed)
  css: 50  # KB (compressed)
  images-per-page: 500  # KB
 
count:
  requests: 50
  third-party-scripts: 3

Beginnen Sie mit den Metriken, die für Ihre Anwendung am wichtigsten sind. Ein E-Commerce-Shop priorisiert wahrscheinlich Largest Contentful Paint (Produktbilder). Ein Dashboard priorisiert eher Time to Interactive (komplexes JavaScript). Eine Content-Seite priorisiert eher First Contentful Paint (Textrendering).

Budget-Einhaltung messen

Lighthouse CI integriert Performance-Budget-Prüfungen direkt in Ihre CI-Pipeline. Es führt Lighthouse gegen Ihre deployte oder lokal servierte App aus und vergleicht die Ergebnisse mit den festgelegten Schwellenwerten.

jsjavascript
// lighthouserc.js
module.exports = {
  ci: {
    collect: {
      url: ['http://localhost:3000/', 'http://localhost:3000/dashboard'],
      numberOfRuns: 3,
    },
    assert: {
      assertions: {
        'first-contentful-paint': ['warn', { maxNumericValue: 1800 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
        'interactive': ['error', { maxNumericValue: 3500 }],
        'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
        'total-byte-weight': ['warn', { maxNumericValue: 500000 }],
      },
    },
    upload: {
      target: 'temporary-public-storage',
    },
  },
};

Die Unterscheidung zwischen warn und error ist wichtig. Warnings markieren Regressionen in den Pull-Request-Kommentaren. Errors blockieren den Merge. Nutzen Sie Warnings für ambitionierte Ziele und Errors für nicht verhandelbare Grenzwerte.

Bundle-Größe überwachen

Die Bundle-Größe ist die vorhersehbarste Performance-Metrik. Anders als Timing-Metriken (die je nach Gerät und Netzwerk schwanken) ist die Bundle-Größe deterministisch — gleicher Code, gleiche Größe, jedes Mal.

jsjavascript
// bundlesize.config.js — or use bundlewatch, size-limit
module.exports = [
  {
    path: 'dist/main.*.js',
    maxSize: '120 KB',
    compression: 'gzip',
  },
  {
    path: 'dist/vendor.*.js',
    maxSize: '80 KB',
    compression: 'gzip',
  },
  {
    path: 'dist/*.css',
    maxSize: '30 KB',
    compression: 'gzip',
  },
];

Tools wie size-limit gehen noch weiter — sie messen nicht nur die Dateigröße, sondern auch die tatsächliche Ausführungszeit und die Parse-Kosten:

shbash
# package.json
{
  "size-limit": [
    {
      "path": "dist/index.js",
      "limit": "15 KB",
      "running": true
    }
  ],
  "scripts": {
    "size": "size-limit",
    "size:check": "size-limit --why"
  }
}

size-limit --why auszuführen zeigt genau, welche Abhängigkeiten zu Ihrem Bundle beitragen — so findet man die 200-KB-Bibliothek, die jemand für eine einzige Hilfsfunktion importiert hat.

Webpack-Bundle-Analyse

Wenn ein Budget überschritten wird, müssen Sie verstehen, warum. Bundle-Analyzer visualisieren, was in Ihren Bundles steckt.

jsjavascript
// webpack.config.js
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
 
module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      reportFilename: 'bundle-report.html',
      openAnalyzer: false,
    }),
  ],
};

Typische Befunde einer Bundle-Analyse:

  • Doppelte Abhängigkeiten: zwei Versionen derselben Bibliothek (z. B. lodash 4.x und 3.x) durch transitive Abhängigkeiten
  • Ungenutzte Exports: eine ganze Bibliothek importieren, obwohl nur eine Funktion genutzt wird — Tree Shaking funktioniert nur mit ES-Modulen
  • Locale-Daten: Bibliotheken wie Moment.js oder date-fns liefern standardmäßig alle Locales mit
tstypescript
// ❌ Imports entire lodash — 70KB+ in your bundle
import _ from 'lodash';
const result = _.groupBy(items, 'category');
 
// ✅ Cherry-pick the function — ~1KB
import groupBy from 'lodash/groupBy';
const result = groupBy(items, 'category');

Performance-Budgets für Bilder

Bilder machen typischerweise 50–70 % des Seitengewichts aus. Ein Bild-Budget verhindert, dass das Muster „nur noch ein Hero-Bild mehr" die Ladezeiten ruiniert.

tstypescript
interface ImageBudget {
  maxSizeKB: number;
  maxDimensions: { width: number; height: number };
  requiredFormats: string[];
}
 
const imageBudgets: Record<string, ImageBudget> = {
  hero: {
    maxSizeKB: 150,
    maxDimensions: { width: 1920, height: 1080 },
    requiredFormats: ['webp', 'avif'],
  },
  thumbnail: {
    maxSizeKB: 30,
    maxDimensions: { width: 400, height: 300 },
    requiredFormats: ['webp'],
  },
  avatar: {
    maxSizeKB: 15,
    maxDimensions: { width: 200, height: 200 },
    requiredFormats: ['webp'],
  },
};

Setzen Sie Bild-Budgets zur Build-Zeit mit automatisierter Kompression durch. Weisen Sie Bilder, die das Budget überschreiten, zurück, bevor sie in Produktion gelangen.

Budgets in CI/CD durchsetzen

Das Budget funktioniert nur, wenn es nicht ignoriert werden kann. Integrieren Sie die Prüfungen in den Pull-Request-Workflow, damit Regressionen vor dem Merge sichtbar sind.

ymlyaml
# .github/workflows/performance.yml
name: Performance Budget
on: [pull_request]
 
jobs:
  budget-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build
 
      - name: Check bundle size
        run: npx size-limit
 
      - name: Lighthouse CI
        run: |
          npm install -g @lhci/cli
          lhci autorun

Wenn ein Budget reißt: Untersuchen Sie die Ursache, bevor Sie das Limit anheben. Die meisten Regressionen lassen sich günstiger beheben als durch eine Budgeterhöhung — Lazy Loading, Code Splitting oder das Ersetzen einer überdimensionierten Abhängigkeit.

Die wichtigsten Erkenntnisse

  1. Legen Sie Budgets frühzeitig fest — Performance zu erhalten ist einfacher, als sie wiederherzustellen
  2. Budgetieren Sie zuerst die Bundle-Größe — sie ist deterministisch und in CI messbar, ohne einen laufenden Server zu brauchen
  3. Nutzen Sie error für harte Grenzen und warn für Ziele — blockieren Sie Merges nur bei nicht verhandelbaren Schwellenwerten
  4. Analysieren Sie, bevor Sie Limits anheben — die meisten Regressionen lassen sich mit kleineren Eingriffen beheben als mit einer Budgeterhöhung
  5. Bild-Budgets haben den höchsten ROI — Bilder sind auf den meisten Seiten das größte Gewicht
  6. Automatisieren Sie die Durchsetzung in CI — ein Budget, das niemand prüft, ist ein Budget, das niemand einhält
Wilfredo Rujel

Wilfredo Rujel

Full-Stack-Softwareentwickler

Diesen Beitrag teilenX