Branch protection is a server-side rule that says "you cannot do X to main." CODEOWNERS is a file that says "if these paths change, these humans must approve." Together they're how teams keep main safe from accidents.
Imagine an apartment building. Anyone can walk up to the front door. But there's a doorman, and his job is to check three things before letting you in: are you on the list, have you been signed in by another resident, and are you carrying nothing that violates building policy. If any of those fail, the doorman politely sends you away.
Branch protection is exactly the same idea, applied to main. It's a set of rules that GitHub's server checks every time anyone tries to push or merge to a protected branch. If the push matches all the rules, it lands. If not, GitHub rejects it with an error message — and the official version of the project is untouched.
Without any rules in place, anyone with push access (today: just you) can run git push --force and overwrite the entire history of main. That's not a typo — force-push literally rewrites which commits exist, throwing away anything you didn't keep. A single bad command on a tired Friday afternoon can destroy weeks of work. Branch protection is what stops that.
You can mix and match. Below are the four rules AL-2.1 tried to set on your repo — these are the ones almost every team uses:
frontend/ and CODEOWNERS says Josh owns frontend, Josh must click Approve before merge is allowed. We'll cover CODEOWNERS in the next concept.main is the single most destructive thing you can do. With this set to false, GitHub refuses every force-push to main, no matter who you are.Here's the literal request the AL-2.1 worker tried to make — and was rejected for (we'll get to why in Lesson 3):
CODEOWNERS is just a text file. You commit it like any other file. GitHub looks for it at a known location (the root of the repo, or inside .github/) and reads it. Each line maps a file pattern to a GitHub username:
When Josh's GitHub username is added to the repo, CODEOWNERS expands to @lovebuilt @josh-gh on shared rows, plus per-lab rows where one of you takes lead. Touching frontend/? Josh's review gets auto-requested. Touching backend/? Yours does.
Here's the part that surprises everyone: the CODEOWNERS file works by itself, even without branch protection — but only as documentation. GitHub still reads it and shows ownership in the web UI. What it can't do, without protection, is block a merge. That's the doorman's job.
This is the single most important concept in this lesson. Same CODEOWNERS file. Same git history. Two completely different outcomes depending on whether branch protection is turned on.
"Please don't park here."
The file is committed and visible in the UI. GitHub even auto-requests reviews from listed owners. But anyone with push access can still merge straight to main with no review and no blocker.
Useful as documentation. Useless as enforcement.
A bollard. You can't drive past.
The same file becomes a hard gate. GitHub refuses every merge to main until the code owners on the touched paths click Approve. Force-pushes bounce. Direct pushes bounce. The doorman is on duty.
Documentation and enforcement.
The AL repo already has a CODEOWNERS file committed (AL-2.1 landed it on 2026-05-18 with @lovebuilt as the sole owner). So the "sign" side is live today — GitHub knows you own everything. The "barrier" side is what's missing. Flipping it on is what the next lesson is about.
CODEOWNERS · committed at repo root, content: * @lovebuilt (plus 3 per-lab rows). Visible in GitHub UI. Documentation works.
Branch protection · not configured. AL-2.1 attempted it; GitHub returned HTTP 403 with the paywall message. The repo is unprotected.
Force-push to main · currently allowed. Nothing stops you (or anyone with push access) from rewriting history.
Direct push to main · currently allowed. No pull request required.
This is fine for solo work — you're not going to force-push your own repo accidentally. It stops being fine the moment Josh joins, which is exactly the timeline that Lesson 3 is about.