🌿

Git Professional

Run Git at scale: monorepos, release engineering, security, governance, hosting administration and CI/CD integration.

34 lessons 102 quiz questions
Lessons & quizzes Certificate

📚 Lessons & quizzes

Each lesson ends with its own short quiz. Answer them as you go — score 90% across all lessons to earn your certificate.

1 Monorepo vs Polyrepo: Strategies and Trade-offs

A monorepo stores many projects (services, libraries, apps) in a single Git repository; a polyrepo (multi-repo) gives each project its own repository. Neither is universally correct — the choice is about trade-offs.

  • Monorepo — atomic cross-project commits, one place to change shared code, unified tooling and CI, easy large-scale refactors. But the repo grows huge, requires scaling tooling, and needs strict ownership controls.
  • Polyrepo — small independent repos, clear boundaries, independent release cadence and access control. But cross-cutting changes span many PRs, and dependency/version drift is common.

Companies like Google and Meta run enormous monorepos with custom tooling; many open-source ecosystems remain polyrepo. Pick based on team size, coupling between projects, and the tooling you can invest in.

# A monorepo groups projects under one .git
monorepo/
  apps/web/
  apps/api/
  libs/shared/
  .git/        # single history for everything

2 Scaling Git to Huge Repositories: Partial Clone

As repositories grow to gigabytes, a full clone downloads every version of every file ever committed, which is slow and wasteful. Partial clone lets the server omit some objects and fetch them lazily on demand.

  • --filter=blob:none — a blobless clone skips file contents (blobs) until you actually check out or diff them. Commits and trees still come down.
  • --filter=tree:0 — a treeless clone also skips trees, downloading them on demand. Smaller still, but more round-trips for history operations.

Partial clone dramatically reduces initial clone size and time, trading some up-front download for occasional on-demand fetches. It requires server support (modern GitHub, GitLab and Azure DevOps support it).

# Blobless clone: fetch file contents on demand
git clone --filter=blob:none https://example.com/big-repo.git

# Treeless clone: even smaller, more lazy fetches
git clone --filter=tree:0 https://example.com/big-repo.git

3 Sparse-Checkout at Scale

Even with a full or partial clone, you rarely need every directory checked out into your working tree. Sparse-checkout populates only the paths you select, keeping the rest of the tree absent on disk.

The modern cone mode (git sparse-checkout set --cone) is optimised for directory-based patterns and performs far better than legacy pattern matching on large repos, because Git can reason about whole directories rather than evaluating every path against a pattern list.

Combined with a blobless partial clone, sparse-checkout means a developer working on one service in a giant monorepo only downloads and materialises that service’s files.

# Enable cone-mode sparse-checkout and pick directories
git sparse-checkout init --cone
git sparse-checkout set apps/web libs/shared

# See what is currently included
git sparse-checkout list

4 Scalar: Optimised Big-Repo Workflow

Scalar is a tool that ships with Git to configure a repository with the best-known settings for very large repos. Instead of manually enabling each optimisation, scalar clone sets them up for you.

  • Uses a partial (blobless) clone by default.
  • Enables sparse-checkout in cone mode.
  • Turns on background maintenance (commit-graph, pack repacking, prefetch).
  • Enables fsmonitor so status and other commands avoid scanning every file.

Scalar is essentially a curated bundle of the large-repo features Git already has, applied automatically so teams get good defaults without expert tuning.

# Clone and register a repo with large-repo optimisations
scalar clone https://example.com/giant-monorepo.git

# Register an existing clone with Scalar maintenance
scalar register

5 The Commit-Graph File

Operations like git log --graph, git merge-base and reachability checks must repeatedly walk commit history. The commit-graph is an auxiliary file that stores commit metadata (parents, generation numbers, commit dates) in a compact, indexed form so Git can answer these queries without parsing every commit object.

On large repos the commit-graph turns history traversals that took seconds into near-instant operations. Generation numbers let Git prune walks early when it can prove a commit cannot be an ancestor.

# Write/refresh the commit-graph for the current repo
git commit-graph write --reachable

# Enable automatic commit-graph writing
git config fetch.writeCommitGraph true

6 fsmonitor and Background Maintenance

