Trunk-Based Development: Schneller liefern mit kurzen Branches
git
HEAD
Git · Trunk-Based Development · CI/CD · Feature Flags
Trunk-Based Development
Schneller liefern mit kurzen Branches

Trunk-Based Development ersetzt lange Feature-Branches durch kurzlebige Zweige, die mehrmals täglich in den Main-Branch zurückfließen. Feature Flags machen es möglich, unfertigen Code sicher auszuliefern, ohne die Anwendung zu gefährden, während eine schnelle, zuverlässige CI-Pipeline jeden Commit sofort auf Regressionen prüft und den Main-Branch dauerhaft auslieferbar hält.

14 Min. Lesezeit Kurzlebige Branches · Feature Flags · CI/CD Git · Git Flow · GitHub Flow

1. Was Trunk-Based Development wirklich löst

Die meisten Git-Workflows mit langlebigen Feature-Branches lösen ein Problem, das sie gleichzeitig verschärfen: Isolation. Ein Branch, der eine Woche oder länger parallel zum Main-Branch existiert, driftet unweigerlich auseinander - andere Entwickler committen weiter, Abhängigkeiten ändern sich, und der Merge am Ende wird zum Risiko statt zur Formsache. Merge Hell entsteht nicht durch schlechte Entwickler, sondern durch die Zeitspanne zwischen Branch-Erstellung und Integration. Je länger diese Spanne, desto größer der Konfliktraum.

Trunk-Based Development (TBD) dreht das Prinzip um: Statt Integration möglichst spät und selten durchzuführen, wird sie so früh und so häufig wie möglich erzwungen. Jeder Entwickler committet direkt oder über sehr kurze Branches mehrmals täglich in den main-Branch, den sogenannten Trunk. Das Ergebnis ist kein Zufallsprodukt, sondern eine bewusste Verschiebung des Risikos: kleine, häufige Integrationen statt seltener, großer. Für PHP- und Magento-Teams mit mehreren Entwicklern an einem Shop-Codebase ist das ein direkter Hebel gegen die klassischen Merge-Konflikte in Layout-XML, di.xml und Composer-Lockfiles.

2. Die Philosophie: sehr kurzlebige Branches

Der Kern von Trunk-Based Development ist eine einfache Regel: Ein Branch lebt Stunden, maximal ein bis zwei Tage - niemals Wochen. Diese Regel ist keine Willkür, sondern ergibt sich direkt aus der Mathematik der Integration: Ein Branch, der zwei Stunden nach der Erstellung gemerged wird, kann kaum relevant vom Trunk abweichen. Ein Branch, der zwei Wochen lebt, hat zwangsläufig hunderte fremde Commits verpasst. Entwickler zerlegen ihre Arbeit deshalb in kleinere, unabhängig lieferbare Einheiten, statt ein komplettes Feature in einem einzigen, riesigen Branch zu entwickeln.

Praktisch bedeutet das: git pull --rebase vor jedem neuen Arbeitsblock, ein neuer Branch pro Teilaufgabe, und ein Merge zurück in den Trunk, sobald die Änderung getestet und reviewt ist - nicht erst, wenn das gesamte Feature fertig ist. Diese Disziplin fühlt sich zunächst ungewohnt an, weil sie erzwingt, große Aufgaben in kleine, in sich geschlossene Schritte zu zerlegen. Genau diese Zerlegung ist aber der eigentliche Wert: Sie macht jeden einzelnen Schritt review- und testbar, statt am Ende ein tausend Zeilen großes Diff zu prüfen, das niemand mehr vollständig durchdringt.


#!/usr/bin/env bash
# Typical short-lived branch workflow for trunk-based development
set -euo pipefail

# Always start from an up-to-date trunk
git checkout main
git pull --rebase origin main

# Create a short-lived branch for one small, self-contained change
git checkout -b feature/checkout-shipping-label-fix

