Every organisation that turns on security scanning ends up with a backlog. Not because anyone was careless — because scanning applied to years of existing code finds years of existing problems, and it finds them all at once.
A backlog is a different problem from a queue, and treating it as a queue is why it never shrinks. A campaign is GitHub’s mechanism for treating it as what it is: a project, with a scope, an owner and a deadline.
The short answer
Section titled “The short answer”A campaign is a named, scoped, time-bound remediation effort over a filtered set of alerts.
- Created and managed from an organisation’s security settings
- Up to 10 active campaigns per organisation
- Up to 1,000 alerts per campaign, with alerts in recently-pushed repositories prioritised for inclusion
- Covers code scanning alerts on the default branch; secret scanning campaigns are also available and are currently in public preview
- Every campaign has a named point of contact — somebody developers can ask
- For code campaigns, alerts are automatically submitted to Copilot Autofix as capacity allows
- Alerts can be assigned to users with write access, or to the Copilot coding agent to generate pull requests
The problem campaigns solve
Section titled “The problem campaigns solve”Consider an organisation that enables code scanning across two hundred repositories and finds four thousand alerts.
Every reasonable response fails on its own:
“Fix them all.” Nobody has the capacity, and there is no order to work in.
“Fix the criticals.” Better, and it still leaves an undifferentiated list spread across two hundred repositories with no owner each.
“Each team fixes their own.” Reasonable, and it produces no coordination, no shared deadline, and no way to tell whether it is happening.
“Ignore it and gate new alerts.” The correct short-term move, covered in code scanning — and left alone, the backlog stays forever.
What is missing is the thing that makes any other large piece of work happen: a defined scope, a person responsible, a date, and visible progress. That is what a campaign supplies. The novelty is not technical; it is organisational, and that is precisely why it works.
Scoping a campaign
Section titled “Scoping a campaign”The selection is the most consequential decision, and the failure mode is choosing too much.
Campaign templates contain filters for common selections, including the requirement that Copilot Autofix supports the alert — which is a well-chosen filter, because it selects the alerts where a developer starts from a suggested fix rather than from nothing.
Custom filters let you define your own criteria: severity, rule, language, repository, age.
Selections that work:
- One vulnerability class across the organisation. Every SQL injection alert. Developers build up the same context repeatedly, the fix is the same shape each time, and reviewers get fast at it.
- Critical and high severity in production-facing repositories. Bounded by impact.
- Everything in one team’s repositories. Bounded by ownership, which makes the point of contact obvious.
- Alerts older than a year. Bounded by age, and a good way to clear accumulated cruft.
Selections that do not:
- Everything. The 1,000-alert limit prevents the worst version, and 1,000 is still not a project anybody completes.
- A random severity slice across every repository. No shared context, no obvious owner, no learning effect.
- Alerts nobody can fix. A campaign over a deprecated codebase is a campaign that fails and teaches people that campaigns fail.
Ownership and deadlines
Section titled “Ownership and deadlines”Two fields that look administrative and are the reason the mechanism works.
The point of contact is a named person developers can ask. Not a team address — a person. The alternative, which every organisation has tried, is a security initiative announced by an alias, where nobody knows who to ask and the answer is nobody.
The due date creates the ordinary pressure any dated work has. Without one, security remediation loses every prioritisation contest against dated feature work, forever, and no amount of severity labelling changes that.
Setting a realistic date matters more than setting an ambitious one. A campaign that visibly fails teaches people that campaigns are theatre; a campaign that completes teaches them that these get finished, which is the belief the second campaign depends on.
You can edit the name, description, due date and manager after creation, so a date that turns out to be wrong is adjustable rather than a reason to abandon the effort.
Developer experience
Section titled “Developer experience”The point of contact and the deadline are for the organisation. What determines whether the work happens is what the developer sees.
Notification. Developers are told about campaigns they can contribute to, rather than having to discover a filtered alert view. Code campaigns notify people with write access to the repository; secret scanning campaigns need only view access to the alerts list, which widens who can help.
Autofix already ran. For code campaigns, alerts are automatically submitted to Copilot Autofix as capacity allows, so a developer picking one up frequently starts from a suggested change rather than from an unfamiliar file. This is the single biggest reduction in per-alert cost, and it is why Autofix-supported filtering is a sensible template criterion.
Assignment. Alerts can be assigned to a person with write access, which makes ownership explicit at the individual level rather than the repository level. They can also be assigned to the Copilot coding agent to generate pull requests automatically.
Running one, end to end
Section titled “Running one, end to end”The mechanics are simple. The sequence around them is what determines whether it works.
-
Put the gate in place first. A ruleset requiring code scanning results at a chosen severity, so new alerts stop accumulating. Without this, the campaign is bailing without plugging the hole.
-
Look at the distribution before choosing scope. Group open alerts by rule. In most organisations a small number of rules account for most alerts, which makes the class-based scope obvious.
-
Agree the point of contact and the deadline with the people who will do the work, not for them. A date somebody else set is a date nobody owns.
-
Announce it before creating it. The notification tells developers there is a campaign; a human explanation tells them why it exists, what the deadline is, and who to ask. The tooling does the first; somebody has to do the second.
-
Create the campaign with the filter you chose. Confirm the alert count matches your estimate — a large discrepancy means the filter is not selecting what you thought.
-
Check in weekly, briefly. Progress, blockers, and whether anything in the selection turns out not to apply. The last one is worth capturing: alerts that were wrongly included are a triage finding, not just a dismissal.
-
Review the pull requests properly, especially the Autofix-derived ones. This is the step the deadline pressures, and it is the step that determines whether the campaign fixed anything.
-
Close it, and write down what you learned. Which class it was, how long it took per alert, what the fix-to-dismissal ratio was. The second campaign is scoped from that number.
Step 8 is what turns one campaign into a capability. An organisation that knows a SQL injection alert costs about twenty minutes to fix and review can plan the next campaign; one that does not will guess, and will guess optimistically.
Tracking
Section titled “Tracking”Campaigns are tracked through the organisation’s security views, showing progress against the selected alerts.
Two numbers are worth watching, and one of them is a trap.
Alerts resolved is the headline, and it counts both fixes and dismissals. A campaign that “completes” by dismissing three hundred alerts has moved a number without changing anything, and the number looks identical either way.
The fix-to-dismissal ratio is the honest measure. A campaign with a high dismissal rate is telling you something real — usually that the selection included alerts that genuinely do not apply, which is a finding about scoping or about triage rather than a failure. Worth investigating rather than celebrating.
When a campaign is complete, or if you want to pause it, close it. Closed campaigns remain viewable and can be reopened, which makes closing a low-stakes action rather than a final one.
Why this is an organisational mechanism, not a technical one
Section titled “Why this is an organisational mechanism, not a technical one”Nothing a campaign does is technically difficult. The alerts already exist. The filters are the same filters the security overview offers. Assignment is a field. A due date is a field.
It works anyway, and the reason is worth understanding because it generalises.
Security debt loses prioritisation contests by default. Feature work has a requester, a date and a visible consequence for missing it. A backlog of alerts has none of those, so it loses every time, every quarter, in every team — not because anybody decided it was unimportant, but because nothing about it competes.
A campaign supplies the three missing properties. A named owner means somebody’s week contains this. A deadline means it appears in planning. Visible progress means slipping is noticed.
The corollary is that a campaign fails when any of the three is missing. A campaign with a deadline nobody agreed to, an owner who is an alias, or progress nobody looks at is the same backlog with a name attached — and running one of those is worse than not running it, because it consumes the credibility the next one needs.
The second corollary is that this mechanism is not specific to GitHub. If your organisation manages security debt well in a tracker already, campaigns add convenience rather than capability. If it does not, the problem being solved is the one described above, and the tooling matters less than the fact that somebody now owns a dated piece of work.
Where campaigns fit
Section titled “Where campaigns fit”Campaigns are for security debt: the accumulated backlog. They are not the mechanism for day-to-day security, and using them for it produces a permanent campaign that nobody treats as urgent.
The division that works:
| Work | Mechanism |
|---|---|
| Preventing new problems | Merge gates — rulesets requiring code scanning results |
| New alerts, day to day | Ordinary triage with a short response time |
| The historical backlog | A campaign, with an owner and a deadline |
| Recurring hygiene | Automated updates — Dependabot |
The first row is what stops the backlog growing while you work it. Running a campaign without a gate is bailing without plugging the hole: the backlog shrinks, new alerts accumulate, and the second campaign is the same size as the first.
Secret scanning campaigns
Section titled “Secret scanning campaigns”Campaigns over secret scanning alerts are available and currently in public preview, and they behave differently from code campaigns in two ways that matter.
No Autofix. There is nothing to suggest — the remediation for a leaked credential is rotation, which happens outside the repository. See Rotating exposed credentials.
Wider participation. Code campaigns notify people with write access, because fixing the alert means changing code. Secret scanning campaigns need only view access to the alerts list, which means a security team member without write access can take part.
The more important difference is one the tooling does not encode: the unit of work is not the alert. A code scanning alert is fixed by a change to the code. A secret scanning alert is resolved by rotating a credential, which may live in a cloud console, a partner’s dashboard or a secret manager — and which may be used by systems that have nothing to do with the repository.
That makes scoping harder. A campaign over three hundred secret scanning alerts is potentially three hundred credential rotations across systems owned by different people. The scoping that works is by provider rather than by repository: every exposed credential for one cloud account is one team’s work, with one rotation procedure, and progress means something.
The prioritisation is also different, and simpler. Where validity checking is available, active credentials are the campaign and everything else is cleanup. A campaign filtered to validity-confirmed alerts is short, urgent and unambiguous, which is close to the ideal shape.
What campaigns do not do
Section titled “What campaigns do not do”Worth stating, because the mechanism is easy to over-apply.
They do not find anything. A campaign operates on alerts that already exist. Coverage — which repositories are scanned, with which query suites, for which languages — is a separate question and a prerequisite. A campaign over a well-scanned twenty percent of your estate is a campaign with a misleading completion number.
They do not triage. The selection is a filter, not an assessment. Every alert in a campaign still needs the reachability and exposure work from Vulnerability alerts, and a campaign that skips it will fix unreachable code and dismiss reachable code at roughly equal rates.
They do not cover Dependabot alerts. Dependency remediation has its own mechanisms — grouped updates, security updates, auto-triage rules. A dependency backlog is a real problem and this is not the tool for it.
They do not replace the gate. Stated already, and worth repeating because it is the most common structural error: a campaign without a merge gate is a bucket with a hole in it.
Common mistakes
Section titled “Common mistakes”Running a campaign without a merge gate. New alerts accumulate while you clear old ones.
Scoping too broadly. A thousand alerts across two hundred repositories is not a project anybody finishes.
No named point of contact. An alias is not an owner, and developers stop when they have a question.
No deadline. Security work loses every scheduling contest against dated work.
Measuring alerts resolved. Dismissals count identically to fixes, and dismissals are the cheap option under deadline pressure.
Batch-approving Autofix suggestions to hit the date. The exact failure mode the deadline creates.
Running several campaigns at once. The limit is ten; the practical number for one organisation is one, occasionally two. Competing campaigns are competing priorities.
Treating a campaign as ongoing security. It is a project. Day-to-day alerts need day-to-day triage.
Mental model
Section titled “Mental model”A campaign converts a pile of alerts into a piece of work. The alerts were always there; what changes is that somebody owns them, there is a date, and progress is visible — which is what makes any large remediation happen, security or otherwise.
What you learned
Section titled “What you learned”- Campaigns manage security debt as a scoped project rather than as a queue
- Up to 10 active campaigns per organisation, and up to 1,000 alerts per campaign
- Code scanning alerts on the default branch are covered; secret scanning campaigns are in public preview
- Alerts in recently-pushed repositories are prioritised for inclusion
- Templates can filter to alerts Copilot Autofix supports, which lowers per-alert cost
- Code campaign alerts are automatically submitted to Autofix as capacity allows
- Alerts can be assigned to a person with write access, or to the Copilot coding agent
- A named point of contact and a realistic due date are what make the mechanism work
- Track the fix-to-dismissal ratio, because resolved-alert count treats both identically
- A campaign without a merge gate refills as fast as it drains
Exercise
Section titled “Exercise”This needs an organisation you administer. If you have no test organisation, work through the design on paper — the reasoning is the exercise.
-
Look at your organisation’s open code scanning alerts. Predict: how many, and how are they distributed across repositories?
-
Group them by rule rather than by severity. Predict: do a small number of rules account for most alerts?
-
Design a campaign scoped to the single most common class. Estimate the alert count and a realistic deadline.
-
Identify the point of contact. Predict: is it obvious who that should be? If not, that is a finding about ownership.
-
Check whether the merge gate exists. Predict: if you cleared this backlog, how long until it returned?
-
If you can, create the campaign with a template filtering to Autofix-supported alerts, and compare its size against the unfiltered class.
-
After a week, look at the fix-to-dismissal ratio rather than the resolved count.