← Blog

Monorepo PR Notifications in Slack: Tame CODEOWNERS

In a monorepo, CODEOWNERS requests reviews from whole teams and one channel carries every PR. Here is how to cut monorepo notification noise in Slack.

Moving to a monorepo fixes dependency drift and breaks notifications. Every team now commits to one repository, so anything subscribed at the repository level carries the whole company's pull request traffic. The frontend engineer who wanted to follow the web app is now reading infra Terraform changes.

CODEOWNERS makes it worse rather than better. It exists to route reviews to the right people, but the routing it does is team-shaped, and GitHub notifies teams by notifying every member.

The arithmetic of a monorepo notification

Take a pull request that touches three directories: packages/api, packages/web, and the shared tooling/eslint-config. CODEOWNERS maps those to @acme/backend, @acme/frontend, and @acme/platform. GitHub requests a review from all three teams.

If those teams have 8, 10, and 5 members, that single pull request generates a review request reaching 23 people. One of them will review the API change. The other 22 get a notification about a diff they were never going to open.

Then the author pushes a fixup commit and a lint bot comments. In a monorepo the same bots run against every package, so a Renovate lockfile bump can trip CODEOWNERS across a dozen paths at once and repeat the whole fan-out.

The official GitHub Slack app has no path filter

The obvious fix is to subscribe each team's channel to just its part of the tree. That is not something /github subscribe can do. Subscriptions are per repository, and the available filters cover event types, branches, and labels. There is no packages/api/** equivalent.

What people do instead is subscribe to labels and then require every author to label correctly, forever. That works until someone forgets, which is the second week. Branch filters have the same problem in a trunk-based monorepo where everything targets main.

A repo-per-channel model was designed for many small repositories. Collapsing 40 repos into one collapses 40 channels into one firehose. The setup steps and the rest of the constraints are in our guide to getting GitHub notifications in Slack.

Fix the CODEOWNERS side first

Before changing tools, cut the number of people GitHub thinks it should ask. Three changes do most of the work.

  • Narrow the patterns. A catch-all line like * @acme/platform at the top of the file pulls one team into every pull request in the repository. Replace it with per-directory rules and keep the fallback owner as small as possible.
  • Turn on code review assignment. GitHub can pick one or two members of a requested team instead of the whole group, using round-robin or load-balance. The review request then lands on named people who know it is theirs.
  • Turn off team notifications for those teams. Code review assignment has a companion setting that stops GitHub from notifying every team member once individual reviewers have been chosen. Without it, you get the assignment and the fan-out.

Owning teams of 15 are the root cause more often than the tooling is. If a team is large enough that a review request to it is background noise, split it or give it narrower paths.

Then route by person, not by repository

Path filtering is a workaround for a repository-shaped delivery model. In a monorepo the only stable unit left is the person: the pull requests where you are the author, the assignee, a requested reviewer, or mentioned by name. That set stays small no matter how big the repository grows.

GitNotifier delivers to that set as personal Slack DMs, so there is no channel to configure per package and nothing to relabel when directories move. A pull request in packages/api reaches you because you own it, not because you subscribed to the repository that contains it.

The monorepo-specific controls sit on top of that:

  • Mute review-requested teams by slug. Add @acme/platform, @acme/frontend to your mute rules and GitNotifier skips the review-request DM when a pull request is requested from those teams, then auto-mutes that pull request for you. If someone requests you by name in addition to the team, the notification still comes through.
  • Direct mentions always survive. Muting a team does not mute the person who types your handle in a comment, which is the case that actually needs you.
  • Bot comments are dropped by default. Renovate, Dependabot, and CodeRabbit generate the highest volume in a monorepo because their changes touch the most paths. See how bot comment spam gets filtered for the routing rules.
  • Reviewer Comment Quiet Mode. Keeps the initial review request but mutes the follow-up thread chatter on pull requests where you are only in reviewer scope.
  • Per-PR mute with a reaction. React 🔇 on a notification to drop the rest of that pull request's traffic. Useful for the giant migration PR that will run for two weeks.
Tip: mute the teams you are a member of but do not review for, not the ones you review for. Most people are on 3 or 4 GitHub teams and only review for one of them.

Keep one channel for the things everyone needs

Quiet personal notifications do not remove the need for shared visibility. Two signals still belong in front of the whole team: a broken default branch, and the queue of reviews nobody has picked up.

GitNotifier alerts on check suite and workflow job failures, including failures on main after a merge, which in a monorepo blocks every team at once. For the review queue, a scheduled pull request digest lists what is still waiting on you across every repository and flags anything with no activity for 24 hours.

A working setup for a 40-person monorepo

  • CODEOWNERS with per-directory patterns and no repository-wide fallback beyond a small platform group.
  • Code review assignment on every owning team, load-balance algorithm, team notifications off.
  • GitNotifier DMs for review requests, mentions, assignments, and reviews on your pull requests.
  • Muted team slugs for the teams you belong to but do not review for.
  • Bot comments muted, with the ready-to-merge and CI failure signals kept.
  • One morning digest for the review queue, one channel for default branch failures.

The pull request volume is unchanged. What changes is that the 22 people who were never going to review the API diff stop hearing about it.

Frequently asked questions

Why do monorepo pull requests generate so many Slack notifications?

A monorepo concentrates every team into one repository, so a repo-level Slack subscription carries all of it. CODEOWNERS then requests reviews from whole teams rather than individuals, and GitHub notifies every member of a requested team by default. One pull request touching three owned paths can reach dozens of people who will never review it.

Can the official GitHub Slack app filter notifications by folder or path?

No. The /github subscribe command works at the repository level, with filters for event types, branches, and labels. There is no path or directory filter, so a channel subscribed to a monorepo receives pull request activity from every package in it.

How do I stop CODEOWNERS from notifying an entire team?

Two levers help on the GitHub side. Enable code review assignment on the team so GitHub picks one or two reviewers instead of the whole group, and turn off the team notification setting so the rest of the members are not pinged. Splitting broad CODEOWNERS patterns into narrower per-directory rules reduces how often a team is pulled in at all.

How does GitNotifier handle monorepo review requests?

GitNotifier is person-first rather than repository-first, so it notifies you about pull requests you are actually on. You can mute review-requested GitHub teams by slug, which drops the review-request DM and auto-mutes that pull request for you while still delivering direct mentions. Bot comments are muted by default, and any single pull request can be muted with a 🔇 reaction.

Monorepo scale without the notification tax

GitNotifier routes GitHub pull request activity to the people on it, with mute rules for the teams and bots that pull you into diffs you do not own. No channel to configure per package, no labels to maintain.