# ... make the change, commit in small logical steps ...
git add src/app/code/Mironsoft/Checkout/Model/ShippingLabel.php
git commit -m "Fix shipping label rendering for split shipments"

# Rebase onto the latest trunk before opening the pull request
git fetch origin
git rebase origin/main

# Push and open a small, fast-to-review pull request
git push -u origin feature/checkout-shipping-label-fix

# After CI is green and review is approved, merge quickly
git checkout main
git pull --rebase origin main
git merge --no-ff feature/checkout-shipping-label-fix
git push origin main

# Delete the branch immediately, it has done its job
git branch -d feature/checkout-shipping-label-fix
git push origin --delete feature/checkout-shipping-label-fix

3. Feature Flags als Enabler

Kurzlebige Branches allein lösen nur die halbe Aufgabe. Die eigentliche Herausforderung bleibt: Was passiert mit einem Feature, das nach zwei Tagen Arbeit noch nicht vollständig fertig ist, aber trotzdem in den Trunk soll? Genau hier setzt das Feature Flag an. Ein Feature Flag ist eine Bedingung im Code, die entscheidet, ob ein neuer Codepfad aktiv ist oder nicht - unabhängig vom Deployment. Unfertiger Code kann so gemerged, deployed und sogar in Produktion ausgeliefert werden, ohne dass ein einziger Nutzer ihn zu sehen bekommt.

Das verändert die Beziehung zwischen Deployment und Release fundamental: Deployment bedeutet, dass Code auf dem Server liegt. Release bedeutet, dass eine Funktion für Nutzer sichtbar oder aktiv ist. Diese beiden Ereignisse sind bei Trunk-Based Development bewusst entkoppelt. Ein Magento-Team kann eine neue Checkout-Funktion über mehrere Tage in kleinen Schritten in den Trunk integrieren, während sie hinter einem Flag inaktiv bleibt - und sie erst per Konfigurationsänderung freischalten, wenn sie vollständig fertig und getestet ist. Kein Deployment, kein Rollback-Risiko, nur ein Toggle.


<?php

declare(strict_types=1);

namespace Mironsoft\Checkout\Model;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;

/**
 * Simple, config-driven feature flag check for the new express checkout flow.
 */
class FeatureFlags
{
    private const XML_PATH_EXPRESS_CHECKOUT_ENABLED = 'mironsoft_checkout/features/express_checkout_enabled';

    /**
     * @param ScopeConfigInterface $scopeConfig Magento configuration reader
     */
    public function __construct(
        private readonly ScopeConfigInterface $scopeConfig
    ) {
    }

    /**
     * Checks whether the express checkout feature is enabled for the current store.
     *
     * @param int|null $storeId Store view id, null for the current store
     * @return bool True if the feature flag is active
     */
    public function isExpressCheckoutEnabled(?int $storeId = null): bool
    {
        return $this->scopeConfig->isSetFlag(
            self::XML_PATH_EXPRESS_CHECKOUT_ENABLED,
            ScopeInterface::SCOPE_STORE,
            $storeId
        );
    }
}

4. Feature-Flag-Patterns in der Praxis

Das einfachste Muster ist der binäre Konfigurations-Flag: ein einzelner Boolean in system.xml, ausgelesen über ScopeConfigInterface, wie im Beispiel oben. Das genügt für die meisten internen Features und erlaubt store-spezifisches Ein- und Ausschalten ohne Code-Änderung. Für riskantere Änderungen reicht ein einfacher An-aus-Schalter aber nicht aus - hier kommen gestaffelte Rollouts ins Spiel: Eine Funktion wird zunächst nur einem kleinen Prozentsatz der Kunden oder einem bestimmten Kundensegment angezeigt, bevor sie für alle aktiviert wird.