Two features keep day-to-day Git fast on huge repositories.

  • fsmonitor watches the filesystem (via a built-in daemon or Watchman) and tells Git exactly which files changed, so git status does not have to lstat() every file in a tree of hundreds of thousands of paths.
  • Background maintenance (git maintenance start) runs scheduled tasks — incremental repacking, commit-graph updates, loose-object cleanup and prefetch — so the repository stays optimised without a developer manually running git gc.

Together they keep interactive commands snappy regardless of repository size.

# Enable the built-in filesystem monitor
git config core.fsmonitor true

# Schedule background maintenance tasks
git maintenance start

7 Monorepo Build Tooling: Nx, Turborepo and Bazel

Git stores a monorepo, but it does not build or test it intelligently. Monorepo build tools add a dependency graph and caching so CI only rebuilds what actually changed.

  • Nx and Turborepo — JavaScript/TypeScript-focused. They compute the affected project graph from changed files, run only affected tasks, and cache task outputs (often remotely shared across the team and CI).
  • Bazel — language-agnostic, hermetic builds with explicit dependency declarations and aggressive content-addressed caching; used for very large polyglot monorepos.

The shared idea is the affected/incremental build: use the change set plus a dependency graph to skip work whose inputs are unchanged.

# Nx: run a target only for projects affected by your changes
nx affected --target=test --base=origin/main

# Turborepo: run build with caching across the workspace
turbo run build

8 Semantic Versioning Policy

Semantic Versioning (SemVer) gives releases a meaningful MAJOR.MINOR.PATCH number so consumers can reason about compatibility.

  • MAJOR — incremented for incompatible (breaking) API changes.
  • MINOR — incremented for backward-compatible new functionality.
  • PATCH — incremented for backward-compatible bug fixes.

Pre-release identifiers (1.0.0-rc.1) and build metadata (+build.5) extend the scheme. A clear SemVer policy — documented and enforced — lets downstream teams pin dependencies and trust that a patch upgrade will not break them.

# Tag a release following SemVer
git tag -a v2.3.1 -m "Patch: fix null check in parser"

# Pre-release and build metadata are also valid
# v3.0.0-rc.1   v1.4.0+build.27

9 Conventional Commits

Conventional Commits is a lightweight convention for commit messages that machines can parse. The format is type(scope): description, optionally followed by a body and footers.

  • feat: a new feature (maps to a MINOR bump).
  • fix: a bug fix (maps to a PATCH bump).
  • A ! after the type or a BREAKING CHANGE: footer signals a MAJOR bump.
  • Other types: docs, chore, refactor, test, ci, etc.

Because the type carries semantic meaning, tooling can derive the next version number and generate changelogs automatically.

feat(parser): add support for nested arrays

fix(api): handle empty payload without throwing

feat(api)!: drop deprecated v1 endpoints

BREAKING CHANGE: the /v1 routes are removed

10 Enforcing Conventional Commits with commitlint

A convention that is not enforced quickly erodes. commitlint checks that commit messages follow a configured rule set (commonly config-conventional) and rejects messages that do not.

It is typically wired into a commit-msg Git hook (often via Husky) so a non-conforming message is blocked locally, and also run in CI as a backstop on pull requests. Enforcement at commit time gives fast feedback; the CI check guarantees nothing slips through regardless of a contributor’s local setup.

# .husky/commit-msg
npx --no -- commitlint --edit "$1"

# commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] };

11 Automated Releases with semantic-release

semantic-release automates the whole release process from Conventional Commits. On each run (usually in CI on the main branch) it:

  • Analyses commits since the last release to determine the next version (patch/minor/major).
  • Generates release notes and updates the changelog.
  • Tags the release in Git and publishes artifacts (e.g. to npm) and a GitHub/GitLab release.

The key principle: the version number is derived from commit history, not chosen by a human. This removes guesswork and makes releases reproducible. If no relevant commits exist, no release is published.

# .github/workflows/release.yml (excerpt)
- run: npx semantic-release
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

12 Branch Protection Rules

Branch protection rules guard important branches (like main) on a hosting platform. Typical protections include:

  • Require pull requests before merging (no direct pushes).
  • Require a minimum number of approving reviews.
  • Require status checks (CI, tests) to pass before merge.
  • Require branches to be up to date with the base before merging.
  • Restrict or forbid force pushes and branch deletion.

These are enforced by the host, not by Git itself, so they apply to everyone pushing through that platform. They are the backbone of code-quality and change governance.

# Protections are configured in the host UI/API, e.g. GitHub:
# - require pull request reviews (>= 1 approval)
# - require status checks to pass
# - dismiss stale approvals on new commits
# - block force pushes to main

