05 — Continuous Integration
Teil C — Wie man ausliefert. Zurück zur Übersicht.
Lernziel
Nach diesem Kapitel kannst du erklären, was Continuous Integration von „wir haben einen Buildserver„ unterscheidet, kennst die vier Voraussetzungen und die Zehn-Minuten-Regel. Du hast eine lauffähige CI-Pipeline für den KFZ-Rechner gebaut.
5.1 Was Continuous Integration wirklich bedeutet
Der Begriff wird fast immer zu eng verstanden. Viele Teams sagen „wir machen CI“ und meinen: Es gibt einen Server, der nach einem Push Tests ausführt.
Das ist die Werkzeugseite. Der eigentliche Inhalt steckt im Wort Integration: Alle Entwickler führen ihre Arbeit fortlaufend — mindestens täglich — im Hauptzweig zusammen, und jede Zusammenführung wird automatisch überprüft.
Die Prüffrage lautet nicht „Habt ihr Jenkins?„, sondern:
Wann hat der letzte Entwickler seine Arbeit zuletzt in main integriert?
Lautet die Antwort „vor drei Wochen, er ist noch auf seinem Branch“, dann findet unabhängig von jeder installierten Software keine Continuous Integration statt. Es gibt dann lediglich einen Server, der jeden Zweig für sich prüft, während die Zweige immer weiter auseinanderlaufen.
5.2 Die vier Voraussetzungen
- Alles liegt in der Versionsverwaltung. Code, Tests, Pipeline-Definition, Infrastrukturbeschreibung, Datenbankmigrationen. Alles, was zum Bauen und Betreiben nötig ist — mit Ausnahme der Geheimnisse.
- Der Build läuft automatisch. Ein Befehl, keine Handgriffe, keine Anleitung mit dreißig Schritten.
- Die Tests laufen automatisch. Aus Kapitel 4 — und zwar bei jedem Commit, nicht nachts.
- Die Rückmeldung kommt schnell. Unter zehn Minuten. Sonst arbeitet niemand damit.
5.3 Die Zehn-Minuten-Regel
Warum ausgerechnet zehn Minuten? Weil das die Zeitspanne ist, in der ein Mensch bei der Sache bleibt.
| Dauer der Pipeline | Was der Entwickler tut |
|---|---|
| unter 2 min | wartet und schaut zu |
| 2–10 min | holt sich Kaffee, kommt zurück, arbeitet weiter |
| 10–30 min | fängt etwas Neues an — der Kontext ist weg |
| über 30 min | schaut abends nach, vielleicht |
| über 60 min | die Pipeline ist ein Ritual, kein Werkzeug |
Ab etwa zwanzig Minuten kippt das Verhalten: Man wartet die Rückmeldung nicht mehr ab, sondern arbeitet weiter — und stapelt Änderungen auf einer möglicherweise kaputten Grundlage. Die Pipeline erfüllt ihren Zweck dann nicht mehr.
Was hilft, wenn es zu lang dauert:
- Schritte parallelisieren, die voneinander unabhängig sind
- Abhängigkeiten und Container-Schichten zwischenspeichern
- Schnelle Prüfungen zuerst — Linting vor Unit-Tests vor Integrationstests
- Langsame E2E-Tests aus der Commit-Pipeline herausnehmen und nachgelagert ausführen
- bei Monorepos nur bauen, was sich geändert hat
5.4 Der Aufbau einer Commit-Pipeline
Push
│
▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Lint │─►│ Unit- │─►│ Build │─►│ Sicher- │
│ Format │ │ Tests │ │ Artefakt │ │ heit │
│ ~10 s │ │ ~30 s │ │ ~2 min │ │ ~1 min │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
▲ │
│ ▼
schnell scheitern Artefakt in
ist eine Tugend die Registry
Das Prinzip heißt fail fast: Die billigsten und schnellsten Prüfungen kommen zuerst. Es ergibt keinen Sinn, acht Minuten lang ein Container-Image zu bauen, wenn der Code einen Syntaxfehler enthält, den ein Linter in vier Sekunden findet.
5.5 Der gebrochene Hauptzweig
Aus der Fertigungsindustrie stammt das Bild der Reißleine am Fließband: Wer einen Fehler bemerkt, hält das gesamte Band an. Was zunächst absurd teuer klingt, ist billiger als die Alternative — nämlich hundert fehlerhafte Teile weiterlaufen zu lassen.
Übertragen auf die Softwareentwicklung:
Ein roter Build auf main hat Vorrang vor allem anderen.
Solange main rot ist, kann niemand verlässlich integrieren, niemand ausliefern und niemand wissen, ob der eigene Fehler neu ist oder von der Vorgängerin stammt. Jede weitere Zusammenführung baut auf unsicherem Grund auf.
Praktische Regeln, die sich bewährt haben:
- Wer den Build bricht, repariert ihn — sofort, nicht morgen.
- Gelingt die Reparatur nicht binnen etwa zehn Minuten, wird der auslösende Commit zurückgenommen. Das ist keine Kränkung, sondern Betriebshygiene.
- Niemand geht mit rotem
mainin den Feierabend.
Das ist übrigens der Punkt, an dem sich Kultur und Technik berühren: Die Regel funktioniert nur in einem Umfeld, in dem ein gebrochener Build als normaler Vorgang gilt und nicht als persönliches Versagen. Wo der Verursacher mit spitzen Bemerkungen rechnen muss, wird niemand mehr häufig integrieren — und damit ist die Continuous Integration erledigt, bevor sie begonnen hat.
5.6 Lab: CI-Pipeline für den KFZ-Rechner
Beide gängigen Systeme im Vergleich — nimm das, was bei dir verfügbar ist.
Variante GitLab CI
- .gitlab-ci.yml
stages: - pruefen - testen - bauen default: image: python:3.12-slim cache: key: files: - requirements.txt paths: - .venv/ .python_vorbereiten: &python_vorbereiten before_script: - python -m venv .venv - source .venv/bin/activate - pip install --quiet --upgrade pip - pip install --quiet -r requirements.txt # ---------- Stufe 1: schnelle Prüfungen ---------- lint: stage: pruefen <<: *python_vorbereiten script: - ruff check src/ tests/ - ruff format --check src/ tests/ # ---------- Stufe 2: Tests ---------- unit-tests: stage: testen <<: *python_vorbereiten script: - pytest --junitxml=report.xml --cov=src --cov-report=xml coverage: '/TOTAL.*\s+(\d+%)$/' artifacts: when: always reports: junit: report.xml coverage_report: coverage_format: cobertura path: coverage.xml expire_in: 1 week # ---------- Stufe 3: Artefakt ---------- container-bauen: stage: bauen image: quay.io/podman/stable before_script: - podman login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" script: - podman build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" . - podman push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" rules: - if: $CI_COMMIT_BRANCH == "main"
Variante GitHub Actions
- .github/workflows/ci.yml
name: CI on: push: branches: [main] pull_request: jobs: pruefen: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" cache: pip - name: Abhängigkeiten installieren run: pip install -r requirements.txt - name: Linting run: | ruff check src/ tests/ ruff format --check src/ tests/ - name: Tests run: pytest --cov=src --cov-report=term-missing bauen: needs: pruefen if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: An der Registry anmelden uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Image bauen und hochladen uses: docker/build-push-action@v5 with: push: true tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
Das zugehörige Containerfile
- Containerfile
FROM registry.access.redhat.com/ubi9/python-312:latest WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY src/ ./src/ USER 1001 EXPOSE 8080 CMD ["python", "-m", "src.server"]
Aufgaben
- Bring die Pipeline zum Laufen und beobachte die Gesamtdauer.
- Baue einen Formatierungsfehler ein. Welche Stufe schlägt fehl, nach wie vielen Sekunden?
- Baue einen fachlichen Fehler ein — etwa einen falschen Rabattfaktor. Welche Stufe fängt ihn?
- Miss die Laufzeit mit und ohne Zwischenspeicher der Abhängigkeiten.
- Aktiviere den Branch-Schutz auf
main, sodass nur zusammengeführt werden kann, wenn die Pipeline grün ist.
Der letzte Punkt ist der wichtigste. Eine Pipeline, deren Ergebnis man ignorieren kann, ist Dekoration. Erst der Branch-Schutz macht aus der Prüfung eine Zusage — und nimmt gleichzeitig dem einzelnen Entwickler die unangenehme Rolle, Kollegen auf rote Builds ansprechen zu müssen. Das erledigt jetzt die Maschine, und Maschinen nehmen einem das nicht übel.
5.7 Anti-Pattern
| Anti-Pattern | Folge | Abhilfe |
|---|---|---|
| Nächtlicher Build | Rückmeldung nach 14 Stunden | bei jedem Push bauen |
Roter main wird toleriert | niemand weiß, was funktioniert | Reißleine, Revert-Regel |
| Pipeline dauert 45 Minuten | wird nicht mehr abgewartet | parallelisieren, zwischenspeichern, aufteilen |
| Tests werden übersprungen, „ist eilig„ | genau dann geht es schief | keine Ausnahmen, technisch erzwingen |
| CI baut, aber niemand schaut hin | Fehler bleiben liegen | Benachrichtigung, Branch-Schutz |
| Pipeline nur auf Feature-Branches | Integration findet nie statt | main ist der Prüfstein |
| Pipeline im Web-Formular zusammengeklickt | nicht versioniert, nicht nachvollziehbar | Pipeline as Code |
5.8 Reflexionsfrage
Wie lange dauert bei euch der Weg vom Push bis zur Rückmeldung „alles in Ordnung“ — und wie oft wird diese Rückmeldung tatsächlich abgewartet?