Ein praktikables Pattern für prozentbasierte Rollouts nutzt einen deterministischen Hash der Kunden- oder Session-ID, damit derselbe Nutzer bei jedem Aufruf konsistent dieselbe Variante sieht - kein zufälliges Hin-und-Her zwischen altem und neuem Verhalten. Wichtig ist außerdem, Flags nach erfolgreichem vollständigem Rollout konsequent aus dem Code zu entfernen: Ein Feature Flag ist ein temporäres Werkzeug, kein dauerhaftes Konfigurationssystem. Code-Basen mit dutzenden vergessenen Flags werden selbst zu einer Quelle von Komplexität und Testaufwand.


<?php

declare(strict_types=1);

namespace Mironsoft\Checkout\Service;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Store\Model\ScopeInterface;

/**
 * Feature flag service supporting boolean flags and percentage-based rollouts.
 */
class FeatureFlagService
{
    /**
     * @param ScopeConfigInterface $scopeConfig Magento configuration reader
     */
    public function __construct(
        private readonly ScopeConfigInterface $scopeConfig
    ) {
    }

    /**
     * Determines whether a feature is active for the given customer,
     * based on a deterministic percentage rollout.
     *
     * @param string $flagCode Config path segment identifying the flag
     * @param string $customerIdentifier Stable identifier, e.g. customer id or session id
     * @return bool True if the feature is active for this customer
     */
    public function isActiveForCustomer(string $flagCode, string $customerIdentifier): bool
    {
        $rolloutPercentage = (int) $this->scopeConfig->getValue(
            sprintf('mironsoft_features/%s/rollout_percentage', $flagCode),
            ScopeInterface::SCOPE_STORE
        );

        if ($rolloutPercentage <= 0) {
            return false;
        }

        if ($rolloutPercentage >= 100) {
            return true;
        }

        // Deterministic bucket: same customer always lands in the same bucket
        $bucket = crc32($flagCode . $customerIdentifier) % 100;

        return $bucket < $rolloutPercentage;
    }
}

5. Warum Trunk-Based Development starke CI braucht

Trunk-Based Development steht und fällt mit einer Voraussetzung, die selten deutlich genug betont wird: Der Trunk muss praktisch jederzeit deploybar sein. Das ist keine optionale Best Practice, sondern die tragende Säule des gesamten Modells - ohne sie kollabiert die Praxis der häufigen Integration sofort. Wenn mehrere Entwickler mehrmals täglich in denselben Branch mergen, muss jede einzelne Änderung automatisiert auf Regressionen geprüft werden, bevor sie den Trunk erreicht. Ein manueller Review allein reicht dafür nicht aus.

Das bedeutet konkret: eine Continuous-Integration-Pipeline, die bei jedem Push läuft, in wenigen Minuten durch ist und zuverlässig zwischen echten Fehlern und Flaky Tests unterscheidet. Eine CI-Suite, die 45 Minuten braucht, bremst nicht nur die Entwicklung aus, sie verleitet Teams dazu, Reviews und Merges zu stapeln - und genau damit zurück in Richtung langlebiger Branches zu rutschen. PHPUnit-Tests, statische Analyse mit PHPStan und ein automatisierter Build-Schritt gehören deshalb in jede Pipeline, die Trunk-Based Development ernsthaft unterstützen soll. Ein roter Build blockiert in einem funktionierenden Trunk-Based-Setup jeden weiteren Merge, bis er wieder grün ist.


# .github/workflows/trunk-ci.yml
# Fast, mandatory checks on every push to main and every pull request
name: Trunk CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  fast-checks:
    runs-on: ubuntu-latest
    timeout-minutes: 8
    steps:
      - uses: actions/checkout@v4

      - name: Set up PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          coverage: none

      - name: Install dependencies
        run: composer install --no-progress --prefer-dist

      - name: Static analysis (PHPStan level 5)
        run: vendor/bin/phpstan analyse app/code --level=5

      - name: Coding standards
        run: vendor/bin/phpcs --standard=Magento2 app/code

      - name: Unit tests
        run: vendor/bin/phpunit --testsuite unit --stop-on-failure

      - name: Fail fast on red build
        if: failure()
        run: echo "Build is red, merge to main is blocked until fixed" && exit 1

