GitHub / 2020–22
Turning several acquired security tools into one product, so developers can ship secure code.

- Problem
- Developers wanted to ship secure code. Projects in popular languages rely on a lot of dependencies, any of which a bad actor could use to distribute malware, and the code itself has its own vulnerabilities. GitHub had acquired the tools to find both, from different companies, each with its own way of reporting a problem.
- My part
- Moved from Microsoft after the acquisition, as GitHub grew its team by bringing people across. Principal Designer in the Security group, hands-on, with code scanning as the main surface, plus requests and support to the Primer design system team.
- The idea
- One Security product, not three tools. Whatever found the problem, the developer meets it in the same shape, in the pull request, on the line that caused it.
- What shipped
- Code scanning, generally available from September 2020 and free for public repositories, as part of one Security offering with secret scanning and dependency review.
The setting
Microsoft acquired GitHub while I was at Microsoft, and GitHub quickly grew its team by moving people across. I was one of them. Between 2020 and 2022 I worked in the Security group, which at that point was really a few technology acquisitions, CodeQL for analysing code among them, that had to become one cohesive group and one set of features.
The need was simple to state and hard to meet: developers wanted to ship secure code. Projects written in popular languages like JavaScript rely on a lot of dependencies, and a bad actor could easily distribute malware through one of them. The code itself has its own vulnerabilities. GitHub now owned the tools to find both, but they had come from different companies, each with its own way of reporting a problem.
What I did
At GitHub I was a Principal Designer. The screens on this page are mine. As product designers we also helped the Design System team develop Primer, the shared component library, with requests and support.
The idea
One Security product, not three tools. Whatever found the problem, code analysis, a leaked secret or a vulnerable dependency, the developer should meet it in the same shape, and meet it where they already are: in the pull request, on the line that caused it.
The choices
One alert, whatever found it
- Chose
- A single alert shape, with the same state, severity, location and history, shared by code scanning, secret scanning and dependency alerts.
- Over
- Each acquired tool keeping the reporting interface it came withA security dashboard alongside the repository rather than inside it
A developer doesn't care which engine found the problem; they care whether it is real, how bad it is and where it is. A shared shape meant learning one thing: the alert page at the top of this case, with its state, severity, location, affected branches and the record of how it was tracked and fixed, is that shape. The cost was that each tool's native output had to fit the frame: CodeQL's data-flow paths went behind a “Show paths” control, and the details specific to one kind of alert had to earn their place on the page rather than arrive by default.
The finding goes in the pull request
- Chose
- An annotation on the changed line, with severity, one sentence on what is wrong and a link to the full alert.
- Over
- The whole alert inline, paths and advisories includedA summary in the checks only, with the alerts kept to the Security tab
The person who wrote the line is the one who can fix it, and the moment they can fix it cheaply is review. But a pull request is already a crowded page, so the inline version had to be short: enough to decide, not enough to fix, with everything else one click away.

Blocking the merge is the repository's call
- Chose
- Code scanning as a status check that a repository can mark as required, so a finding can block the merge.
- Over
- Always blocking, so nothing vulnerable can be mergedNever blocking: inform and let people decide
Interrupting is a strong move, and the wrong default for a platform with every kind of project on it. Making it a check meant a repository could choose how hard the rule was, using a mechanism it already knew. The cost is that on the repositories that opt in, a false positive becomes a blocker until someone deals with it, which is exactly why the next decision mattered.
Dismissing needs a reason
- Chose
- Alerts can be dismissed, with a stated reason such as a false positive or something used only in tests, and stay in the history.
- Over
- No dismissal, so every finding has to be fixed or lived withA silent close
If developers can't say “not this one”, they stop looking at all of them. If they can say it without a reason, the count going down stops meaning anything. A reason keeps the dismissal honest and leaves a record of why, for the next person who hits the same line.
Shipping it
Code scanning became generally available in September 2020, free for public repositories and part of Advanced Security for private ones, alongside secret scanning and dependency review under one Security tab.
Beyond Security
I also designed for GitHub Actions and Code Search. The search work was as much writing as drawing: specifying how qualifier suggestions should narrow as a developer types.

Looking back
I was designing for millions of developers I couldn't see, on a platform where every choice was highly scrutinised and commented on on Twitter. The CEO was on there too, listening, and sometimes committing to things in public. That is a different discipline from working with a handful of customers: you find out what people think in public, and quickly, and the roadmap could take shape there as much as inside.
What I would keep is the alert as the unit, not the tool. Deciding early that a developer meets one kind of thing, wherever it came from, is what let three products become one; everything else on this page followed from that.
What I would do differently is design the dismiss path first. Dismissal with a reason came as a consequence of blocking, but it is what makes blocking survivable, and it should have been on the table before it.