Why this matters
In Analytics Engineering, the main branch is the source of truth for production data transformations and dashboards. A broken main branch means failed pipelines, stale dashboards, and confused stakeholders. Protecting it reduces incidents and speeds up reliable releases.
- Real task: Ensure dbt models only reach production after tests and review.
- Real task: Prevent force-pushes that rewrite commit history used by audit and lineage.
- Real task: Require domain experts to approve schema changes.
Concept explained simply
Branch protection is a set of rules that say: “No one can change main unless quality gates are passed.”
Mental model
Think of main as an airport runway. Protections are control tower checks: weather check (CI tests), clearance (code review), and block on the runway (no direct landings). Planes (feature branches) can approach, but only land after clearance.
Key terms
- Protected branch: A branch with rules that restrict pushing or merging.
- Status checks: Automated CI jobs (lint, tests) that must pass before merging.
- Required reviews: One or more approvals from reviewers or code owners.
- Force-push: Rewriting history; dangerous on main and usually blocked.
Core protections to apply
- Require pull request before merging
- Require at least 1–2 approvals (ideally from code owners for critical paths)
- Require status checks to pass (linting, unit/data tests, build)
- Disallow force-pushes
- Restrict who can push (ideally no one; merges only via PR)
- Require linear history (squash or rebase merges)
- Dismiss stale approvals if new commits are pushed to the PR
- Require signed commits (where supported) for auditability
Recommended default for analytics repos
- 2 approvals
- Code owner review required for models, macros, and seeds
- Checks: SQL lint, unit tests, data tests, docs build
- Squash merges only; no direct pushes; no force-push
Worked examples
Example 1: Basic production safety
Goal: Prevent broken SQL from reaching production.
- Create a rule for branch “main”.
- Require pull requests with 1 approval.
- Add required status checks: Lint + unit/data tests.
- Disable force-push and restrict pushes to release bot only (if used) or no one.
Outcome: Any change must be reviewed and pass tests before merge.
Example 2: Critical model ownership
Goal: Ensure Finance approves changes to revenue models.
- Define code owners for /models/finance/.
- Turn on “Require review from Code Owners”.
- Keep 2 approvals total; at least one from Finance.
Outcome: Domain experts gate production-critical changes.
Example 3: Hotfix without breaking policy
Goal: Fix a production bug quickly, without bypassing protection.
- Create branch hotfix/fix_nulls.
- Push fix, open PR, tag code owners.
- Run the same status checks; use squash merge once green.
Outcome: Fast, safe restore without disabling protections.
Step-by-step: Define your branch protection policy
- Decide approvals: 1 for small teams, 2 for mature teams.
- Pick status checks: at minimum lint + unit/data tests; add docs build if used.
- Choose merge strategy: squash or rebase to keep linear history.
- Set ownership: Code owners for critical directories.
- Lock down pushes: disallow direct push; block force-push.
- Harden review: dismiss stale approvals on new commits; require up-to-date branch before merge.
- Auditability: require signed commits if supported.
Minimal viable policy
- 1 approval
- Lint + tests must pass
- No direct push, no force-push
- Squash merges
Stronger policy
- 2 approvals, code owner required
- Lint + unit/data tests + docs
- Signed commits
- Dismiss stale approvals
How to implement (platform-agnostic)
The exact clicks differ, but the flow is similar:
- Open repository settings for branches.
- Create a rule that targets main.
- Enable: require pull request, required reviews, required status checks.
- Enable: restrict who can push; disable force-push.
- Enable: require linear history; dismiss stale approvals; require up-to-date branch.
- Configure code owners and required checks list.
Typical settings to toggle
- Require a pull request before merging
- Require X approvals (1–2)
- Require review from Code Owners
- Require status checks to pass (select your CI jobs)
- Require branches to be up to date before merging
- Restrict who can push to matching branches
- Prevent force pushes and deletions
- Require linear history / squash merges only
- Require signed commits
Who this is for
- Analytics Engineers maintaining production transformations and metrics.
- Data/BI Engineers responsible for stable pipelines.
- Team leads setting repository governance.
Prerequisites
- Comfort with feature branches, commits, and pull requests.
- Basic CI understanding (what a status check is).
- Ability to read your team’s testing/linting outputs.
Learning path
- Refresh Git branching and PR workflows.
- List your mandatory checks (lint, tests, docs).
- Draft a protection policy (approvals, owners, merges).
- Apply policy to a test repo; iterate.
- Roll out to production repos and monitor.
Exercises
Do these hands-on tasks. Then take the Quick Test at the end of the page. Note: The quick test is available to everyone; sign in to save your progress.
- Exercise 1 — Draft your protection policy
Write the complete rule set for main for an analytics repo. Include approvals, code owners, status checks, push rules, merge rules, and stale approvals behavior. - Exercise 2 — Simulate a protected-branch flow
Create a feature branch, make a change, and list exactly which protections would block a merge until fixed. Describe how you’d resolve each block locally before merging.
Checklist before you say “done”
- PR required and direct push blocked
- Approvals count decided
- Code owners identified
- Status checks listed and mapped to CI jobs
- Merge strategy chosen (squash/rebase)
- Force-push disabled
- Stale approvals policy set
Common mistakes and self-check
- Mistake: Allowing direct pushes “just for small fixes.”
Self-check: Try to push to main; it should be rejected. - Mistake: No required checks, assuming reviewers will run tests.
Self-check: Open a PR with a failing lint; merge should be blocked. - Mistake: Merging without code owner review for critical models.
Self-check: Change a critical folder file; PR should request the owner. - Mistake: Allowing force-push to main for “cleanup.”
Self-check: Attempt force-push; it must be denied. - Mistake: Using merge commits that complicate history/bisect.
Self-check: Confirm squash merges are enforced.
Practical projects
- Policy draft: Propose a main-branch policy for your team; include rationale for each rule.
- CI mapping: Map each required check to a CI job and create a minimal green pipeline.
- Ownership: Create or refine a CODEOWNERS file for critical directories.
Mini challenge
Someone requests a temporary bypass to merge an urgent dashboard fix. Propose a response that maintains safety. Outline the steps to ship the fix within protections and the audit trail you’ll keep.
Next steps
- Roll protections out to staging and production repos.
- Track incidents and mean time to recovery; tune policy if merges are too slow or too risky.
- Educate the team: document the policy and add it to your PR template.
Appendix: Helpful snippets
Example CODEOWNERS
# Require Finance on finance models /models/finance/ @finance-owners # Require Platform on macros /macros/ @platform-core
Branch naming tips
feat/add_customer_ltv_model fix/hotfix_null_handling chore/upgrade_dbt_version
Quick Test
Take the Quick Test below. Anyone can take it; sign in to save your progress and track completion.