6. Testdisziplin: Pre-Commit-Hooks, Reviews, kleine PRs

CI allein genügt nicht, wenn fehlerhafter Code überhaupt erst den Weg zur Pipeline findet. Ein Pre-Commit-Hook, der lokal Linting, statische Analyse und die schnellsten Unit-Tests ausführt, fängt einen großen Teil der Fehler ab, bevor sie überhaupt gepusht werden. Das reduziert nicht nur die Last auf der CI-Infrastruktur, sondern verkürzt auch die Feedback-Schleife für den Entwickler von Minuten auf Sekunden - ein entscheidender Unterschied bei mehreren Merges pro Tag.

Genauso wichtig ist die Größe der Pull Requests selbst. Ein PR mit 50 geänderten Zeilen lässt sich in zehn Minuten gründlich reviewen; ein PR mit 2000 Zeilen wird entweder tagelang liegen gelassen oder oberflächlich durchgewunken - beides untergräbt die Grundidee von Trunk-Based Development. Teams, die konsequent auf kleine PRs setzen, kombinieren das häufig mit Pair Programming oder Mob Programming für die riskanteren Codeabschnitte: Wenn zwei Entwickler eine Änderung gemeinsam schreiben, ist der nachgelagerte Review-Aufwand oft minimal, weil der Review faktisch schon während des Schreibens stattgefunden hat.


#!/usr/bin/env bash
# .git/hooks/pre-commit - fast local gate before code reaches CI
set -euo pipefail

echo "Running fast pre-commit checks..."

# Only lint and analyse staged PHP files, not the whole codebase
staged_php_files=$(git diff --cached --name-only --diff-filter=ACM -- '*.php')

if [[ -z "$staged_php_files" ]]; then
  echo "No PHP files staged, skipping checks"
  exit 0
fi

echo "$staged_php_files" | xargs -r vendor/bin/phpcs --standard=Magento2

echo "$staged_php_files" | xargs -r php -l

vendor/bin/phpstan analyse $staged_php_files --level=5 --no-progress

echo "Pre-commit checks passed, proceeding with commit"

7. Trade-offs: wann Trunk-Based Development nicht passt

Trunk-Based Development ist kein universeller Ersatz für jeden Workflow. Teams ohne belastbare CI-Infrastruktur, ohne automatisierte Tests oder mit sehr wenig Erfahrung im gemeinsamen Umgang mit Git riskieren, dass ein instabiler Trunk zur Norm wird - genau das Gegenteil des angestrebten Ziels. Ein Team, das gerade erst lernt, saubere Commits zu schreiben und Merge-Konflikte zu lösen, profitiert oft mehr von etwas mehr Isolation, bis sich die grundlegenden Git-Fähigkeiten gefestigt haben.

Auch regulierte Branchen mit fest terminierten, auditierten Releases - etwa im Zahlungsverkehr oder Gesundheitswesen - stoßen an Grenzen: Wenn jede Produktionsänderung eine formale Freigabe durchlaufen muss, kollidiert die hohe Merge-Frequenz von Trunk-Based Development mit dem Freigabeprozess. In solchen Fällen bietet sich ein hybrider Ansatz an: Trunk-Based Development für die Entwicklung, kombiniert mit Release-Branches oder Feature Flags, die eine kontrollierte, terminierte Freischaltung erlauben, ohne auf lange Entwicklungs-Branches zurückzufallen. Der Fehler liegt selten im Prinzip selbst, sondern darin, es ohne die nötige Infrastruktur einzuführen.

8. Migration: Schritt für Schritt zu Trunk-Based Development

Der Umstieg gelingt selten über Nacht und sollte es auch nicht. Der erste Schritt ist fast immer die CI-Pipeline: Bevor Branches verkürzt werden, muss ein automatisierter, schneller Testlauf existieren, der bei jedem Push zuverlässig grün oder rot meldet. Ohne dieses Fundament produziert jeder weitere Schritt nur mehr Risiko. Parallel dazu lohnt sich ein Audit der bestehenden Testabdeckung: Wo fehlen automatisierte Tests für kritische Pfade wie Checkout oder Zahlungsabwicklung, und wo lässt sich mit vertretbarem Aufwand nachrüsten?

