Branch protection was a repository setting. Rulesets are a governance system, and the difference is targeting.
A repository rule protects one branch in one repository. An enterprise ruleset applies a rule to every repository matching a filter, across every organisation, and can be changed in one place. That is what makes fleet-wide policy possible — and what makes a mistake fleet-wide too.
The short answer
Section titled “The short answer”Rulesets exist at three levels: repository, organisation and enterprise. Each is defined once and targets a set of branches, tags or pushes.
They aggregate, they do not override. GitHub’s documentation states that “creating a new ruleset can make the rules targeting a branch or tag more restrictive, but never less restrictive.” An enterprise ruleset cannot be relaxed by an organisation or repository one.
Three enforcement statuses: Active, Evaluate and Disabled. Evaluate applies the ruleset without enforcing it, letting you observe what would have been blocked. It is generally available and it is the single most useful rollout tool available.
Targeting is the enterprise feature. Rulesets can select organisations and repositories by list, by name pattern, or by custom property filter.
The rule catalogue
Section titled “The rule catalogue”What a ruleset can actually require, grouped by what it governs.
Branch and tag rules:
- Restrict creations — only bypass users can create matching branches or tags
- Restrict updates — “only users with bypass permissions can push to branches or tags whose name matches the pattern”
- Restrict deletions — enabled by default
- Block force pushes — enabled by default
- Require linear history — no merge commits
- Require a pull request before merging
- Require status checks to pass before merging
- Require signed commits — “contributors and bots can only push commits that have been signed and verified”
- Require code scanning results — block merges on alerts above a chosen severity
- Require code quality results
- Require workflows to pass before merging — available at organisation and enterprise level
- Require merge queue and require deployments to succeed — repository level only
Metadata restrictions, which require an enterprise plan:
- Commit message format — including requiring a GitHub issue number, or matching a pattern
- Committer email format and author email format
- Branch name format and tag name format
Push rules:
- Restrict file paths — “Prevent commits that include changes in specified file paths from being pushed”
- Restrict file extensions
- Restrict file path length
- Restrict file size — “Prevent commits that exceed a specified file size limit from being pushed”
Push rules and the fork network
Section titled “Push rules and the fork network”A property worth understanding before relying on push rules.
Push rulesets apply to “the entire fork network for a repository.”
Which means a file-size or file-path restriction reaches forks, including forks in other organisations and forks owned by user accounts. For an internal repository that the whole enterprise can fork, that is a large surface.
This is the correct behaviour — a restriction that could be bypassed by forking would not be a restriction — but it does mean the blast radius of a push rule is larger than the repository it is attached to.
Push rules are the mechanism for preventing large files, and they are far better than detection after the fact. A push rejected at 25 MB never enters history; a 25 MB file discovered later is a rewriting project. See binary file strategy.
Targeting
Section titled “Targeting”The capability that makes enterprise rulesets different in kind from repository rules.
Enterprise rulesets target organisations — all of them, a selected list, a name pattern, or by organisation custom properties.
Then target repositories within them — all, or by a dynamic filter on repository custom properties, or by repository name pattern.
The custom property filter is the important one. A filter expression selects repositories by their metadata:
visibility:private props.team:infra -language:javaThis is what makes policy expressible as intent rather than as a list. “All production-tier repositories require signed commits” is a ruleset with a property filter, and a repository becomes subject to it the moment its tier property is set — with no ruleset change.
The alternative is a list, which is stale the day after you write it.
Which makes custom properties a prerequisite for good ruleset design, not an optional extra. Establish the metadata before writing targeted rulesets, or you will end up with name patterns pretending to be classification.
Targeting patterns that hold up
Section titled “Targeting patterns that hold up”The filter is where a ruleset succeeds or becomes unmaintainable. Some patterns work and some do not.
Target by what the repository is, not what it is called. props.tier:production survives renames, reorganisations and team changes. name:*-prod survives none of them and quietly stops matching when somebody names a repository differently.
Target by visibility for security floors. visibility:public is a durable, meaningful category — public repositories genuinely warrant different rules, and the property is maintained by GitHub rather than by you.
Combine positive and negative terms. A filter such as production-tier repositories excluding a language whose tooling cannot yet satisfy a rule is a legitimate, temporary exclusion — and it is visible in the ruleset rather than hidden in a bypass list.
Avoid targeting individual repositories from an enterprise ruleset. If one repository needs something different, that is a repository ruleset. An enterprise ruleset naming three repositories is a repository ruleset wearing the wrong hat.
Beware the unset property. A repository whose tier property has never been set does not match props.tier:production — and also does not match props.tier:development. It falls through every targeted ruleset and is governed only by the universal baseline. This is the most common silent gap in a property-targeted governance model, and the fix is to require the property at creation and to audit for unset values.
Test the filter before relying on it. Look at the list of repositories a ruleset matches, and compare it to the list you expected. The difference is always instructive, and it is usually the unset-property problem.
Layering
Section titled “Layering”The behaviour that determines how the three levels combine.
Rules aggregate. A branch subject to an enterprise ruleset requiring pull requests and a repository ruleset requiring signed commits must satisfy both.
More restrictive wins. GitHub’s wording — a new ruleset “can make the rules targeting a branch or tag more restrictive, but never less restrictive” — means there is no mechanism by which a lower-level ruleset relaxes a higher-level one.
Which has a direct design consequence: an enterprise ruleset should express the floor, not the target. Anything you put in it applies to every matched repository with no local escape.
Teams add above it. A team that wants two reviewers instead of one adds a repository ruleset. That composes correctly.
And the corollary: you cannot express “most repositories need this, but these three do not” as a single enterprise ruleset. You express it by targeting — the three are excluded by the property filter — or by bypass actors.
Bypass
Section titled “Bypass”Every ruleset has an escape hatch, and designing it is most of the work.
Bypass actors can include repository, organisation and enterprise owners, roles such as maintain and write, teams, enterprise teams, GitHub Apps, Dependabot and the Copilot coding agent.
Branch and tag bypass can be limited to pull requests only, meaning the actor can merge a pull request that violates the rule but cannot push directly. This is a meaningfully narrower grant and it is usually the right one.
Design bypass around real needs, and there are only a few:
- Automation that must push directly — a release bot creating a tag, a dependency updater
- Emergency response — a break-glass path when production is broken at 3 a.m.
- Migration — a one-off operation during a transition
Everything else is a signal that the rule is wrong. A bypass list that grows steadily is a rule that does not match how people work, and the correct fix is to change the rule.
Bypass use is auditable, and it should be reviewed. A break-glass path nobody reviews is not a break-glass path; it is a permission.
Prefer a named app or team to individuals. Individual bypass entries outlive the people in them, and they are the entries nobody removes at offboarding.
Evaluate mode as the rollout mechanism
Section titled “Evaluate mode as the rollout mechanism”The feature that makes enterprise-wide policy safe to introduce.
Evaluate applies the ruleset without enforcing it. GitHub describes it as not enforced, but allowing you to monitor which actions would or would not violate the rules.
Which converts the hardest question — “what will this break?” — from a guess into a measurement.
-
Write the ruleset with the intended rules and targeting. Set enforcement to Evaluate.
-
Wait. Long enough to cover a normal cycle of work — at minimum a week, ideally a full release cycle. A rule that looks harmless on Tuesday breaks the release process on Thursday.
-
Read the results. Which repositories, which teams, which actions would have been blocked.
-
Decide, per violation, whether the rule or the practice is wrong. Both answers are legitimate, and the ratio tells you whether the rule is ready.
-
Fix the practice where it is wrong, with the teams involved, before enforcing.
-
Narrow the targeting if a category of repository is genuinely different. This is where the property filter earns its place.
-
Switch to Active, and announce it before you do rather than after.
Skipping evaluate is the most common cause of a failed governance rollout. An enterprise ruleset switched straight to Active on a Monday morning produces a support queue, a rollback, and an organisation that has learned to resist the next attempt.
Choosing which rules to actually mandate
Section titled “Choosing which rules to actually mandate”The catalogue is long. Most enterprises should mandate a short list, and the reasoning for each is worth stating.
Block force pushes and restrict deletions on default branches. Both are on by default in a new ruleset, both prevent irreversible loss, and no team has a legitimate objection. Start here.
Restrict file size. Prevents a class of repository health problem permanently, at a threshold well below GitHub’s 100 MB hard limit. Uncontroversial once teams understand the alternative is a history rewrite.
Require a pull request before merging. Defensible almost everywhere, and it is the rule that most compliance frameworks are actually asking about when they ask about change control.
Require status checks — but be careful. The check names must exist in every targeted repository, and a required check that never runs blocks merges forever. This rule is where enterprise-wide targeting most often goes wrong.
Require signed commits only if you have solved signing for everybody first. It is a good control and a terrible surprise; every developer, every bot, and every automated commit needs a working signing path before this can be enforced. See signed commits.
Metadata restrictions — commit message format, branch naming — are cheap to enforce and generate constant low-grade friction. Mandate them only where there is a downstream consumer, such as release note generation that parses commit messages. A naming convention with no consumer is bureaucracy.
Require code scanning results where code scanning is actually configured and tuned. Enforcing a severity threshold against a scanner nobody has triaged produces a fleet-wide merge block on day one.
The pattern: mandate rules that prevent irreversible harm, and rules whose downstream consumer exists. Everything else belongs at the team level, where the team can decide whether the friction is worth it.
When a rule breaks something
Section titled “When a rule breaks something”Enforcement failures have a small number of shapes, and recognising them saves hours.
A required status check that does not run. The most common by far. The check is required by name; the repository has no workflow producing that name; the pull request can never merge. Symptom: “Expected — waiting for status to be reported”, forever.
A check name that changed. A workflow renamed its job, the required check name did not follow, and every pull request in that repository blocks. The ruleset is correct and the world moved.
A skipped job. Path filters skip the job that produces a required check. The pull request blocks even though nothing is wrong. This is the interaction between path-filtered CI and required checks, and it catches everybody once.
Automation with no bypass. A release bot that pushes a tag directly, blocked by restrict-creations. The fix is a scoped bypass for that app, not relaxing the rule.
Signed commits and a bot that does not sign. Bots commit through the API and their commits may or may not be signed depending on how they authenticate. Verify before enforcing.
A rule that only bites at release time. Everything passes for three weeks, then the quarterly release process hits restrict-updates on a release branch. This is why evaluate mode needs to run for a full cycle, not a week.
In every case, the diagnostic question is the same: which ruleset, at which level, produced this? The repository’s rules view shows the applicable rules and their source, and it is the first place to look rather than the last.
Designing the ruleset set
Section titled “Designing the ruleset set”Not one ruleset — a small designed set. Common shapes:
A universal baseline. Applied to every repository, containing only rules nobody argues with: block force pushes and restrict deletions on the default branch. These are already defaults for a reason.
A tier ruleset. Targeted by a production or criticality property. Requires pull requests, status checks, and possibly signed commits.
A hygiene push ruleset. File size limits and forbidden extensions, applied broadly. Cheap, uncontroversial, and it prevents a whole class of repository health problems.
A regulated-scope ruleset. Targeted by a compliance property. The stricter rules — required reviews from a specific team, code scanning thresholds, commit signing.
Keep the number small. Every ruleset is something a future administrator has to reason about when a push is rejected, and rejection messages do not always make it obvious which of five rulesets was responsible.
Name them for what they do, not for who asked for them. enterprise-baseline is findable; sec-req-2024-q3 is archaeology.
Managing rulesets as code
Section titled “Managing rulesets as code”At a handful of rulesets, the interface is fine. Past that, the interface is where drift lives.
Rulesets are available through the API, which means they can be defined in a repository, reviewed as pull requests, and applied by a workflow. That is the same argument as any other configuration: the value of version control is the history and the review, not the automation.
What you gain: a diff when a rule changes, a record of who approved it, the ability to answer “when did we start requiring this” without archaeology, and the ability to recreate the configuration if something is deleted.
What you must handle: drift. A ruleset changed in the interface and not in the repository is a divergence, and a workflow that overwrites it without warning will surprise somebody. Reconcile on a schedule and report differences rather than silently correcting them.
Keep the definitions readable. A ruleset expressed as a large JSON blob is technically version-controlled and practically unreviewable. Structure the files so a reviewer can see what changed in a rule, not just that a field moved.
Do not automate the enforcement transition. Moving a ruleset from Evaluate to Active should be a deliberate human act with an announcement attached, even when everything else is automated.
And keep the policy inventory in the same place. The ruleset definition says what; a comment or an adjacent document says why. In two years the why is what nobody will have.
See API automation for the mechanics and policy as code for the general pattern.
Rulesets versus the other mechanisms
Section titled “Rulesets versus the other mechanisms”Rulesets are one of several enforcement surfaces, and choosing the wrong one is a common design error.
| Requirement | Mechanism |
|---|---|
| What may be pushed to a branch | Ruleset |
| Whether a repository may be created, deleted, transferred | Repository policies (public preview) |
| Whether members may fork, or change visibility | Enterprise policy |
| Who may access a repository | Teams and permissions |
| Which actions may run in a workflow | Actions policies (public preview) |
| What a repository must contain at creation | Templates |
The distinction that matters most: rulesets govern changes to code. Lifecycle events — creating, deleting, transferring a repository — are not code changes, and rulesets do not cover them. That is what repository policies are for, and they are documented as being in public preview and subject to change.
Using the wrong mechanism produces a control that looks present and is not. A ruleset cannot stop somebody deleting a repository. An enterprise policy cannot require two reviewers. Check that the mechanism can express the requirement before writing it down as satisfied.
Common mistakes
Section titled “Common mistakes”Going straight to Active. Evaluate exists precisely for this.
Putting the target state in the enterprise ruleset instead of the floor. It applies everywhere with no escape.
Targeting by repository name pattern instead of custom property. Names encode history, not intent.
Writing rulesets before establishing metadata. The targeting has nothing to select on.
A bypass list that grows. The rule does not match how people work.
Individual bypass entries. They outlive the people and survive offboarding.
Forgetting that push rules cover the fork network. Larger scope than the repository.
Assuming every rule works at every level. Merge queue and deployment rules are repository-level only.
Too many rulesets. Nobody can explain why a push was rejected.
No announcement before enforcement. The rule may be right; the surprise is what people remember.
Measuring whether governance is working
Section titled “Measuring whether governance is working”Rulesets produce a rare thing in governance: measurable outcomes.
Coverage. What fraction of repositories are matched by the baseline ruleset, and what fraction fall through every targeted one. The second number is the interesting one, and it should trend to zero.
Evaluate-mode violations over time. For a ruleset you have not yet enforced, the violation count is a direct measure of readiness. A count that is falling means teams are adapting; a flat count means nobody has been told.
Bypass invocations. How often, by whom, for what. A rising trend on a specific rule means the rule is wrong. A bypass actor who invokes it weekly is doing routine work through an emergency path.
Rejected pushes. Push rules produce a clean signal — a file-size rule that rejects three pushes a week is preventing three repository health problems a week, and that is the number to quote when somebody asks whether the policy is worth it.
Unset custom properties. The gap metric. Every repository with no tier is a repository nobody has classified, and it is invisible to targeted policy.
Time to exception. How long it takes a team with a legitimate need to get one. If the answer is weeks, teams will find workarounds instead, and the governance model will be quietly hollow.
Report these to the people who set the policy, not only to the people subject to it. A governance programme that cannot show its own effect is one that gets removed in the next reorganisation.
Mental model
Section titled “Mental model”An enterprise ruleset states a floor that no organisation or repository can lower. Its power is targeting — selecting repositories by what they are rather than by name — and its risk is that everything in it applies everywhere immediately. Evaluate mode is how you find out what a rule means before it means it.
What you learned
Section titled “What you learned”- Rulesets exist at repository, organisation and enterprise level, and they aggregate rather than override
- A new ruleset can make rules more restrictive but never less restrictive
- Enterprise rulesets target organisations and then repositories, by list, name pattern or custom property filter
- The rule catalogue covers branch and tag rules, metadata restrictions requiring an enterprise plan, and push rules
- Merge queue and deployment rules are repository-level only; workflow rules are organisation and enterprise level
- Push rules apply to the entire fork network
- Bypass can be restricted to pull requests only, which is usually the right grant
- Evaluate mode applies a ruleset without enforcing it, and it is the correct first step for every enterprise rollout
- An enterprise ruleset should express the floor, because teams can add above it but never below
Exercise
Section titled “Exercise”Use an enterprise or organisation you administer.
-
List every ruleset currently defined at each level. Predict: did you know how many there were?
-
For each, identify what it targets and how — list, pattern or property.
-
Find one targeted by name pattern that would be better targeted by a property. Design the property.
-
Review every bypass entry. For each, state the operational need. Predict: how many are individuals who should not be there?
-
Write a new ruleset with one uncontroversial rule, targeted broadly, in Evaluate mode.
-
After a week, read what it would have blocked. Predict: were you right about the number?
-
For each violation, decide whether the rule or the practice is wrong.
-
Write the announcement you would send before switching it to Active.