Pull Requests and Pull-Request CI
A pull request is technically a proposed merge, but professionally it serves a much larger purpose:
A merge changes the repository. A pull request establishes why that change should happen and proves that it is safe.
The common industry approach is a lightweight, trunk-based workflow—often called GitHub Flow:
protected main branch
↑
short-lived working branch
↑
pull request
├─ automated CI evidence
├─ human review
└─ discussion and decisions
GitHub describes pull requests as proposals that bring the description, commits, reviews, conversations, and automated checks together before merging. See GitHub’s pull-request documentation.
The important concepts
| Thing | Purpose |
|---|---|
| Branch | Temporary workspace for one change |
| Commit | Saved snapshot within that workspace |
| Pull request | Proposal and review record for the whole change |
| CI | Automated evidence that the proposed change works |
| Review | Human judgment about whether it is the right implementation |
| Merge | Incorporates the accepted change into the protected branch |
| CD | Delivers the accepted commit to staging or production |
A PR does not copy your code somewhere special. It compares:
- the head branch containing your proposed work;
- against the base branch you want to change, such as
dev/main.
When you push another commit to the head branch, the existing PR updates automatically. CI runs again, and reviewers see the new differences.
The standard PR lifecycle
1. Start from an up-to-date protected branch
For this repository:
git switch dev/main
git pull --ff-only
git switch -c feature/booking-conflict-check
The working branch should usually represent one coherent change—not an entire month of development.
Common names include:
feature/booking-conflict-check
fix/realtime-disconnect
refactor/call-session-lifecycle
ci/add-pr-validation
The exact naming convention matters less than consistency.
2. Implement one reviewable change
A healthy PR contains one conceptual change with its tests and documentation.
- Good: “Reject overlapping booking times and add tests.”
- Too broad: “Rewrite booking, calling, configuration, logging, and deployment.”
- Artificially narrow: “Add an unused booking interface,” followed by another PR that finally uses it.
Small PRs are easier to understand, test, merge, revert, and review thoroughly. Google’s published engineering guidance similarly recommends one self-contained change and normally separating significant refactoring from behavior changes. See Google’s small-change guidance.
“Small” is conceptual, not a strict line limit. A 300-line database migration may be one coherent change, while 50 unrelated lines spread across ten components may not be.
3. Run relevant checks locally
Before asking another person—or CI—to spend time on the change, the author runs the appropriate local validations.
For this repository, the canonical checks are:
bun run format:check
bun run typecheck
bun run test
bun run build
You do not necessarily run the full expensive suite after every keystroke. During development you run targeted checks, then run the appropriate complete checks before marking the PR ready.
4. Push the branch and open a draft PR
git push -u origin feature/booking-conflict-check
Initially, the PR can be marked Draft. A draft means:
The direction is visible and CI may run, but I am not claiming this is ready to merge.
Opening drafts early is useful when you want feedback on an architectural direction before completing all the implementation.
A useful PR description answers:
## Why
Customers can currently create overlapping appointments.
## What changed
- Added overlap detection to booking creation.
- Added tests for boundary and timezone cases.
- Left existing appointments unchanged.
## Validation
- bun run typecheck
- bun run test
- bun run build
## Risk and rollout
Affects new booking creation only. No database migration.
For UI work, add screenshots. For database work, explain migration and rollback behavior. For operational changes, explain deployment and monitoring.
The PR description should explain why and what, not narrate every line of code.
5. Pull-request CI runs automatically
Opening the PR and pushing subsequent commits trigger a clean CI environment.
A typical PR pipeline for this repository would resemble:
install locked dependencies
↓
format and generated-config checks
↓
typechecking
↓
tests
↓
production build
↓
security/dependency checks
These jobs can run in parallel where possible.
CI answers questions such as:
- Does this work outside the author’s computer?
- Can dependencies be installed reproducibly?
- Does generated configuration match committed files?
- Does the entire repository typecheck?
- Do the tests pass?
- Can a production artifact be built?
- Did automated security tooling detect a serious problem?
A CI result is attached to a particular commit. Push more code, and that evidence must be regenerated.
CI checks are initially only information. They become an actual gate when the base branch has a ruleset requiring them. GitHub then refuses to merge until the required checks pass. See GitHub’s status-check documentation.
A professional norm is simple:
A red required check is not ignored. The author fixes it or demonstrates that the check itself is defective.
Repeatedly rerunning flaky tests until they happen to pass is not a valid fix.
6. The author performs a self-review
Before requesting another reviewer, the author reads the PR’s Files changed view as if they had not written it.
This often catches:
- debug statements;
- unrelated files;
- accidental formatting churn;
- poor names;
- missing tests;
- stale comments;
- secrets or generated output;
- code that made sense during implementation but is confusing in the final diff.
Self-review remains useful even when you are the only developer.
7. Human review evaluates what CI cannot
The author marks the PR ready and requests a reviewer.
CI is good at deterministic facts. A human reviewer asks different questions:
- Is this solving the correct problem?
- Is the design appropriate?
- Is it unnecessarily complex?
- Are important failure cases missing?
- Are tests validating meaningful behavior?
- Is the database migration reversible or backward-compatible?
- Could this interrupt active calls?
- Does it expose private data?
- Will another developer understand it in six months?
Reviewers normally choose one of three outcomes:
- Comment: feedback without blocking or approving.
- Approve: acceptable to merge.
- Request changes: a blocking issue must be addressed.
GitHub supports these review states and can enforce approvals through protected-branch rules. See GitHub’s review documentation.
Healthy review comments distinguish severity:
Blocking: this migration prevents the previous release from starting.
Suggestion: extracting this parser may make the error path clearer.
Nit: consider renaming `data` to `appointment`.
Review should improve the change, not become a quest for personal stylistic perfection. Formatting and other mechanical rules belong in automation. Google’s review standard recommends approving once a change clearly improves the codebase, even when it is not theoretically perfect. See Google’s code-review standard.
8. Address feedback on the same branch
You normally do not open a new PR to address review comments.
Make changes, commit them, and push:
git add ...
git commit -m "Handle booking boundaries consistently"
git push
The PR updates, CI reruns, and the reviewer can examine only the new changes.
Discussion threads should end in one of three states:
- the code was changed;
- both parties agreed no change was needed, with the reason recorded;
- the topic was explicitly deferred to a tracked follow-up.
If substantial new code is pushed after approval, it should receive another review. GitHub rulesets can dismiss stale approvals or require approval of the latest push. See GitHub’s ruleset options.
9. Merge only when the acceptance conditions are satisfied
A PR is ready when:
- the intended behavior is clear;
- required CI is green;
- required approval exists;
- blocking feedback is addressed;
- conversations are resolved;
- the proposed result is compatible with the current base branch;
- migration and rollout implications are understood.
The common merge strategies are:
- Squash merge: turn the entire PR into one commit.
- Merge commit: preserve every feature-branch commit and add a merge commit.
- Rebase merge: replay every feature-branch commit onto the base.
For this repository, I recommend squash merging initially. It produces a readable history in which one PR corresponds to one reversible commit:
4c82a91 Reject overlapping appointments (#184)
a9e713f Improve call shutdown handling (#183)
80b0332 Add tenant configuration validation (#182)
Temporary commits such as “address review,” “fix typo,” and “try test again” do not pollute the permanent history. GitHub supports all three strategies. See GitHub’s merge documentation.
The PR title therefore becomes important because it commonly becomes the squash commit message.
10. Delete the working branch and monitor delivery
After merging:
- delete the feature branch;
- run CI again on the authoritative merged commit;
- build the immutable release artifact;
- deploy it to staging;
- perform smoke checks;
- promote that artifact to production when approved;
- monitor the deployment.
Merging and deploying are separate decisions. A PR can be safely merged but placed behind a feature flag, deployed only to staging, or held until a release window.
If the merged change causes trouble, revert the PR’s merge commit with another PR. Do not silently rewrite protected branch history.
The standard CI layers
Professional repositories usually separate three types of automation:
Pull-request CI
Fast feedback before merge:
format → typecheck → unit/integration tests → build → security checks
Trunk CI
Runs after merge on the definitive commit:
repeat critical validation → create immutable artifact → record provenance
This protects against two individually green PRs interacting badly when combined.
Delivery pipeline
Operates on the built artifact:
deploy staging → smoke test → production approval → deploy → verify/rollback
The production server should not repeat dependency resolution and build whatever happens to be checked out there.
What I recommend for your present team size
Because you may initially be the only regular developer, requiring another human approval could make all work impossible. Start with:
- every change to
dev/mainmust go through a PR; - required format, typecheck, test, and build checks;
- required resolution of conversations;
- block direct pushes, force-pushes, and deletion of
dev/main; - squash merge only;
- automatically delete merged branches;
- run trunk CI after every merge;
- allow a documented emergency bypass, but do not use it for ordinary convenience.
Your solo ritual becomes:
branch → implement → local validation → draft PR → CI
→ self-review → ready PR → green checks → squash merge
→ staging deployment → verification
When a second active developer joins, add one required approval. For sensitive areas—authentication, migrations, telephony lifecycle, secrets—request the person with the most relevant knowledge. At higher PR volume, add a merge queue so GitHub tests and serializes the exact merge order.
You do not need two approvals, a change-control meeting, multiple release branches, or a committee for every edit. Those are organization-specific controls, not the essence of pull requests.
The professional habit is this:
No change enters the trusted branch merely because its author believes it is finished. It enters with an understandable proposal, reproducible automated evidence, and review proportional to its risk.
Prepared for the Callmo Assistant repository, August 2026.