Danach folgt eine schrittweise Verkürzung der Branch-Lebensdauer: von zwei Wochen auf eine Woche, von einer Woche auf zwei Tage, bis Branches routinemäßig binnen 24 Stunden gemerged werden. Parallel dazu wird eine minimale Feature-Flag-Infrastruktur eingeführt - selbst ein simpler config-basierter Flag-Mechanismus wie im Beispiel oben reicht als Startpunkt. Entscheidend ist, das Team mitzunehmen: Pull-Request-Größe, Commit-Häufigkeit und Review-Geschwindigkeit sind Gewohnheiten, die sich über Wochen einspielen, nicht per Anweisung von einem Tag auf den anderen ändern.

9. Trunk-Based Development im direkten Vergleich

Für PHP- und Magento-Agenturen mit mehreren parallelen Projekten und wechselnden Teamgrößen lohnt sich der direkte Vergleich der gängigen Workflows. Keiner der drei Ansätze ist grundsätzlich überlegen - die Wahl hängt von CI-Reife, Release-Kadenz und Teamgröße ab.

Dimension Trunk-Based Development Git Flow GitHub Flow
Branch-Lebensdauer Stunden bis 1-2 Tage Wochen bis Monate Tage bis 1 Woche
CI-Voraussetzung Sehr hoch, zwingend Moderat ausreichend Hoch empfohlen
Release-Kadenz Mehrmals täglich möglich Feste Release-Zyklen Kontinuierlich, pro PR
Struktur-Komplexität Niedrig, ein Branch-Typ Hoch, viele Branch-Typen Niedrig bis moderat
Umgang mit unfertigem Code Feature Flags im Trunk Isolation im Feature-Branch Meist erst mergen, wenn fertig
Eignung für kleine, unerfahrene Teams Risikoreich ohne CI-Reife Gut geeignet Gut geeignet
Eignung für regulierte Releases Nur mit Release-Flags/Branches Sehr gut geeignet Bedingt geeignet

In der Praxis zeigt sich: Git Flow bleibt für Magento-Projekte mit festen, geplanten Release-Fenstern und mehreren parallel gepflegten Versionen sinnvoll. GitHub Flow ist ein solider Mittelweg für Teams mit guter, aber nicht perfekter CI-Reife. Trunk-Based Development entfaltet seinen vollen Vorteil erst, wenn CI-Pipeline, Testabdeckung und Feature-Flag-Infrastruktur bereits solide stehen - dann aber mit der höchsten erreichbaren Lieferfrequenz bei gleichzeitig geringerem Merge-Konflikt-Risiko.

Mironsoft

Git-Workflows, CI/CD-Pipelines und Feature-Flag-Architektur für Magento-Teams

Bereit für schnellere, sicherere Releases?

Wir analysieren euren Git-Workflow, bauen eine CI-Pipeline, die den Trunk zuverlässig grün hält, und führen eine Feature-Flag-Architektur ein, mit der euer Magento-Team unfertigen Code sicher ausliefern kann.

CI/CD-Setup

Schnelle, zuverlässige Pipelines mit PHPStan, PHPUnit und automatisiertem Deployment

Feature-Flag-Architektur

Config-basierte Flags und gestaffelte Rollouts für risikoarme Releases

Git-Workflow-Beratung

Migration von Git Flow zu kurzlebigen Branches, angepasst an Teamgröße und CI-Reife

10. Zusammenfassung