13 CODEOWNERS and Required Reviews

A CODEOWNERS file maps file paths to the individuals or teams responsible for them. When a pull request touches a matching path, the host automatically requests a review from the listed owners, and — when combined with branch protection’s “require review from Code Owners” — their approval becomes mandatory before merge.

Patterns work like gitignore globs, and the last matching pattern wins. CODEOWNERS is how large organisations route reviews to the right experts and enforce that sensitive areas (security configs, infrastructure) are always reviewed by their owners.

# .github/CODEOWNERS
*               @org/core-team
/infra/         @org/platform
/security/      @org/appsec
*.sql           @org/dba

14 Signed Commits: GPG and SSH

Commit authorship metadata is trivially forgeable — anyone can set user.name and user.email to anyone. Cryptographic signing proves a commit or tag really came from the holder of a key.

  • GPG — the traditional approach; sign with a GPG key registered on the host.
  • SSH signing — reuse an existing SSH key (gpg.format ssh); simpler key management since teams already have SSH keys.

Hosts show a Verified badge when a signature matches a key registered to the author. Organisations can require signed commits via branch protection so unverified commits are rejected.

# Configure SSH-based commit signing
git config gpg.format ssh
git config user.signingkey ~/.ssh/id_ed25519.pub
git config commit.gpgsign true

# Sign a single commit explicitly
git commit -S -m "signed commit"

15 Keyless Signing with Sigstore gitsign

Managing long-lived GPG/SSH keys across an org is painful. Sigstore enables keyless signing: gitsign signs a commit using a short-lived certificate issued by Sigstore’s Fulcio CA after you authenticate with an OIDC identity provider (e.g. your GitHub or Google login).

The signing record is logged in the Rekor transparency log. Because the certificate is ephemeral, there is no private key to rotate, store or leak — identity is bound to your OIDC account at signing time. This fits cleanly into CI, where a workflow can sign with its OIDC token.

# Configure gitsign as the signing program
git config gpg.x509.program gitsign
git config gpg.format x509
git config commit.gpgsign true

# Commit; gitsign prompts an OIDC login and signs keylessly
git commit -m "keyless signed commit"

16 Preventing Secrets from Entering History

The cheapest secret leak is the one that never lands. Stop secrets before they are committed and as a CI backstop.

  • Pre-commit scanning — tools like gitleaks or detect-secrets run in a pre-commit hook and block a commit that contains high-entropy strings or known credential patterns.
  • CI scanning — the same scanners run on pull requests so nothing slips through if a developer skipped the local hook.
  • Host secret scanning + push protection — platforms can reject a push that introduces a recognised secret.

Defence in depth: local hooks give fast feedback, CI and host scanning guarantee coverage.

# Run gitleaks against staged changes (pre-commit)
gitleaks protect --staged --redact

# Scan full history in CI
gitleaks detect --source . --redact

17 Removing Leaked Secrets: Rotate First

If a secret reaches a repository, the first and most important step is to rotate (revoke and replace) the credential. Once a secret has been pushed — especially to a shared or public host — assume it is compromised: it may be in clones, forks, caches, CI logs and backups that you cannot rewrite.

Rewriting history to scrub the secret is still worthwhile to limit further exposure, but it does not make the leaked value safe. The correct order is: rotate the secret first, then rewrite history, then force everyone to re-clone, then verify the secret no longer works.

# Order of operations after a leak:
# 1) ROTATE the credential immediately (revoke + reissue)
# 2) Rewrite history to remove the value (filter-repo / BFG)
# 3) Force-push, then have collaborators re-clone
# 4) Confirm the old secret is now invalid

18 Rewriting History: git filter-repo and BFG

To purge a file or secret from all history you must rewrite commits. Two modern tools do this:

  • git filter-repo — the recommended, fast and flexible tool (replaces the deprecated filter-branch). It can remove paths, replace text, and prune empty commits.
  • BFG Repo-Cleaner — simpler and very fast for the common cases of deleting large files or replacing secret strings.

Rewriting changes commit SHAs for everything after the touched point, so all collaborators must re-clone or hard-reset. Remember from the previous lesson: rotate the secret first — history rewriting reduces exposure but never makes a leaked credential safe.

# Remove a file from all history with filter-repo
git filter-repo --path config/secrets.env --invert-paths

# Replace a secret string everywhere with BFG
bfg --replace-text passwords.txt repo.git

19 Git Hosting Administration: Orgs, Teams and Permissions

At scale, access is managed through organisations (or groups), teams and roles rather than per-user grants on each repo.

  • Organisations/Groups own repositories and billing and set org-wide policy.
  • Teams bundle members; you grant a team a permission level on a set of repos so adding a person to a team grants all the right access at once.
  • Roles (Read, Triage, Write, Maintain, Admin on GitHub; Guest…Owner on GitLab) define what each member can do.

The guiding principle is least privilege: grant the minimum role needed, prefer team-based grants over individual ones, and review access regularly.

# Example permission model (GitHub):
# Team: platform   -> Maintain on infra-* repos
# Team: appsec     -> Read on all repos, Write on security-* repos
# Individual admin grants avoided; use teams + least privilege

20 SSO, SAML and Access Control

Enterprises centralise identity so accounts are provisioned and de-provisioned in one place. SSO via SAML (or OIDC) ties repository-host access to the corporate identity provider (Okta, Entra ID, etc.).

  • A user must authenticate through the IdP to access org resources.
  • When someone leaves, disabling their IdP account immediately cuts off Git access — critical for offboarding.
  • SCIM can automatically provision and de-provision team membership.
  • Personal access tokens and SSH keys can be required to be SSO-authorised so they too respect the IdP.

This makes access governance enforceable and auditable across the whole organisation.

# Conceptual flow:
# 1) User hits org repo -> redirected to IdP (SAML/OIDC)
# 2) IdP authenticates -> host grants session
# 3) Tokens/SSH keys must be SSO-authorised to act on org repos
# 4) Offboarding: disable IdP account -> access revoked

21 Audit Logs and Compliance

Regulated and security-conscious organisations must answer “who did what, when?”. Hosting platforms provide audit logs recording events such as permission changes, branch-protection edits, secret access, repo creation/deletion and member changes.

  • Stream audit logs to a SIEM for retention, alerting and correlation.
  • Combine with signed commits and protected branches to produce a defensible change record.
  • Many frameworks (SOC 2, ISO 27001) expect evidence that changes are reviewed, attributed and traceable.

The Git history itself is part of the evidence, but governance also relies on the host’s tamper-evident audit trail of administrative actions.

# Typical audit-relevant events to monitor/stream:
# - branch protection rule changed
# - repository made public
# - team or member permission changed
# - personal access token created
# Stream these to a SIEM for retention and alerting

22 Protected Tags and Deployment Environments

Branches are not the only refs worth protecting.

  • Protected tags — release tags (e.g. v*) can be locked so they cannot be created, overwritten or deleted except by authorised users. This stops a published release tag from being silently moved to point at different code.
  • Deployment environments — hosts let you define environments (staging, production) with required reviewers, wait timers and environment-scoped secrets. A deploy to production can require manual approval, and its secrets are never exposed to other jobs.

Together these make the release-to-deploy path governed, not ad hoc.

# GitHub environment with manual approval (workflow excerpt)
jobs:
  deploy:
    environment:
      name: production    # required reviewers + scoped secrets
    steps:
      - run: ./deploy.sh

23 CI/CD Integration: Triggers, Shallow Clones and Caching

CI runs Git operations constantly, so efficient integration matters.

  • Triggers — pipelines run on events: pushes, pull requests, tags, schedules or manual dispatch. Tag triggers commonly drive releases.
  • Shallow clones--depth=1 fetches only the latest commit(s), speeding up checkout when full history is not needed.
  • Caching — cache dependencies and build outputs keyed by lockfile hashes to avoid redundant work.

Be careful: a shallow clone lacks history, so steps needing it (e.g. computing changed files against a base, or release tooling) must fetch-depth: 0 or unshallow first.

# GitHub Actions checkout (full history when needed)
- uses: actions/checkout@v4
  with:
    fetch-depth: 0    # 0 = full history; 1 = shallow

# Trigger on tags to drive a release
on:
  push:
    tags: ['v*']

24 OIDC: Keyless CI Authentication to Clouds

Storing long-lived cloud credentials as CI secrets is a major risk. OIDC lets a CI job exchange a short-lived, signed identity token for temporary cloud credentials — no static secret stored.

  • The CI provider issues a signed OIDC token describing the workflow, repo and branch/environment.
  • The cloud (AWS/GCP/Azure) trusts that issuer and, if the token’s claims match a configured policy, returns short-lived credentials.
  • You can scope trust to a specific repository and even a specific environment or branch.

This removes the most commonly leaked class of secret and ties access to the actual workload identity.

# GitHub Actions assuming an AWS role via OIDC
permissions:
  id-token: write
steps:
  - uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789012:role/ci-deploy
      aws-region: eu-north-1

25 GitOps: Git as the Source of Truth

GitOps applies Git workflows to operations: the desired state of infrastructure and applications is declared in a Git repository, and an automated agent continuously reconciles the live system to match that repository.

  • Git is the single source of truth; changes happen via pull requests, giving review, audit and easy rollback (just revert a commit).
  • A controller (e.g. Argo CD, Flux) pulls the declared state and applies it, detecting and correcting drift.

Because every change is a reviewed, attributed commit, GitOps inherits Git’s governance: history, blame, signatures and protected branches all apply to your infrastructure.

# A GitOps repo declares desired state; a controller reconciles it
clusters/prod/
  app-a/deployment.yaml
  app-b/deployment.yaml
# Argo CD / Flux watches this repo and applies changes,
# reverting any out-of-band drift back to the committed state

26 Large Binaries: Git LFS vs Artifact Stores

Git is built for text and performs poorly when large binaries are committed directly, because every version is stored forever and bloats every clone.

  • Git LFS (Large File Storage) replaces large files in the repo with small text pointers; the actual bytes live in a separate LFS store and are fetched on checkout. Good when binaries are genuinely part of the source and must be versioned alongside code.
  • Artifact stores (e.g. a package registry, S3, Artifactory) keep build outputs and large assets outside Git entirely, referenced by version. Better for derived artifacts that should not live in source control at all.

Policy rule of thumb: version source inputs (with LFS if large); publish derived artifacts to an artifact store.

# Track a binary type with Git LFS
git lfs track "*.psd"
git add .gitattributes design/logo.psd
git commit -m "Add logo via LFS"
# .gitattributes now contains:  *.psd filter=lfs diff=lfs merge=lfs -text

27 Repository Templates and Standards

Consistency across many repos comes from templates and shared standards, not manual setup each time.

  • Template repositories let new repos start with a curated baseline: directory layout, CI workflows, linters, license, CODEOWNERS and security policy.
  • Org-level defaults (e.g. a .github repo) provide default community health files (CONTRIBUTING, issue/PR templates, security policy) to every repo that lacks its own.
  • Standards as code — shared lint configs, reusable CI workflows and policy checks keep repos aligned as they evolve.

This reduces drift, speeds onboarding, and ensures every new project inherits the org’s guardrails.

# An org-wide .github repo supplies defaults to all repos:
.github/
  CONTRIBUTING.md
  PULL_REQUEST_TEMPLATE.md
  ISSUE_TEMPLATE/
  SECURITY.md
  workflows/   # reusable CI workflows

28 Onboarding: CONTRIBUTING and PR Templates

Clear contribution guidance lowers friction and raises quality.

  • CONTRIBUTING.md explains how to set up the project, branch and commit conventions, how to run tests, and the review/merge process.
  • Pull request templates pre-fill the PR description with a checklist (tests added, docs updated, breaking changes noted), nudging contributors to provide what reviewers need.
  • Issue templates structure bug reports and feature requests so triage is faster.
  • A CODE_OF_CONDUCT sets behavioural expectations, especially for open projects.

These documents encode tribal knowledge so newcomers can contribute correctly without asking a maintainer each time.

# .github/PULL_REQUEST_TEMPLATE.md
## What does this change?

## Checklist
- [ ] Tests added/updated
- [ ] Docs updated
- [ ] Breaking changes noted

29 Disaster Recovery: Backups and Mirrors

“Every clone is a backup” is only partly true — it omits server-side data (issues, PRs, settings, LFS objects, wikis) and may be stale. A real DR plan is deliberate.

  • Mirror clonesgit clone --mirror captures all refs; schedule regular git fetch on the mirror to keep it current and stored off-platform.
  • Bundlesgit bundle packages history into a single file for offline/cold storage.
  • Platform backups — export issues, PRs, permissions and settings via the host’s API, since those live outside Git.

Test restores periodically — an untested backup is a hope, not a plan.

# Create and refresh an off-platform mirror
git clone --mirror https://example.com/repo.git repo.git
cd repo.git && git fetch --prune

# Package history into a single bundle file
git bundle create repo.bundle --all

30 Migrating Hosts and from SVN with History

Moving repositories without losing history is a common enterprise task.

  • Host to host — a --mirror clone followed by a mirror push transfers all branches and tags. Issues, PRs and permissions need separate API-based migration since they are not in Git.
  • From SVNgit svn clone imports SVN history into Git, optionally with an authors map so SVN usernames become proper Git author identities. For large/complex migrations, dedicated importers (e.g. reposurgeon) handle branches/tags more faithfully.

Validate after migrating: compare commit counts, tags and key file checksums between source and destination.

# Move all refs between hosts
git clone --mirror https://old-host/repo.git
cd repo.git
git push --mirror https://new-host/repo.git

# Import SVN history with an authors map
git svn clone --stdlayout --authors-file=authors.txt https://svn/repo

31 Force-Push Incidents and Reflog Recovery

A bad push --force can overwrite commits on a shared branch. Recovery hinges on the fact that Git rarely deletes objects immediately.

  • Refloggit reflog records where refs pointed locally; the overwritten commit is usually still reachable by its old SHA, so you can reset or re-push it. Note the reflog is local, so recovery is easiest from a clone that still has the old tip.
  • Prevention — require --force-with-lease (which refuses if the remote moved unexpectedly), forbid force pushes on protected branches, and keep mirrors so a server tip is recoverable.

Because force-push damage is often unrecoverable on the server alone, prevention plus off-platform mirrors is the real safety net.

# Find the lost commit and restore the branch
git reflog
git reset --hard <old-sha>

# Safer force that refuses if the remote moved unexpectedly
git push --force-with-lease

32 Submodule and Subtree Governance

Sharing code across repos is often done with submodules or subtrees, and each needs governance.

  • Submodules embed another repo as a pinned commit reference. They keep histories separate and pin an exact version, but contributors must git submodule update --init, and forgetting to bump or commit the pointer causes confusion. Govern by documenting update procedures and pinning to reviewed commits.
  • Subtrees merge another project’s history into a subdirectory of your repo. No extra clone step, but the host repo grows and updates use git subtree pull.

Choose deliberately: submodules for clean separation and exact pinning; subtrees for a single self-contained clone. Document the chosen workflow so the team handles updates consistently.

# Submodule: add and pin a dependency repo
git submodule add https://example.com/lib.git vendor/lib
git submodule update --init --recursive

# Subtree: pull updates into a subdirectory
git subtree pull --prefix=vendor/lib https://example.com/lib.git main --squash

33 Repository Maintenance and Performance Policy

Large, long-lived repos degrade without upkeep. A maintenance policy defines what runs, when, and how performance is monitored.

  • Repacking and gc — consolidate loose objects into packs and drop unreachable ones (typically via scheduled git maintenance rather than ad hoc git gc).
  • Commit-graph upkeep — keep the commit-graph current for fast history operations.
  • Monitoring — track clone time, repo size, pack count and fetch latency; alert on regressions.
  • Hygiene rules — ban large-binary commits (use LFS/artifact stores), prune stale branches, and keep history clean.

Treating repo health as an SLO keeps developer experience fast as the codebase grows.

# Audit what is bloating the repo
git count-objects -vH

# Schedule maintenance instead of ad hoc gc
git maintenance start
git maintenance run --task=incremental-repack

34 Documentation and Non-Code Repositories

Git is not only for application source. Treating other assets as code (docs-as-code, everything-as-code) brings the same governance — review, history, and CI — to non-code content.

  • Documentation — Markdown docs in Git get PR review, versioning and link-checking/build pipelines (static site generators publish on merge).
  • Configuration and policy — infrastructure, runbooks and policies live in Git so changes are reviewed and auditable.
  • Considerations — for binary-heavy content (images, designs) apply the large-binary policy; non-technical authors may need a friendlier editing layer (a CMS or web editor that commits on their behalf).

The payoff is one consistent change-management model across code and everything around it.

# Docs-as-code: build and publish on merge to main
on:
  push:
    branches: [main]
jobs:
  docs:
    steps:
      - run: mkdocs build && ./publish.sh

🎓 Certificate of Completion

🔒 Complete every lesson quiz above with 90%+ to unlock your downloadable certificate.