Foundations: Product, Code and the Working Environment

GitHub as a System for Accepting and Controlling Changes

How to read a GitHub pull request as a bundle of separate pieces of evidence (target, diff, revision, reviews, checks, protection rules, merge and…

Chapter 5Foundations: Product, Code and the Working Environment

An AI agent reports: "The changes are ready, all checks are green." But what exactly did it change? Which version of the code did GitHub actually check? Who reviewed the diff? Is the main branch really protected? And has the version been installed for users? GitHub puts the answers to all of these questions on the same screen, but answering one of them does not answer the rest.

A pull request (PR, a request to have changes included in a branch) page shows the file differences, the reviewers' feedback and the automated checks side by side. Before recommending a merge, a reviewer has to establish which version was checked and what each status actually means.

What you will be able to do

After this unit you will be able to check a PR's target branch, its diff, the associated revision, the discussion, the reviews and the checks. You will be able to tell apart a comment, an approval, a check result, a merge, a release and a deployment, and, from the evidence available, justify one of three decisions: the change can be accepted, it needs fixing, or there is not yet enough evidence.

Prerequisites. The anatomy of a code project, the terminal and local environment and Git history and branches explain repository structure, the terminal and Git history. Here we look at the shared process on GitHub. Building automated pipelines and checks in detail belongs to continuous integration and continuous delivery and deployment, and the method of reading code to AI code review. You do not need to install anything or do any exercises.

One place, several different things

On GitHub, a repository stores files, branches and history. An Issue describes a request, a defect or a topic for discussion; Discussions are meant for separate conversations within a community or team. A PR proposes changes from a source branch into a target branch and brings together the diff, a description of intent, links to the related work, comments, reviewers' feedback and checks.

The general difference between a Git tag, a release and a deployment is explained in Git history and branches. A Git tag is a named reference to a Git object, often a commit; by itself it neither publishes nor installs the program. The GitHub-specific detail that matters here is that a GitHub Release publishes a version based on a tag and can include notes and files (About Releases). Whether something was actually deployed is checked through a separate deployment record (deployment environments).

None of these objects proves any of the others. A closed Issue does not certify that the code was written, and a merged PR does not certify that the feature was deployed. An environment named production under Environments tells you neither whether protection is switched on nor whether the last deployment succeeded. Check the record of the action and its outcome in the context that matters.

A PR shows the source branch the commits come from and the target branch they are proposed for. For example, feature/new-invoice might be the source and main the target. A wrongly chosen target distorts the diff or sends the changes into the wrong release stream (GitHub Docs: reviewing proposed changes).

On a PR page it helps to follow a set order: first check the target branch, then understand the intent from the description, compare that with the diff and the linked task, and only then assess the reviews and checks. If the base branch changes, the diff itself can change, so before you evaluate anything, note exactly which version of the code you are looking at.

The Draft label means the author is not yet asking for a full review. Ready for review changes what the team expects, but it does not confirm that the change is complete or safe. The description should state the reason for the change and its scope, the linked task, the evidence and the risks. A PR template helps collect this information, but it does not vouch for its truth (GitHub Docs: managing and standardizing pull requests).

A comment, a review and an approval mean different things

Element What it means
Comment A conversation about a line of the diff or the PR as a whole; not yet a formal decision about readiness
Review request An invitation to a reviewer; the person invited has not yet submitted a result
Submitted review One of the outcomes Comment, Approve or Request changes

A Comment can contain a blocking objection, and an Approve can contain an optional suggestion. An approval is the reviewer's opinion about the diff they saw; it is not a guarantee of product quality and not a substitute for verification. After new commits, branch protection may mark an approval as stale; and even if it is still displayed, any substantial new difference needs to be reviewed (GitHub Docs: requesting a pull request review, protected branches).

So the decision to merge does not follow from any single field. The reviewer reads what the comments actually say, checks whether they refer to the current diff, and relates them to the repository's formal gates. If an objection points to an unknown risk, that risk has to be investigated or consciously accepted, even when an Approve status formally allows the merge.

Whether Request changes blocks a merge depends on the active rules and on the reviewer's permissions. Without required reviews it may be purely informational; a configured rule may require the problem to be fixed or the request to be properly dismissed by an authorised person. An approving comment does not replace an approval from a code owner (CODEOWNER) if a rule demands one. The status itself is a signal; the gates are defined by the repository's configuration (GitHub Docs: protected branches).

A comment on a line of the diff can ask a question or propose an exact change at that spot; a general comment discusses the PR as a whole. The author can reply, add a commit and mark the conversation as resolved, but the "resolved" marker alone does not prove the fix is sufficient: look at the new diff and at how it answers the original risk.

Begin a review with the problem and with what the change must not alter. Go through the files in order, attach your remarks to the relevant lines, and after checking, submit an overall review result. The Viewed button helps you keep track of the files you have read, but it only marks them; it does not approve their content. Submit the overall review after examining the actual diff, not after merely reading the description (GitHub Docs: reviewing proposed changes).

Checks and GitHub Actions: what was actually run

Continuous integration (CI) means merging changes into a shared branch frequently, with automated building and testing. Continuous delivery keeps changes in a state where they can be released on demand; the release itself may require a human decision. Continuous deployment automatically deploys every change that passes the required checks. Both of the last two are sometimes shortened to CD, which is why it matters to tell them apart by what they actually do. DevOps Research and Assessment (DORA) describes continuous delivery as a capability of the process (DORA: Continuous Delivery; Google Cloud: DevOps capabilities). These are not names of GitHub Actions products.

In GitHub Actions, a workflow defines the events that trigger it and its jobs: groups of steps that run in a runner environment. A workflow can be triggered when a PR is opened, updated or reopened, or when someone starts it manually. One PR can trigger several workflows, each with several jobs; a check can correspond to a single job or to an external service. The log shows the output of the steps; an artifact is a file created and stored during a run that can be downloaded for later use (GitHub Docs: workflows, workflow artifacts, events that trigger workflows).

A check's name does not tell you what was checked: open the run description and the log, and compare them with the changed files and the risk. For example, unit tests on its own does not prove that an integration, accessibility or end-user scenario was tested. If a PR has three jobs and one of them is conditionally run only for documentation, a green summary does not mean all three areas affected by the change were checked.

Revision What exactly is being checked
Head commit The latest commit on the source branch, which the reviewer compares against the diff
pull_request event Usually a test merge commit in refs/pull/<number>/merge that combines the current head and base
Another event The workflow may run on the head commit or on some other revision
Required check A rule may require a result for the head commit or for the test merge commit; check the actual source SHA

GitHub may show a commit's SHA (its identifier, produced by the Secure Hash Algorithm) in shortened form. After the head or the base changes, check the statuses for the current pair of branches: an earlier successful run may refer to different code. A check and an approval are different kinds of evidence: the first is a report from a workflow or a service, the second is a reviewer's decision (GitHub Docs: events that trigger workflows, troubleshooting required status checks, protected branches).

In practice, find out: which commit was checked; whether all required jobs have finished; who published the status; which logs back up the claim; whether any job is red, pending or skipped; and whether the job actually addressed the risk that changed. GitHub may count certain neutral or skipped states as passing for a rule; a skipped documentation check, for instance, proves nothing about authorisation. Depending on the rule, a required check may refer to the head commit or to the test merge commit, so the status name is not enough: verify the source SHA.

A successful CI run does not confirm the client's requirements, the contents of secrets, the state of production data or what the site looks like to a user, unless a specific job checked those things. A missing check is not an automatic failure, but if a rule requires it, or the risk calls for evidence, the information needed for a decision is still missing.

Small changes are easier to review and discuss; the IT Revolution article adapted from Accelerate links them to delivery practices. This is no guarantee of quality: a small PR can still break security, and we do not claim to have read the full Accelerate book here (IT Revolution, "24 Key Capabilities").

Branch protection and ownership of code

Branch protection and rulesets. A branch can require a PR, a certain number of approvals, approval from code owners (CODEOWNERS), named checks, resolved conversations, an up-to-date base, signed commits or a successful deployment. Rulesets apply rules to selected branches or tags. What is available depends on the pricing plan and the repository type, and what is actually protected depends on the settings: check the target branch itself, not just GitHub's list of features (GitHub Docs: protected branches, available rules for rulesets).

Also check the bypass actors: the people or apps for whom an exception applies. By default, branch protection restrictions do not apply to administrators or to users with a custom role that has the bypass branch protections permission; a rule can be set to include them too. In a ruleset, the list of bypass actors is configured explicitly (GitHub Docs: protected branches, rulesets).

The CODEOWNERS file lists who is responsible for which file paths. It can automatically send the owner a review request, that is, an invitation to review; until that person submits a result, this is neither an approval nor evidence that a review took place. To make an owner's approval mandatory, you need a corresponding protected-branch rule or ruleset. Check the file itself in the target branch and the rule actually in force, rather than inferring from the owner's name or from a "review requested" notice (GitHub Docs: about code owners, protected branches).

Permissions and secrets. Check the role of the agent or GitHub App separately from the permissions of GITHUB_TOKEN: that token governs what the workflow can access, not what the App or the agent can do. Follow the principle of least privilege; a workflow that only needs to read code does not need write access to the repository (GitHub Docs: GITHUB_TOKEN).

Give secrets only to the steps that need them. Never paste them into logs, PR comments, descriptions or screenshots; masking in logs does not guarantee safe handling. For a PR from a fork (a separate copy of the repository, often under a different owner's account), the pull_request event runs with limited permissions and does not receive ordinary secrets. Other events may follow a different access model, so do not run a privileged workflow on untrusted code without assessing the risk (GitHub Docs: secrets, workflows in forked repositories, compromised runners).

Merging a PR and deploying with GitHub

Once the required reviews and checks are in place, the PR can be merged into the target branch. For a job that uses a GitHub environment, you can require approval before it runs and restrict which branches are allowed and who can access secrets. As of 10 October 2026, GitHub Docs limit required reviewers to public repositories on the Free, Pro and Team plans; for a private repository, check the current plan terms. An environment's name does not mean these rules are switched on, and a deployment record does not prove the feature works for users (GitHub Docs: deployment environments).

GitHub offers three ways to merge a PR, and each shapes the history differently:

Method Result in the target history
Merge commit Keeps the branch's commits and adds a separate merge commit
Squash Creates one new commit; the original commits do not become separate ancestors of the target branch
Rebase-and-merge Re-applies the commits on top of the current target branch with new SHAs, without a merge commit

The choice should follow the repository's convention; the general model of merge, release and deployment is explained in Git history and branches (GitHub Docs: pull request merges).

Do not blur these steps in a report: a merge brings the changes into the target branch; a release marks a version for distribution; a deployment installs it into a chosen environment. Depending on the process, a release or deployment may start automatically after the merge, or a separate human action may be required. Verify each claim with the corresponding record, log or check in the relevant environment.

Fully worked examples

Example 1: A small PR with two checks

Situation. The team is adding a final payment due date to invoices. In this example the PR targets main, references the task BILL-42 and changes three files: the invoice template, the function that builds the text, and a test for that text. All names and events are invented for teaching purposes.

What the description says. The author explains which time zone the date uses, lists the cases checked (with a date and without one), and notes that the interface format does not change. The description also links the PR to the task and states what has not been checked.

What the reviewer sees. The reviewer compares the three files with the stated intent: the template shows the date only when there is one, the function uses the agreed time zone, and the test covers the cases with and without a date. A passing format-and-unit-tests does not prove that the date format matches the rule, and docs-links does not check it at all. The reviewer finds an unchecked edge case: a date that falls on the boundary between two days after the time-zone conversion. The author clarifies the expected format in the requirement, adds a test, and changes the function if the test shows a mismatch. The new version of the head then gets a new SHA, and the team compares the check results against that version specifically.

In the simulated log, format-and-unit-tests shows that the required tests completed for commit 8ab31e2; docs-links also passes, but it does not check the date. After the new commit, the checks run again on the new SHA. The reviewer re-reads the changed line and replies in the thread of the original comment. The SHA quoted is part of an invented teaching scenario, not a real run from this course.

In the simulated continuation, the added test passes, and the reviewer checks the fix and submits an approval for the current diff. This is a teaching example, not a report of a real run or a real approval. If the commit changes again after this review, check whether the substantial difference needs a fresh review.

Result. In the simulated scenario, the current head has two passing checks and a new approval after the second review. The team can recommend a merge if this evidence satisfies the repository's mandatory rules; otherwise the PR waits for the remaining conditions. None of this proves the invoicing service has been released or deployed.

Why this order. The description gives the intent, the diff shows the implementation, the review assesses the substance, and a check reports on one specific automated run. After new edits, the next check has to apply to the updated revision; every claim in the PR has a matching source of evidence.

Another option, and its limit. For a text file, the team might check the links and a preview instead of running a test job. A check called all-green whose content is unknown proves very little. No set of checks will confirm that the date matches the contract if nobody checks the contract terms.

Example 2: Green checks, but access to a report was never verified

Situation. A PR adds a "My analytics reports" screen. The build and the component tests pass, but the feature loads a report by organisation ID.

What else the reviewer has to establish. The reviewer checks where the server determines the current user, whether every request is matched against that user's organisation, and how a request for someone else's ID is rejected. A rendering test proves none of this. What is needed is a server-side test: user A can read their own report but not organisation B's report.

Decision. A merge is premature until the access boundary is confirmed. The author adds a server-side check plus a positive test and a rejection test. In the simulated continuation, user A reads their own report, and a request for organisation B's report is rejected.

After the new commit, the team does not rely on the earlier status: it checks the current head and base, the SHA the check ran against, the log and the changed code. A passing rendering test can stay green even though no test ever checked who owns the data; so the next piece of evidence has to cover the server-side access boundary, not just the screen.

Result. The test confirms only this particular boundary between two organisations, not every role and path.

Another option, and its limit. Access might be controlled by a shared server layer or by a database policy. In that case, check that control and the test that covers it; do not demand that protection be duplicated in every screen if the shared boundary is in force and confirmed by specific evidence.

Example 3: The agent declared the task done before deployment

Situation. An agent opened a draft PR for a pricing page, moved the Jira task to "Done" and cited a green build check. The workflow built the site, but there is no deployment for this revision. A mention of production in the documentation does not confirm that anything was launched.

Checking. Compare the target branch and the description with the diff; find out what the workflow built and which commit the check belongs to. Without a deployment record, the team reports that the site has not been deployed yet. A build is not a merge, a release or a deployment.

Action and result. When the PR is ready for formal review, the agent marks it Ready for review, moves the Jira task to the status defined by the team's workflow, and lists the steps not yet done. Jira statuses and evidence of completion are covered in more detail in Jira and the studio operating system. After the required review and merge, a person or an authorised workflow starts the deployment; the team checks the deployment record and verifies the pricing page. Only then can anyone say it has been deployed and verified in the chosen environment.

If the required deployment never ran, leave it in the report as an unfinished step and name who has to authorise or perform it. Do not move the Jira task to Done just because of a build: the status has to match the team's agreement on whether review, merge, deployment and verification of the user-facing result are required.

Another option, and its limit. Some teams deploy every change automatically after a merge; others require manual approval. Check the workflow and the run log: the word "automatically" or a green build does not prove that the required deployment job started with the right configuration or changed the system users actually see.

A short evidence pack for the reviewer

Before recommending acceptance, you can summarise a PR like this:

Question What evidence to look for
What is changing, and why? The PR description, the linked task, the scope of the change
What exactly did the author change? The diff of the head branch against the target (base)
Which code did the reviewer look at? The current head commit, the diff and the review actually submitted
Which revision did the workflow check? The full SHA of the commit the check ran on (head or test merge commit), its status for the current head and base, the log and, if needed, the artifact
Who reviewed the change? The submitted review and the mandatory-approval rule actually in force
What evidence is missing? Uncovered risks, dependencies, access rights, unfinished steps
Has the change been deployed? A deployment record for the specific environment and a check of the result

This pack separates what has been confirmed from what has not yet been proven. If the description says several jobs passed, but the page shows only old runs, or the check's source does not match the current head and base, record the discrepancy and re-run the necessary checks. A reviewer needs to see not just a green summary but the revision, the result and the limits of each piece of evidence.

A real example from a public PR

In the public PR pallets/flask #6095, the author proposed replacing uses of the private API of the pytest fixture monkeypatch with public calls. A reviewer advised keeping the fixture and scoping it with pytest.MonkeyPatch.context(); the author asked whether that would change how the patches are cleaned up. The PR was merged into stable on 30 July 2026. This is an example of one concrete discussion, not a universal recommendation; its CI statuses and any discussion outside the public history were not analysed here. The public history shows what was discussed, the author's response, the code change and the fact of the merge, but not necessarily every conversation or the motives behind the decision outside GitHub. So this case illustrates the shape of a review exchange; it does not prove that every team should act this way.

When the merge decision should wait

Hold back your recommendation to merge if:

  • the PR does not match the task, or a significant reviewer objection has gone unanswered;
  • a required review or check is still pending, or refers to an old revision;
  • the branch has a conflict or fails the up-to-date rule;
  • there is a leaked secret, an unexplained change in permissions, or the risk of an untested migration.

Name which fact is missing and who can confirm it. If you do not know the repository's rules, check them: a "ready to merge" label does not replace analysing the business or security risk.

Sources and further reading

  • GitHub Docs, Requesting a pull request review and Reviewing proposed changes in a pull request, current web pages with no stated publication date; accessed 23 September 2026. Used for the difference between inviting a reviewer and a submitted review, and for diffs, comments, suggestions and the three review types. The interface may change.
  • GitHub Docs, Managing and standardizing pull requests, current web page; accessed 23 September 2026. Used for PR templates, CODEOWNERS, protected branches and rulesets; the existence of an option does not show that it is switched on.
  • GitHub Docs, About protected branches and Available rules for rulesets, current pages; accessed 23 September 2026. Used for the rules on approvals, status checks, branch freshness, protection and bypass; availability depends on the plan and the specific configuration.
  • DevOps Research and Assessment (DORA), Google Cloud: DevOps capabilities and Continuous Delivery, current web pages; accessed 23 September 2026. Used for the meaning of the name DORA and to distinguish the ability to release on demand from automatic continuous deployment.
  • GitHub Docs, Workflows and Workflow artifacts, current web pages; accessed 23 September 2026. Used for the roles of workflows, jobs, runners and stored artifacts.
  • GitHub Docs, Events that trigger workflows and Troubleshooting required status checks, current web pages; accessed 23 September 2026. Used for the difference between the head commit and the test merge commit for pull_request, for tying a required check to the right revision, and for the requirement of an up-to-date base branch.
  • GitHub Docs, About code owners, current web page; accessed 23 September 2026. Used for the difference between a code owner's review request and a mandatory approval, which only rules can establish.
  • GitHub Docs, Use GITHUB_TOKEN for authentication in workflows, Secrets, Compromised runners and GitHub Actions runners, current web pages; accessed 23 September 2026. Used for the difference between the workflow token's permissions and an installed App's access, and for secrets, the limits of fork events and run environments. Masking a secret in logs is no guarantee of safe handling.
  • GitHub Docs, Deployment environments, current web page; accessed 23 September 2026 and re-checked 10 October 2026. Used for environments, branch restrictions, required reviewers, plans and repository visibility, and environment secrets; an environment's name does not prove a deployment succeeded.
  • Git, git-tag, current documentation; accessed 23 September 2026. Used to explain a tag as a named reference to a Git object.
  • GitHub Docs, About releases, current web page; accessed 23 September 2026. Used for how a GitHub Release relates to a tag and to additional notes or files.
  • GitHub Docs, About pull request merges, current web page; accessed 23 September 2026. Used for the consequences of merge commit, squash and rebase-and-merge.
  • IT Revolution, 24 Key Capabilities to Drive Improvement in Software Delivery, 1 September 2020; accessed 23 September 2026. The article states that it adapts an excerpt from Accelerate. Used for a narrow link between small changes, short feedback loops and the development process; it is no guarantee that a particular PR or automated check is correct.
  • Pallets Projects, Flask pull request #6095: Replace the use of private monkeypatch fixture API, public PR opened by mgorny, 14–30 July 2026; viewed 23 September 2026. Used only as an example of a concrete proposal, a reviewer's comment, the author's reply and a merge. The CI results were not checked for this analysis.