Trunk-Based Development löst das Kernproblem klassischer Branch-Modelle: Je länger ein Branch isoliert existiert, desto teurer wird die Integration. Kurzlebige Branches von Stunden bis maximal zwei Tagen halten den Konfliktraum klein. Feature Flags entkoppeln Deployment von Release und erlauben, unfertigen Code sicher im Trunk zu integrieren, ohne ihn für Nutzer sichtbar zu machen. Beides zusammen funktioniert aber nur mit einer schnellen, zuverlässigen CI-Pipeline, die den Trunk bei jedem Push auf Regressionen prüft, und mit Testdisziplin durch Pre-Commit-Hooks und kleine, schnell reviewbare Pull Requests.

Kein Team sollte Trunk-Based Development als reine Stilfrage einführen. Die Voraussetzungen, insbesondere eine belastbare CI-Infrastruktur und ausreichende Testabdeckung, entscheiden darüber, ob häufige Integration zu mehr Stabilität führt oder einen instabilen Trunk produziert. Für Magento-Agenturen mit mehreren Entwicklern an derselben Codebase ist der schrittweise Umstieg, beginnend bei der CI-Pipeline und endend bei einer schlanken Feature-Flag-Infrastruktur, der zuverlässigste Weg zu schnelleren, risikoärmeren Releases.

Trunk-Based Development - Das Wichtigste auf einen Blick

Kurzlebige Branches

Stunden bis maximal zwei Tage, mehrmals täglicher Merge in den Trunk statt seltener, großer Integrationen.

Feature Flags

Trennen Deployment von Release. Unfertiger Code liegt sicher im Trunk, bleibt aber für Nutzer inaktiv.

Starke CI-Pipeline

Schnelle, automatisierte Tests bei jedem Push. Ein roter Build blockiert jeden weiteren Merge.

Testdisziplin & kleine PRs

Pre-Commit-Hooks fangen Fehler lokal ab. Kleine Pull Requests bleiben schnell und gründlich reviewbar.

11. FAQ: Trunk-Based Development

1Was ist Trunk-Based Development?
Ein Git-Workflow mit sehr kurzlebigen Branches, die mehrmals täglich in den Main-Branch integriert werden, statt lange isolierte Feature-Branches zu pflegen.
2Wie lang sollte ein Branch maximal leben?
Wenige Stunden, maximal ein bis zwei Tage. Länger lebende Branches driften vom Trunk ab und erhöhen das Merge-Konflikt-Risiko.
3Was ist ein Feature Flag und wozu dient es?
Eine Code-Bedingung, die eine Funktion aktiviert oder deaktiviert. Entkoppelt Deployment von Release, unfertiger Code bleibt sicher inaktiv.
4Unterschied zu Git Flow?
Git Flow nutzt mehrere langlebige Branch-Typen mit festen Release-Zyklen. Trunk-Based Development reduziert das auf einen Trunk mit Feature Flags statt Isolation.
5Unterschied zu GitHub Flow?
GitHub Flow erlaubt Branches über mehrere Tage bis zur Feature-Fertigstellung. Trunk-Based Development verkürzt das weiter und setzt konsequent auf Feature Flags.
6Welche CI-Voraussetzungen sind nötig?
Eine schnelle, automatisierte Pipeline mit statischer Analyse und Unit-Tests, die bei jedem Push in wenigen Minuten zuverlässig läuft.
7Geeignet für kleine, unerfahrene Teams?
Nicht ohne Weiteres. Ohne belastbare CI-Infrastruktur profitieren solche Teams oft mehr von etwas mehr Isolation, etwa über GitHub Flow.
8Wie migriere ich mein Team dorthin?
Erst CI-Pipeline und Testabdeckung aufbauen, dann Branch-Lebensdauer schrittweise verkürzen und eine einfache Feature-Flag-Infrastruktur einführen.
9Wie handhabt man Releases?
Kontinuierlich direkt aus dem Trunk oder über kurzlebige Release-Branches. Feature Flags erlauben zusätzlich gezielte Freischaltung unabhängig vom Deployment.
10Was passiert mit Code Reviews?
Reviews werden kleiner und schneller. Viele Teams kombinieren das mit Pair Programming, sodass der Review bereits während des Schreibens stattfindet.