Follow trunk-based development practices with short-lived branches, frequent integration to main, feature flags, and continuous integration. Use when managing git workflow and releases.
You are assisting with code that follows trunk-based development practices for rapid, continuous delivery.
One Main Branch: All developers work on a single "trunk" branch (main/master) with short-lived feature branches that merge back quickly (within 1-2 days).
For small, low-risk changes:
# Make change
git add .
git commit -m "Add validation for grade range"
git pull --rebase
git push origin main
When to commit directly:
For larger changes, use branches but merge within 1-2 days:
# Create short-lived branch
git checkout -b add-quota-rule
# Work for < 2 days
git add .
git commit -m "Add quota capacity validation"
git push origin add-quota-rule
# Create PR, get quick review, merge
Rules for feature branches:
Merge to main multiple times per day:
# Update from main frequently
git checkout main
git pull
git checkout add-quota-rule
git rebase main
# Fix any conflicts
git push origin add-quota-rule --force-with-lease
Target frequency:
Good branch names:
add-minimum-grade-rule
fix-quota-overflow
refactor-competence-calculation
Bad branch names:
dev, development, feature # Too generic
johns-work # Not descriptive
v2-rewrite # Too large/long-lived
Branch lifecycle:
1. Create from main
2. Work < 2 days
3. Keep up-to-date with main (rebase daily)
4. Create PR when ready
5. Quick review + CI
6. Merge to main
7. Delete branch immediately
When a feature takes longer than 2 days, use feature flags:
# Feature flag pattern
from config import feature_flags
class AdmissionEvaluationService:
def evaluate(self, application):
if feature_flags.is_enabled('new_quota_algorithm'):
return self._evaluate_with_new_algorithm(application)
else:
return self._evaluate_with_old_algorithm(application)
Feature flag types:
Control when features go live:
# config/feature_flags.py
FEATURE_FLAGS = {
'new_quota_system': {
'enabled': False, # Not ready for production
'description': 'New quota allocation algorithm'
}
}
A/B testing or gradual rollouts:
def is_enabled_for_user(feature: str, user_id: str) -> bool:
"""Enable for percentage of users."""
if feature == 'new_ui':
# Enable for 10% of users
return hash(user_id) % 100 < 10
return False
Control access by role:
def can_access_feature(feature: str, user_role: str) -> bool:
if feature == 'admin_quota_override':
return user_role in ['admin', 'superuser']
return True
Introduce abstraction, implement new version behind it:
# Step 1: Create abstraction (merge to main)
class QuotaAllocator(Protocol):
def allocate(self, students: List[Student]) -> AllocationResult:
...
# Step 2: Make existing code use abstraction (merge to main)
class OldQuotaAllocator(QuotaAllocator):
def allocate(self, students):
# Existing logic
pass
# Step 3: Add new implementation (merge to main, behind flag)
class NewQuotaAllocator(QuotaAllocator):
def allocate(self, students):
# New logic
pass
# Step 4: Switch implementations via config (merge to main)
def get_allocator() -> QuotaAllocator:
if feature_flags.is_enabled('new_quota'):
return NewQuotaAllocator()
return OldQuotaAllocator()
# Step 5: Eventually remove old implementation
Deploy new code but don't expose it:
def evaluate_admission(application):
# Production code path
result = old_evaluation(application)
# Dark launch: run new code but don't use result
if feature_flags.is_enabled('test_new_evaluator'):
new_result = new_evaluation(application)
log_comparison(result, new_result) # Compare results
return result
Break large features into small, independently valuable pieces:
Large Feature: "New Quota System"
Break down:
ā Day 1: Add Quota entity (merge)
ā Day 2: Add capacity validation (merge)
ā Day 3: Add allocation algorithm (merge behind flag)
ā Day 4: Add UI for quota management (merge behind flag)
ā Day 5: Enable flag, deprecate old system
Commit small, logical units:
# Good: Small, focused commits
git commit -m "Add Grade value object"
git commit -m "Add grade validation rules"
git commit -m "Add tests for grade validation"
# Bad: Infrequent large commits
git commit -m "Implement entire grade system"
Follow conventional commits:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixrefactor: Code refactoringtest: Add/update testsdocs: Documentationchore: MaintenanceExamples:
feat(quota): Add capacity validation for quota entity
Implement validation to ensure quota capacity is never negative
and filled count never exceeds capacity.
Refs: #123
---
fix(grades): Handle missing grade in competence calculation
Previously crashed when student was missing a grade.
Now returns 0 points for missing grades with a warning.
Fixes: #456
---
refactor(admission): Extract rule evaluation to domain service
Move evaluation logic from use case to domain service to follow
Clean Architecture and DDD principles.
Each commit should:
Keep PRs small for faster reviews:
Good PR sizes:
How to keep PRs small:
Target review time: < 2 hours
Reviewer checklist:
Auto-approve criteria:
## What
Brief description of the change
## Why
Business reason or issue reference
## How
Technical approach
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests pass
- [ ] Manual testing done
## Checklist
- [ ] Follows coding standards
- [ ] Documentation updated
- [ ] No breaking changes (or flagged)
- [ ] Ready to merge
## Screenshots (if UI changes)
[Add screenshots]
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -e ".[dev]"
- name: Lint
run: |
ruff check .
mypy src/
- name: Test
run: |
pytest --cov=src --cov-report=xml
- name: Coverage check
run: |
coverage report --fail-under=80
Main branch deploys automatically to staging:
# .github/workflows/deploy-staging.yml
name: Deploy to Staging
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy to staging
run: ./scripts/deploy-staging.sh
Create release tags from main:
# When ready to release
git checkout main
git pull
git tag -a v1.2.0 -m "Release v1.2.0: Add quota management"
git push origin v1.2.0
Follow semver (MAJOR.MINOR.PATCH):
v1.0.0 ā Initial release
v1.1.0 ā Add new quota rules (feature)
v1.1.1 ā Fix quota calculation bug (patch)
v2.0.0 ā Change quota API (breaking change)
feature-branch (30 days old)
āāā 500 commits behind main
āāā Massive merge conflicts
add-quota-entity (1 day) ā merged
add-quota-validation (1 day) ā merged
add-quota-ui (1 day, behind flag) ā merged
Day 1: Start feature
Day 5: First merge from main
Day 10: Try to merge to main ā Conflicts!
Day 1: Merge from main, work, merge to main
Day 2: Merge from main, work, merge to main
PR: "Implement entire admission system"
Files changed: 50
Lines changed: 2000+
Review time: 3 days
PR: "Add Grade value object"
Files changed: 3
Lines changed: 120
Review time: 15 minutes
# Set up helpful aliases
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.sync '!git fetch origin && git rebase origin/main'
# Set up pull to rebase by default
git config --global pull.rebase true
In GitHub/GitLab, protect main:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- repo: https://github.com/astral-sh/ruff-pre-commit
hooks:
- id: ruff
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
hooks:
- id: mypy
# 1. Create short-lived branch
git checkout -b add-age-requirement-rule
# 2. Implement (TDD)
# - Write tests
# - Implement rule
# - Tests pass
# 3. Commit
git add .
git commit -m "feat(admission): Add age requirement rule
Add rule to validate student age meets minimum requirement
for specific programs.
Refs: #234"
# 4. Push and create PR
git push origin add-age-requirement-rule
# 5. Quick review ā merge ā delete branch
# Day 1: Domain model
git checkout -b quota-domain-model
# Implement Quota entity, value objects
git commit -m "feat(quota): Add Quota entity and value objects"
# PR ā merge ā delete branch
# Day 2: Validation
git checkout -b quota-validation
# Add capacity validation
git commit -m "feat(quota): Add capacity validation"
# PR ā merge ā delete branch
# Day 3: Allocation algorithm (incomplete)
git checkout -b quota-allocation
# Add new algorithm behind feature flag
git commit -m "feat(quota): Add new allocation algorithm (feature-flagged)"
# PR ā merge ā delete branch
# Day 4: UI (hidden)
git checkout -b quota-ui
# Add UI behind same feature flag
git commit -m "feat(quota): Add quota management UI (feature-flagged)"
# PR ā merge ā delete branch
# Day 5: Enable feature
git checkout -b enable-quota-feature
# Change feature flag to enabled
git commit -m "feat(quota): Enable new quota system"
# PR ā merge ā delete branch
When practicing trunk-based development: