AI lowered the barrier to contributions, yet maintainers are still the filters

GitHub is where open-source projects live, and that openness is our biggest strength. These projects run on maintainers, people spending their evenings reviewing strangers' suggested changes for free. AI lowered the barrier to contributing, and I joined GitHub as the company began tackling what that actually meant in practice.


In one year, monthly pull request volume went from 569K to 991K. Merge rates fell from 78.5% to 63.9%. Maintainers were buried in noise, and blocking accounts became a norm. However, blocking was still a plain list, built for a much smaller era of open source. Now ~143K blocking actions on GitHub every 90 days I joined as the team started asking: How do we give maintainers real control over who can contribute?


I joined the Tiny Wins team with one job- fix small, painful problems for maintainers, fast. As AI writes more code, the humans who judge that code matter more. Maintainers are GitHub's most important users and its least profitable. If they leave, contributors follow. Blocked Users went from first ticket to release in about six weeks, timed against the company's FY27 Q1 plan. I owned the design end to end, research, specs, prototypes, testing, and three shipped code changes.


Our loop: listen, ship, close the loop with the people who asked.

Things to keep in mind

Reasoning

Blocking is a maintainer's main lever against spam and bots. On an average, ~143,000 blocking actions on GitHub every 90 days.

The blocking tool was built for a list under 100 users, not ten thousands. The real issue was one page doing two jobs: adding a block and managing the ones you already had.

Goal

Any maintainer, on any project, should be able to find and reconstruct why someone was blocked, even months later. Maintainers should be able to control repository bloat and future interactions


The system had to be designed for a future scale, not just today's- because if a million PRs a year is possible now, a billion isn't hypothetical.

Dynamics

Permissions splinters three ways: personal vs. organization blocks, a user's write access, and GitHub's privacy rules.


The platform was mid-migration, from Rails to React, limiting design system component.


Short on time, staff, and reviews.


Inconsistent design ownership and visual consistency for the past five years

Context

None of the six instances on dotcom to block a user were consistent. Private notes and patterns.


We stayed close to maintainers - interviews, weekly NSAT survey, the #feedback-for-maintainers Slack channel, and the broader GitHub community. This gave direction.


System interdependencies across interaction tools (eg: temporary limits, report, hide)

Formation

Notes moved behind an icon and detail is one click away

Currently, maintainers utilize the existing notes field to paste long issue/PR links or other longer text (from Kusto/Trino query). It took up too much real estate on the current UI and became unecessary at first glance. So, printing the full note on every row was solving the wrong problem. The note didn't belong inline anymore, and I moved it behind an icon that dispalys when a note exists. The icon became the signal that there's more to read.

Form 02

Tables tailored to two access levels

Individuals and organizations needed different information. Organization blocks are shared across multiple maintainers. So, individual accounts had four columns (Username, Date blocked, Reason label, Actions) and organizations getting two more (Blocked by, Blocked until). I dropped the exact block time from the UI as noise, keeping it available through the CLI for anyone who needs precision.

Form 03

Preset labels let people see at a glance why a user is blocked

Maintainers triage thousands of blocked users. It is taxxing to understand each note typed out, and make sense of why a user is blocked. Adding quick labels (spam, misconduct, harassment, entitlement) helps maintainers to scan. Based on slack channels, notes, and three usability testing these were the most common block reasons.

A maintainer pushed back, fairly, โ€œwhy not just let us type anything?โ€ Yes, free text was more expressive, but it can't be sorted or filtered, and it stops being readable at exactly the scale this needed to solve for. A tradeoff that changed the page from something you read to something you scan.

Form 04

React-in-Rails pattern that places modern data to older pages

The settings page couldn't natively use GitHub's newer React table, since the platform was mid-migration from Rails. Rather than wait on that migration or build a custom one-table, I mounted the table as a React island inside the existing Rails page, and used design-system components as partial React components on the page. That kept the six-week timeline intact and left the team with a reusable pattern for putting a modern table on an older pages. This changed other design decisions in other projects.

Form 05

Accelerating usability testing path with Copilot

A cross-functional decision couldn't be validated on paper. I built three clickable prototypes with Copilot: individual, organization, and block-from-profile, and ran them past PM, a staff ship to GitHub employees, a maintainer Slack channel, and Trust and Safety. Every screen used GitHub Primerโ€™s Design System components and tokens to maintain consistency on existing pages.

Form 06

Internal testing, maintainer feedback, and Trust & Legal team reviews shaped the final feature release

Before GA launch, we had a Trust and Safety and Legal review. Labels/notes are private to moderators and blocking never notifies the blocked person, which meant this was a safety decision. This review cut "Privacy violation" before it ever shipped, since legal determination sought redundant under โ€˜Harassmentโ€™. Separately, "Entitlement", had already shipped with real accounts, was unshipped later.

Code-fix: Removing a label that real records already used meant I couldn't just delete it from the code: I split the type definition into everything the page can still display (four labels, for backward compatibility) and everything a maintainer can newly select (three). A good way to drop a label without breaking code.

Results

Globally, around 540,000 GitHub maintainers now have better tooling against AI-generated spam and misconduct means a more curated repository. Maintainer time redirected toward reviewing real contributions instead of triaging noise. Maintainers can search by username, name, or email, and sort through users.

91% satisfaction and a 86% jump in NSAT for UX/designs compared to the previous week

Maintainers can differentiate between blocking a user and searching for existing blocks

A reusable pattern for embedding a modern components inside an older Rails-rendered page

Next

Now that any maintainer can quickly see why someone was blocked, the next constraint is cross-repository trust. During interviews and testings, maintainers repeatedly said that whether someone's been blocked on another repository/organization is a great trust signal, but it comes with its own risks.