Trunk-Based Development: Warum kurzlebige Branches gewinnen
Wie Trunk-Based Development mit kurzlebigen Branches Merge-Konflikte reduziert, die Auslieferung beschleunigt und den main-Branch dauerhaft deploybar hält.

Langlebige Feature Branches fühlen sich sicher an. Man arbeitet isoliert, lässt sich Zeit, vermeidet es, etwas kaputt zu machen. Eine Woche später versucht man den Merge und verbringt einen halben Tag damit, Konflikte zu lösen. Der Branch hat sich währenddessen von main entfernt, und jetzt ist ausgerechnet der Merge der riskanteste Teil der ganzen Änderung.
Trunk-Based Development dreht dieses Prinzip um. Alle committen direkt auf den main-Branch (oder mergen kurzlebige Branches innerhalb von ein bis zwei Tagen). Der Integrationsschmerz verschwindet, weil es kaum Divergenz gibt. Der main-Branch ist immer deploybar, weil jeder Commit die CI durchläuft.
Die zwei Modelle
Es gibt zwei Varianten von Trunk-Based Development. Teams mit bis zu etwa einem Dutzend Personen können direkt auf den Trunk committen. Größere Teams arbeiten mit kurzlebigen Branches, die höchstens ein bis zwei Tage bestehen.
# ❌ Long-lived feature branches — the traditional model
main ─────────────────────────────────────────────
\ /
feature/auth-redesign ───────────── (2 weeks)
\ /
sub-branch ───── (1 week)
# 3 weeks of divergence, massive merge, manual conflict resolution# ✅ Trunk-based: short-lived branches merged within 1-2 days
main ──●──●──●──●──●──●──●──●──●──●──●──●──
│ │ │ │ │ │
└─●─┘ └──●──┘ └┘ └─●─┘ └──●──┘
# Each branch lives hours to 2 days max
# Small diffs, easy review, minimal conflictsDie entscheidende Kennzahl ist die Integrationshäufigkeit. Langlebige Branches werden einmal integriert, wenn die gesamte Arbeit erledigt ist. Branches im Trunk-Based-Ansatz werden fortlaufend integriert, in kleinen Schritten.
Unfertige Arbeit sicher machen
Der größte Einwand lautet: „Wie committe ich unfertige Features auf main, ohne etwas kaputt zu machen?" Die Antwort: Feature Flags.
// Feature flag implementation — can be as simple as environment config
interface FeatureFlags {
newCheckoutFlow: boolean;
betaDashboard: boolean;
experimentalSearch: boolean;
}
function getFeatureFlags(): FeatureFlags {
return {
newCheckoutFlow: process.env.FF_NEW_CHECKOUT === 'true',
betaDashboard: process.env.FF_BETA_DASHBOARD === 'true',
experimentalSearch: process.env.FF_EXPERIMENTAL_SEARCH === 'true',
};
}// ❌ Keeping half-finished UI in a branch for weeks
// Meanwhile, 5 other developers change the same components
// ✅ Merging incomplete work behind a flag — code is on main, hidden from users
function CheckoutPage() {
const flags = useFeatureFlags();
if (flags.newCheckoutFlow) {
return <NewCheckout />; // Work in progress, only visible internally
}
return <CurrentCheckout />; // Production users see this
}Der unfertige Code landet zwar in Produktion, wird für Nutzer aber nie ausgeführt. Entwickler bauen ihn weiter inkrementell aus und mergen täglich kleine Änderungen. Sobald das Feature fertig ist, wird einfach der Flag umgelegt.
Branch-Schutz und CI-Gates
Trunk-Based Development setzt eine solide CI-Pipeline voraus. Jeder Merge muss automatisierte Prüfungen bestehen, bevor er main erreicht.
# .github/workflows/ci.yml — required checks for trunk
name: CI
on:
pull_request:
branches: [main]
jobs:
quality-gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: npm ci
- name: Type check
run: npx tsc --noEmit
- name: Lint
run: npx eslint . --max-warnings 0
- name: Unit tests
run: npx jest --ci --coverage
- name: Integration tests
run: npx jest --ci --selectProjects integration
- name: Build
run: npm run build# Branch protection rules (configured in GitHub settings)
# Required for main branch:
# ✓ Require pull request reviews (1 reviewer)
# ✓ Require status checks to pass (quality-gate)
# ✓ Require branches to be up to date before merging
# ✓ Restrict who can push directly to mainMit diesen Gates kann kaputter Code main gar nicht erst erreichen. Der main-Branch bleibt dadurch dauerhaft in einem deploybaren Zustand.
Kleine Pull Requests als Standard
Trunk-Based Development bedeutet nicht, große Changesets schneller zu mergen. Es bedeutet, die Arbeit in kleinere Changesets aufzuteilen, die jeweils für sich genommen Sinn ergeben.
# ❌ One PR that does everything (800 lines changed)
feat: implement user settings page
- Add settings API endpoints
- Create settings form components
- Add form validation
- Update navigation
- Add settings E2E tests
- Update user model with preferences
# ✅ Multiple small PRs (each under 200 lines)
# PR 1: Add preferences field to user model + migration
# PR 2: Add GET/PUT /api/settings endpoints
# PR 3: Create SettingsForm component (behind feature flag)
# PR 4: Wire form to API, add validation
# PR 5: Add navigation link (flag-gated)
# PR 6: Enable feature flag, remove old code# Stacked PRs workflow — each builds on the previous
git checkout main && git pull
git checkout -b settings/model # PR 1: schema change
# ... commit, push, open PR
git checkout -b settings/api # PR 2: built on model changes
# ... commit, push, open PR (base: settings/model)
git checkout -b settings/ui # PR 3: built on API changes
# ... commit, push, open PR (base: settings/api)Kleine PRs werden schneller reviewt. Ein Diff mit 100 Zeilen bekommt innerhalb von Stunden fundiertes Feedback. Ein Diff mit 800 Zeilen liegt tagelang in der Warteschlange und wird am Ende nur noch mit einem routinemäßigen „LGTM" abgenickt.
Merge-Konflikten proaktiv begegnen
Kurzlebige Branches reduzieren Konflikte, beseitigen sie aber nicht vollständig. Wenn zwei Entwickler dieselbe Datei anfassen, hilft es, main häufig zu pullen, um Konflikte frühzeitig zu erkennen.
# Pull main into your branch at least once a day
git checkout my-branch
git fetch origin
git rebase origin/main
# If conflicts occur, they're small — just today's divergence
# Fix conflicts, then continue
git add .
git rebase --continue# ❌ Two-week branch: 15 files conflicted, 3 hours to resolve
# ✅ One-day branch: 1 file conflicted, 5 minutes to resolveEin Rebase statt eines Merge hält die Historie linear. Der main-Branch liest sich dann als Abfolge in sich geschlossener Änderungen, nicht als Spaghetti-Graph aus Merges.
Den Erfolg von Trunk-Based Development messen
Verfolge diese Kennzahlen, um zu überprüfen, ob Trunk-Based Development tatsächlich funktioniert:
// Metrics that indicate healthy trunk-based development
interface TrunkMetrics {
// How long a branch exists before merging
branchLifespanHours: number; // Target: < 48 hours
// Lines changed per PR
prSize: number; // Target: < 200 lines
// Time from PR open to merge
reviewCycleHours: number; // Target: < 8 hours
// How often main is deployed
deployFrequency: string; // Target: daily or more
// Percentage of main commits that pass CI
greenBuildRate: number; // Target: > 98%
// Time to fix a broken main
mainRecoveryMinutes: number; // Target: < 30 minutes
}# Quick check: branches older than 2 days
git for-each-ref --sort=-committerdate refs/remotes/origin \
--format='%(committerdate:relative) %(refname:short)' \
| head -20
# If you see branches older than 2 days, investigate why
# Common causes: blocked on review, scope too large, unclear requirementsEin Team, das Trunk-Based Development gut umsetzt, hat einen flachen Graphen — main bewegt sich stetig vorwärts, mit kleinen, häufigen Commits. Ein Team, das es schlecht umsetzt, hat ebenfalls einen flachen Graphen, der aber immer wieder von großen Merges unterbrochen wird, die den Build brechen.
Die Übergangsstrategie
Teams, die an langlebige Branches gewöhnt sind, können nicht von heute auf morgen umsteigen. Der Übergang sollte schrittweise erfolgen:
- Woche 1-2: Setze eine maximale Branch-Lebensdauer von 5 Tagen durch. Teile bestehende lange Branches in kleinere Stücke auf.
- Woche 3-4: Reduziere das Maximum auf 3 Tage. Führe Feature Flags für unfertige Arbeit ein.
- Woche 5-6: Strebe Branches mit 1-2 Tagen Lebensdauer an. Verlange ein Rebase vor jedem Merge.
- Ab Woche 7: Miss die Kennzahlen. Passe die Review-SLAs an, um schnelle Merge-Zyklen zu unterstützen.
Die technische Infrastruktur (CI-Gates, Feature Flags, Branch-Schutz) sollte stehen, bevor der Übergang beginnt. Ohne automatisierte Quality Gates wird jeder Commit auf main zum Risiko.
Die wichtigsten Erkenntnisse
- Kurzlebige Branches (1-2 Tage) beseitigen die Merge-Hölle — kleine Diffs, kleine Konflikte, schnelle Lösung
- Feature Flags machen unfertige Arbeit auf main ungefährlich — der Code wird ausgeliefert, läuft aber erst nach Aktivierung
- CI-Gates schützen main — automatisierte Tests, Linting und Type-Checks laufen bei jedem PR
- Kleine PRs bekommen bessere Reviews — unter 200 Zeilen gibt es fundiertes Feedback, bei 800 Zeilen wird nur noch abgenickt
- Rebase main täglich — wer Konflikte früh erkennt, löst eine Datei statt fünfzehn
- Miss die Lebensdauer der Branches — überschreiten sie dauerhaft 48 Stunden, muss die Aufteilung der Arbeit verbessert werden


