All topics
General · Learning hub

GitHub notes for developers

Master GitHub with a curated set of 10 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallMore General notes
GitHub

GitHub Essentials

GitHub Essentials GitHub is the de facto platform for hosting Git repositories, collaborating on code, and managing software projects. This page covers the core

GitHub Essentials

GitHub is the de facto platform for hosting Git repositories, collaborating on code, and managing software projects. This page covers the core workflows every developer uses daily.

Repositories & Forks

A repository (repo) is a project container holding all files and history. Forking creates your own copy of someone else's repo under your account, enabling you to propose changes via pull requests.

# Create a new repo on GitHub via CLI
gh repo create my-project --public --clone
gh repo create my-project --private --clone

# Fork a repo and clone your fork
gh repo fork owner/repo --clone
cd repo
git remote -v
# origin    https://github.com/YOU/repo.git (fetch)
# upstream  https://github.com/owner/repo.git (fetch)

# Sync fork with upstream
git fetch upstream
git checkout main
git merge upstream/main
git push origin main

# Clone with specific depth (faster for large repos)
git clone --depth 1 https://github.com/owner/repo.git

# View repo info
gh repo view owner/repo
gh repo view owner/repo --web   # Open in browser

Pull Requests

Pull Requests (PRs) are the primary mechanism for proposing changes. They enable code review, discussion, CI checks, and controlled merging.

# Standard PR workflow
git checkout -b feature/user-auth
# ... make changes ...
git add -p                              # Stage hunks interactively
git commit -m "feat: add JWT authentication"
git push -u origin feature/user-auth

# Create PR via CLI
gh pr create --title "Add JWT authentication" \
  --body "Implements RS256 JWT auth with refresh tokens" \
  --base main \
  --reviewer alice,bob \
  --label "feature,auth"

# List and view PRs
gh pr list
gh pr list --state all --author "@me"
gh pr view 42
gh pr view 42 --web

# Check out someone else's PR locally
gh pr checkout 42

# Merge a PR
gh pr merge 42 --squash --delete-branch
gh pr merge 42 --rebase
gh pr merge 42 --merge   # Merge commit

# Review a PR
gh pr review 42 --approve
gh pr review 42 --request-changes --body "Please add tests"

Issues

# Create an issue
gh issue create --title "Button not rendering on Safari" \
  --body "Steps to reproduce..." \
  --label "bug" \
  --assignee "@me"

# List issues
gh issue list
gh issue list --label "bug" --state open
gh issue list --assignee "@me"

# View and close issues
gh issue view 15
gh issue close 15 --comment "Fixed in PR #42"
gh issue reopen 15

# Link issues to PRs in commit messages / PR bodies
# GitHub auto-closes issues when PR merges:
# Closes #15, Fixes #16, Resolves #17

Code Review Workflow

Effective code review is a skill. GitHub provides inline comments, suggestion blocks, and required review policies via branch protection rules.

# Branch protection rules (set via GitHub UI or API):
# - Require pull request reviews before merging
# - Require status checks (CI) to pass
# - Require branches to be up to date
# - Require signed commits
# - Restrict who can push to main

# Best practices for reviewers:
# 1. Review in small chunks (< 400 lines per PR)
# 2. Use "suggestion" blocks for concrete code changes
# 3. Distinguish blocking (must fix) vs non-blocking (nit) comments
# 4. Approve only when all concerns are addressed

# CODEOWNERS file - auto-assign reviewers based on file paths
# File: .github/CODEOWNERS
# * @org/core-team              # Default owner for all files
# /src/auth/ @alice @bob        # Auth team owns auth directory
# *.ts @org/frontend-team       # TS files → frontend team
# /infra/ @org/devops           # Infra files → devops team

# Draft PRs - mark as not ready for review
gh pr create --draft --title "WIP: Refactor auth module"
# Convert draft to ready
gh pr ready 42

Releases & Tags

# Create a release
gh release create v1.2.0 \
  --title "v1.2.0 - Performance improvements" \
  --notes "## Changes
- Reduced bundle size by 30%
- Fixed memory leak in data fetcher" \
  --latest

# Create release from tag with auto-generated notes
gh release create v1.2.0 --generate-notes

# Upload assets to a release
gh release upload v1.2.0 ./dist/app-linux-amd64 ./dist/app-darwin-amd64

# List releases
gh release list

# View latest release
gh release view
GitHub

GitHub Actions

GitHub Actions GitHub Actions is a CI/CD platform built directly into GitHub. Workflows are YAML files in `.github/workflows/` that run automatically on trigger

GitHub Actions

GitHub Actions is a CI/CD platform built directly into GitHub. Workflows are YAML files in `.github/workflows/` that run automatically on triggers like pushes, PRs, schedules, or manual dispatch.

Workflow Anatomy

# .github/workflows/ci.yml
name: CI

# Triggers
on:
  push:
    branches: [main, develop]
    paths-ignore: ['**.md', 'docs/**']
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]
  schedule:
    - cron: '0 9 * * 1'   # Every Monday at 9am UTC
  workflow_dispatch:       # Manual trigger from GitHub UI
    inputs:
      environment:
        description: 'Target environment'
        required: true
        default: 'staging'
        type: choice
        options: [staging, production]

# Environment variables available to all jobs
env:
  NODE_VERSION: '20'
  REGISTRY: ghcr.io

jobs:
  test:
    name: Run Tests
    runs-on: ubuntu-latest
    # runs-on: [self-hosted, linux, x64]   # Self-hosted runner

    # Job-level environment
    env:
      DATABASE_URL: postgresql://localhost/test

    # Services (Docker containers)
    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: postgres
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint

      - name: Run tests
        run: npm test -- --coverage
        env:
          CI: true

      - name: Upload coverage
        uses: actions/upload-artifact@v4
        with:
          name: coverage-report
          path: coverage/
          retention-days: 7

Secrets & Contexts

# Secrets are set in: Repo → Settings → Secrets and variables → Actions
# Access via ${{ secrets.SECRET_NAME }}

# Common contexts:
# ${{ github.sha }}          - commit SHA
# ${{ github.ref }}          - branch/tag ref  (refs/heads/main)
# ${{ github.ref_name }}     - branch name (main)
# ${{ github.actor }}        - user who triggered the workflow
# ${{ github.repository }}   - owner/repo
# ${{ github.event_name }}   - push, pull_request, etc.
# ${{ runner.os }}           - Linux, Windows, macOS
# ${{ job.status }}          - success, failure, cancelled

steps:
  - name: Deploy to production
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    run: |
      curl -X POST https://api.vercel.com/v1/integrations/deploy \
        -H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \
        -d '{"target": "production"}'
    env:
      DEPLOY_ENV: production

  - name: Notify Slack on failure
    if: failure()
    uses: slackapi/slack-github-action@v1.26.0
    with:
      payload: |
        {
          "text": "Build failed on ${{ github.ref_name }} by ${{ github.actor }}"
        }
    env:
      SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Test Matrix

# Run tests across multiple Node versions and OSes in parallel
jobs:
  test:
    strategy:
      fail-fast: false   # Don't cancel other jobs on one failure
      matrix:
        node-version: [18, 20, 22]
        os: [ubuntu-latest, windows-latest, macos-latest]
        exclude:
          - os: windows-latest
            node-version: 18   # Skip Node 18 on Windows
        include:
          - os: ubuntu-latest
            node-version: 20
            coverage: true     # Only upload coverage for this combo

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test
      - name: Upload coverage
        if: matrix.coverage
        uses: codecov/codecov-action@v4

Deploy Workflow (Next.js to Vercel)

# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      deployments: write

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - run: npm ci

      # Cache build output
      - uses: actions/cache@v4
        with:
          path: |
            .next/cache
          key: ${{ runner.os }}-nextjs-${{ hashFiles('package-lock.json') }}-${{ hashFiles('**/*.ts', '**/*.tsx') }}
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('package-lock.json') }}-

      - run: npm run build
        env:
          NEXT_PUBLIC_API_URL: ${{ vars.NEXT_PUBLIC_API_URL }}

      - name: Deploy to Vercel
        run: |
          npx vercel --prod \
            --token ${{ secrets.VERCEL_TOKEN }} \
            --scope ${{ vars.VERCEL_ORG_ID }}
        env:
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
          VERCEL_ORG_ID: ${{ vars.VERCEL_ORG_ID }}

Reusable Workflows & Composite Actions

# Reusable workflow: .github/workflows/reusable-test.yml
on:
  workflow_call:
    inputs:
      node-version:
        required: false
        type: string
        default: '20'
    secrets:
      DATABASE_URL:
        required: true

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
      - run: npm ci && npm test
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}

---
# Caller workflow
jobs:
  run-tests:
    uses: ./.github/workflows/reusable-test.yml
    with:
      node-version: '22'
    secrets:
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
GitHub

GitHub CLI & API

GitHub CLI & API The GitHub CLI (`gh`) brings GitHub workflows into the terminal. The REST and GraphQL APIs enable automation, scripts, and integrations. Togeth

GitHub CLI & API

The GitHub CLI (`gh`) brings GitHub workflows into the terminal. The REST and GraphQL APIs enable automation, scripts, and integrations. Together they eliminate context-switching to the browser.

GitHub CLI Installation & Auth

# Install
brew install gh                     # macOS
winget install GitHub.cli            # Windows
sudo apt install gh                  # Ubuntu/Debian

# Authenticate
gh auth login                        # Interactive (browser or token)
gh auth login --with-token < token.txt
gh auth status                       # Check current auth
gh auth refresh -s write:packages   # Add extra scopes
gh auth logout

# Config
gh config set editor nvim
gh config set git_protocol ssh
gh config list

CLI: PRs, Issues, Repos

# --- Pull Requests ---
gh pr list --state open --base main
gh pr create --fill                  # Use commit info for title/body
gh pr diff 42
gh pr checks 42                      # Show CI status
gh pr comment 42 --body "LGTM! Merging tomorrow"
gh pr edit 42 --add-label "urgent" --add-reviewer carol
gh pr merge 42 --auto --squash       # Auto-merge when checks pass
gh pr close 42

# --- Issues ---
gh issue create --template bug_report.md
gh issue list --label "bug" --assignee "@me" --limit 20
gh issue edit 15 --title "Updated title" --add-assignee dave
gh issue transfer 15 owner/other-repo
gh issue lock 15 --reason resolved

# --- Repos ---
gh repo list --limit 30 --source     # Only non-forks
gh repo clone owner/repo
gh repo create --template owner/template-repo my-new-project
gh repo rename my-new-name
gh repo archive
gh repo delete owner/old-repo --yes
gh repo set-default owner/repo      # Set default repo for directory

# --- Releases ---
gh release list
gh release download v2.0.0 --pattern "*.tar.gz"
gh release delete v1.0.0-beta --yes

REST API with curl

# Base URL: https://api.github.com
# Auth header: -H "Authorization: Bearer TOKEN"

TOKEN=$(gh auth token)   # Get current token

# Get repo info
curl -s -H "Authorization: Bearer $TOKEN" \
  https://api.github.com/repos/owner/repo | jq .full_name

# List open PRs
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://api.github.com/repos/owner/repo/pulls?state=open&per_page=20" \
  | jq '.[].title'

# Create an issue
curl -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Bug: login fails","body":"Steps...","labels":["bug"]}' \
  https://api.github.com/repos/owner/repo/issues

# Get workflow run logs
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://api.github.com/repos/owner/repo/actions/runs?status=failure" \
  | jq '.workflow_runs[0] | {id, name, conclusion, head_branch}'

# Trigger a workflow dispatch
curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ref":"main","inputs":{"environment":"staging"}}' \
  https://api.github.com/repos/owner/repo/actions/workflows/deploy.yml/dispatches

# Pagination with Link header
curl -s -I -H "Authorization: Bearer $TOKEN" \
  "https://api.github.com/repos/owner/repo/issues?per_page=100" \
  | grep -i link   # Shows next/last page URLs

GraphQL API

# GitHub GraphQL endpoint: https://api.github.com/graphql
# Use gh api graphql for convenience

# Query PR details with reviews
gh api graphql -f query='
  query($owner: String!, $repo: String!, $number: Int!) {
    repository(owner: $owner, name: $repo) {
      pullRequest(number: $number) {
        title
        state
        additions
        deletions
        reviewDecision
        reviews(last: 5) {
          nodes {
            author { login }
            state
            body
          }
        }
        commits(last: 1) {
          nodes {
            commit {
              statusCheckRollup {
                state
              }
            }
          }
        }
      }
    }
  }
' -F owner=myorg -F repo=myrepo -F number=42

# Get all repos in org with stars
gh api graphql --paginate -f query='
  query($endCursor: String) {
    organization(login: "myorg") {
      repositories(first: 100, after: $endCursor) {
        nodes { name stargazerCount }
        pageInfo { hasNextPage endCursor }
      }
    }
  }
' | jq '.data.organization.repositories.nodes[] | select(.stargazerCount > 10)'

Useful CLI Aliases & Scripts

# Create gh aliases
gh alias set prc 'pr create --fill'
gh alias set prl 'pr list --state open'
gh alias set cleanup '!git branch --merged | grep -v main | xargs git branch -d'

# Run aliases
gh prc
gh prl
gh cleanup

# List all aliases
gh alias list

# Useful one-liners
# Open current branch's PR in browser
gh pr view --web

# Show CI status for current branch
gh pr checks

# Watch Actions run in real time
gh run watch

# Download artifact from latest run
gh run download --name coverage-report

# List failed runs for a workflow
gh run list --workflow ci.yml --status failure --limit 5
GitHub

GitHub Interview Questions

GitHub Interview Questions Common questions about GitHub, CI/CD, and collaboration workflows. These come up in DevOps, backend, and full-stack engineering inter

GitHub Interview Questions

Common questions about GitHub, CI/CD, and collaboration workflows. These come up in DevOps, backend, and full-stack engineering interviews.

1. What is the difference between Git and GitHub?

Git is the open-source distributed version control system - it tracks file changes, manages branches, and stores history locally on your machine. GitHub is a cloud hosting platform built on top of Git that adds collaboration features: pull requests, code review, Issues, Actions (CI/CD), wikis, project boards, and security scanning. You can use Git without GitHub (self-hosted GitLab, Bitbucket, or no remote at all), but GitHub requires Git. Other comparable platforms are GitLab and Bitbucket.

2. What is a Pull Request and how does it differ from a git merge?

A Pull Request is a GitHub UI feature that wraps a proposed merge with a discussion thread, code review, and CI checks. It is a collaboration workflow, not a Git primitive. A git merge is the underlying Git operation that actually integrates branches - it can be done directly on the command line with no review. PRs are mandatory in team environments because they enforce review policies, create an audit trail, trigger automated checks, and allow discussion before code lands in the main branch.

3. What are branch protection rules and why are they important?

Branch protection rules (Settings → Branches → Protection rules) prevent direct pushes to critical branches like main. Common rules: require PR reviews before merging (N approvals), require status checks (CI must pass), require branches to be up to date before merging, restrict who can push, require signed commits, and prevent force pushes. They enforce team standards automatically rather than relying on discipline, preventing accidental breakage of the main branch and ensuring every change goes through review and CI.

4. How do GitHub Actions compare to Jenkins?

GitHub Actions is GitHub-native CI/CD: zero setup, YAML-based workflows versioned with the code, generous free tier (2000 min/month), and a massive marketplace of pre-built actions. Jenkins is a self-hosted, plugin-based Java CI server with maximum flexibility but significant operational overhead (you manage the server, plugins, agents). Actions is better for most teams starting fresh; Jenkins is preferred when you need deep customization, have an existing investment, need to run on-prem for security reasons, or require pipelines that span multiple SCM systems.

5. What is GitHub Packages and how is it used?

GitHub Packages is a package hosting service integrated with GitHub repos and Actions. It supports npm, Docker (Container Registry at ghcr.io), Maven, NuGet, RubyGems, and Gradle. You publish artifacts (Docker images, npm packages) to it as part of your CI pipeline, then pull them in deployments. The advantage is tight integration - packages are linked to repos, access is controlled by repository permissions, and you can reference package versions directly in Actions workflows without managing a separate registry.

6. How do you prevent secrets from being exposed in a GitHub repo?

Best practices: (1) Never commit secrets - use GitHub Secrets (encrypted, not visible in logs) for Actions. (2) Add .env to .gitignore before the first commit. (3) Enable GitHub secret scanning - it automatically detects leaked API keys, tokens, and credentials from 100+ providers and alerts you. (4) Use environment-scoped secrets so production credentials are only available to deployment workflows. (5) If a secret is leaked, rotate it immediately - assume it is compromised. (6) Use tools like git-secrets, truffleHog, or Gitleaks in pre-commit hooks.

7. What is the CODEOWNERS file and how does it work?

The CODEOWNERS file (located at .github/CODEOWNERS, CODEOWNERS, or docs/CODEOWNERS) maps file paths to GitHub users or teams who own that code. When a PR touches an owned file, GitHub automatically requests reviews from the designated owners. Format: glob patterns followed by @user or @org/team. Combined with branch protection's "Require review from Code Owners" setting, this enforces that relevant experts always review changes in their area - preventing a frontend developer from merging infrastructure changes without DevOps sign-off.

8. What are GitHub Environments and when would you use them?

GitHub Environments (Settings → Environments) are named deployment targets (staging, production, etc.) with their own secrets, variables, and protection rules. Environment-scoped secrets override repo-level secrets for jobs targeting that environment. Protection rules include: required reviewers (a human must approve the deployment), wait timers (delay before deployment), and branch restrictions (only certain branches can deploy to production). Use them to model your staging/prod pipeline - the production environment can require manual approval and have tighter secrets access than staging.

9. How does GitHub handle monorepos for CI/CD?

GitHub Actions has path filters to only trigger workflows when relevant files change: use `on.push.paths` and `on.pull_request.paths` to scope triggers to specific directories. For example, a change in `packages/api/` only triggers the API workflow. For larger monorepos, tools like Turborepo or Nx integrate with Actions to run only affected package tests. The `actions/cache` action is critical in monorepos to share build caches across jobs and runs. Another pattern is using `workflow_call` to define per-package reusable workflows called from a root orchestrator workflow.

10. What is GitHub Dependabot and how does it help with security?

Dependabot is GitHub's automated dependency management tool. It has two main modes: Dependabot alerts scan your dependency manifest (package.json, requirements.txt, etc.) against the GitHub Advisory Database and alert you to known vulnerabilities (CVEs). Dependabot security updates automatically open PRs to bump vulnerable dependencies to patched versions. Dependabot version updates (configured via .github/dependabot.yml) can also keep all dependencies up-to-date on a schedule, not just vulnerable ones. It supports npm, pip, Maven, Gradle, Cargo, Go modules, and more.

GitHub

Repositories, Forks & Pull Requests

GitHub: Repositories, Forks & Pull Requests GitHub is the dominant platform for hosting Git repositories, collaborating on code, and managing software projects.

GitHub: Repositories, Forks & Pull Requests

GitHub is the dominant platform for hosting Git repositories, collaborating on code, and managing software projects. Pull Requests are the primary collaboration mechanism.

Repository Setup

# Create repo via GitHub CLI
gh repo create my-project --public --clone
gh repo create my-org/my-project --private --clone

# Clone a repo
git clone https://github.com/owner/repo.git
gh repo clone owner/repo   # same but uses gh auth

# View repo info
gh repo view owner/repo
gh repo view owner/repo --web   # open in browser

# Set remote for existing local repo
git remote add origin https://github.com/owner/repo.git
git push -u origin main

Forking Workflow

# Fork and clone
gh repo fork owner/repo --clone
cd repo
git remote -v
# origin    https://github.com/YOU/repo.git
# upstream  https://github.com/owner/repo.git

# Sync fork with upstream
git fetch upstream
git checkout main
git merge upstream/main   # or: git rebase upstream/main
git push origin main

# Keep fork branch up to date
git checkout feature/my-work
git rebase upstream/main

Pull Requests

# Create PR
git checkout -b feature/user-auth
# ... make changes ...
git add -p   # stage interactively
git commit -m "feat: add JWT authentication"
git push -u origin feature/user-auth

gh pr create \
  --title "Add JWT authentication" \
  --body "Implements RS256 JWT auth with refresh tokens. Closes #42" \
  --base main \
  --reviewer alice,bob \
  --label "feature,auth" \
  --assignee "@me"

# List and view PRs
gh pr list
gh pr list --state all --author "@me"
gh pr list --label "needs-review"
gh pr view 42
gh pr view 42 --web

# Check out someone else's PR locally
gh pr checkout 42

# Update PR
gh pr edit 42 --title "New title" --add-label "ready"

# Merge
gh pr merge 42 --squash --delete-branch
gh pr merge 42 --rebase
gh pr merge 42 --merge

# Review
gh pr review 42 --approve
gh pr review 42 --request-changes --body "Please add tests for edge cases"

Branch Protection Rules

  • Require pull request before merging: nobody can push directly to main

  • Require status checks: CI must pass before merge

  • Require branches to be up to date before merging: no stale PRs

  • Require conversation resolution: all review comments must be resolved

  • Restrict who can push: limit direct pushes to specific people/teams

  • Require signed commits: all commits must be GPG-signed

  • Do not allow force pushes: prevents --force on protected branch

PR Templates

<!-- .github/pull_request_template.md -->
## Summary
<!-- What does this PR do? -->

## Changes
-
-

## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests pass
- [ ] Tested locally

## Screenshots (if UI changes)

## Related Issues
Closes #

## Checklist
- [ ] Code reviewed by self
- [ ] No sensitive data exposed
- [ ] Documentation updated if needed
GitHub

GitHub Actions & CI/CD

GitHub Actions & CI/CD GitHub Actions automates workflows triggered by GitHub events. Workflows are YAML files in .github/workflows/. Each workflow has jobs, jo

GitHub Actions & CI/CD

GitHub Actions automates workflows triggered by GitHub events. Workflows are YAML files in .github/workflows/. Each workflow has jobs, jobs have steps.

Workflow Structure

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
    paths-ignore: ['**.md', 'docs/**']
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]
  schedule:
    - cron: '0 9 * * 1'      # Every Monday 9am UTC
  workflow_dispatch:           # Manual trigger
    inputs:
      environment:
        description: 'Target environment'
        type: choice
        options: [staging, production]
        default: staging

env:
  NODE_VERSION: '20'

jobs:
  test:
    name: Run Tests
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_PASSWORD: postgres
        ports: ['5432:5432']
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - run: npm ci

      - run: npm run lint

      - run: npm test -- --coverage
        env:
          DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test

      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: coverage-report
          path: coverage/
          retention-days: 7

Secrets & Contexts

# Secrets: Settings → Secrets and variables → Actions
# Access: ${{ secrets.SECRET_NAME }}

# Useful built-in contexts
# ${{ github.sha }}          — commit SHA
# ${{ github.ref_name }}     — branch name (main)
# ${{ github.actor }}        — who triggered the workflow
# ${{ github.repository }}   — owner/repo
# ${{ github.event_name }}   — push, pull_request, etc.
# ${{ runner.os }}           — Linux, Windows, macOS
# ${{ job.status }}          — success, failure, cancelled

steps:
  - name: Deploy to production
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    run: npx vercel --prod --token ${{ secrets.VERCEL_TOKEN }}

  - name: Notify on failure
    if: failure()
    uses: slackapi/slack-github-action@v1
    with:
      payload: |
        {"text": "Build failed on ${{ github.ref_name }} by ${{ github.actor }}"}
    env:
      SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Matrix Builds

jobs:
  test:
    strategy:
      fail-fast: false
      matrix:
        node: [18, 20, 22]
        os: [ubuntu-latest, windows-latest]
        exclude:
          - os: windows-latest
            node: 18
        include:
          - os: ubuntu-latest
            node: 20
            coverage: true

    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci && npm test
      - if: matrix.coverage
        run: npm run coverage:upload

Caching

- uses: actions/cache@v4
  with:
    path: |
      ~/.npm
      .next/cache
    key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}-${{ hashFiles('**/*.ts','**/*.tsx') }}
    restore-keys: |
      ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}-
      ${{ runner.os }}-node-

Reusable Workflows & Composite Actions

# .github/workflows/reusable-deploy.yml
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
    secrets:
      VERCEL_TOKEN:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - uses: actions/checkout@v4
      - run: npx vercel --prod --token ${{ secrets.VERCEL_TOKEN }}

# Call from another workflow:
# jobs:
#   deploy:
#     uses: ./.github/workflows/reusable-deploy.yml
#     with:
#       environment: production
#     secrets:
#       VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}

Releases & Tags

# .github/workflows/release.yml
on:
  push:
    tags: ['v*']

jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - uses: softprops/action-gh-release@v2
        with:
          files: dist/*.zip
          generate_release_notes: true
GitHub

Issues, Projects & Discussions

GitHub: Issues, Projects & Discussions Issue Templates # .github/ISSUE_TEMPLATE/bug_report.yml name: Bug Report description: Something isn't working labels: [bu

GitHub: Issues, Projects & Discussions

Issue Templates

# .github/ISSUE_TEMPLATE/bug_report.yml
name: Bug Report
description: Something isn't working
labels: [bug, needs-triage]
assignees: [lead-dev]
body:
  - type: markdown
    attributes:
      value: "Thanks for taking the time to report a bug!"

  - type: input
    id: version
    attributes:
      label: Version
      placeholder: "1.2.3"
    validations:
      required: true

  - type: textarea
    id: description
    attributes:
      label: What happened?
      description: A clear description of the bug
    validations:
      required: true

  - type: textarea
    id: steps
    attributes:
      label: Steps to reproduce
      value: |
        1. Go to '...'
        2. Click on '...'
        3. See error

  - type: dropdown
    id: severity
    attributes:
      label: Severity
      options: [Critical, High, Medium, Low]
    validations:
      required: true

Issues via CLI

# Create issues
gh issue create --title "Bug: login fails on mobile" --body "..." --label bug
gh issue create --title "Feature: dark mode" --template "feature_request.yml"

# List and view
gh issue list
gh issue list --state open --label "bug" --assignee "@me"
gh issue view 42
gh issue view 42 --web

# Update and close
gh issue edit 42 --add-label "in-progress" --remove-label "needs-triage"
gh issue close 42 --comment "Fixed in #45"
gh issue reopen 42

GitHub Projects v2

Projects v2 is a spreadsheet-like project management tool with Board, Table, and Roadmap views. Supports custom fields, automation, and grouping.

  • Views: Board (kanban), Table (spreadsheet), Roadmap (timeline)

  • Custom fields: text, number, date, single select, iteration (sprints)

  • Automation: auto-assign status when PR is opened/merged, auto-archive closed issues

  • Insights: burn-up/burn-down charts based on custom fields

  • Cross-repo: a single Project can span multiple repositories

  • Linked to issues and PRs via "Projects" field in the issue sidebar

# Add issue to project
gh project item-add PROJECT_NUMBER --owner org --url ISSUE_URL

# List project items
gh project item-list PROJECT_NUMBER --owner org

# Update a project field
gh project item-edit --project-id PVT_xxx --id ITEM_ID \
  --field-id FIELD_ID --single-select-option-id OPTION_ID

CODEOWNERS

# .github/CODEOWNERS
# Auto-assign reviewers based on changed files

# Default owners for everything
*                   @myorg/core-team

# Frontend
/src/components/    @myorg/frontend-team
/src/styles/        @alice @bob
*.css               @alice

# Backend
/src/api/           @myorg/backend-team
/src/db/            @charlie

# Infrastructure
/terraform/         @myorg/devops
/.github/           @myorg/devops
Dockerfile          @myorg/devops

# Documentation
/docs/              @myorg/docs-team
*.md                @myorg/docs-team

Discussions

  • Discussions: forum-style Q&A and announcements separate from Issues

  • Categories: Q&A (mark answers), Announcements, Ideas, Show and Tell, General

  • Convert issue to discussion (and vice versa) — useful for feature requests

  • Pin discussions to the top of the Discussions tab

  • Lock discussion: prevent new comments while keeping it visible

  • GitHub Discussions API: automate creation via GraphQL

GitHub

GitHub CLI & REST/GraphQL API

GitHub CLI & REST/GraphQL API gh CLI Setup & Auth # Install (macOS) brew install gh # Authenticate gh auth login gh auth login --with-token <<< "$GITHUB_TOKEN"

GitHub CLI & REST/GraphQL API

gh CLI Setup & Auth

# Install (macOS)
brew install gh

# Authenticate
gh auth login
gh auth login --with-token <<< "$GITHUB_TOKEN"  # for CI

# Check status
gh auth status

# Switch account
gh auth switch --user alice

Core gh Commands

# Repos
gh repo create, clone, fork, view, list, rename, delete, archive

# PRs
gh pr create, list, view, checkout, merge, close, reopen, edit, review, diff, ready

# Issues
gh issue create, list, view, close, reopen, edit, delete, pin

# Workflows
gh workflow list
gh workflow run deploy.yml --field environment=staging
gh run list --workflow=ci.yml
gh run view 12345
gh run watch 12345   # stream logs in real time
gh run rerun 12345 --failed

# Releases
gh release create v1.2.0 dist/*.zip --generate-notes --latest
gh release list
gh release download v1.2.0

# Gists
gh gist create script.sh --public
gh gist list

REST API via gh api

# GET request
gh api repos/owner/repo
gh api repos/owner/repo/pulls?state=open
gh api user

# POST request
gh api repos/owner/repo/issues \
  --method POST \
  --field title="New bug" \
  --field body="Description" \
  --field labels[]="bug"

# Paginate through all results
gh api --paginate repos/owner/repo/issues | jq '.[].title'

# Raw output (no pretty-printing)
gh api repos/owner/repo --jq '.stargazers_count'

# Use jq to extract fields
gh api repos/owner/repo/releases | jq '.[0] | {tag: .tag_name, date: .published_at}'

GraphQL API

# GitHub GraphQL endpoint: https://api.github.com/graphql
# gh api graphql handles auth automatically

# Query PR details with reviews
gh api graphql -f query='
  query($owner: String!, $repo: String!, $number: Int!) {
    repository(owner: $owner, name: $repo) {
      pullRequest(number: $number) {
        title
        state
        additions
        deletions
        reviewDecision
        reviews(last: 5) {
          nodes {
            author { login }
            state
            body
          }
        }
      }
    }
  }
' -F owner=myorg -F repo=myrepo -F number=42

# Get all repos in org with pagination
gh api graphql --paginate -f query='
  query($endCursor: String) {
    organization(login: "myorg") {
      repositories(first: 100, after: $endCursor) {
        nodes { name stargazerCount isPrivate }
        pageInfo { hasNextPage endCursor }
      }
    }
  }
' | jq '.data.organization.repositories.nodes[] | select(.stargazerCount > 100)'

Authentication — Tokens & Apps

  • Classic PAT: broad scopes (repo, workflow, etc.), not recommended for new projects

  • Fine-grained PAT: per-repository permissions, expiry date — prefer this for automation

  • GitHub App: installs on orgs, generates short-lived tokens, better audit trail — best for CI/CD bots

  • OAuth App: user authorizes, gets token on their behalf — for third-party integrations

  • GITHUB_TOKEN: auto-generated per-workflow, scoped to the repo — use this in Actions

# In GitHub Actions — GITHUB_TOKEN is provided automatically
steps:
  - uses: actions/checkout@v4
    with:
      token: ${{ secrets.GITHUB_TOKEN }}  # default
      # OR use a PAT for cross-repo operations:
      token: ${{ secrets.MY_PAT }}
GitHub

Security: Dependabot, Code Scanning & Secrets

GitHub Security: Dependabot, Code Scanning & Secrets Dependabot Alerts & Version Updates Dependabot monitors your dependencies for known vulnerabilities and can

GitHub Security: Dependabot, Code Scanning & Secrets

Dependabot Alerts & Version Updates

Dependabot monitors your dependencies for known vulnerabilities and can automatically open PRs to update them.

# .github/dependabot.yml
version: 2
updates:
  # npm dependencies
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
      timezone: "Europe/London"
    open-pull-requests-limit: 5
    groups:
      dev-dependencies:
        dependency-type: "development"
      aws-sdk:
        patterns: ["@aws-sdk/*"]
    ignore:
      - dependency-name: "lodash"
        versions: ["4.x"]
    labels:
      - "dependencies"
    reviewers:
      - "alice"
    commit-message:
      prefix: "chore"

  # GitHub Actions
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"

Dependabot Security Updates

  • Enable in: Settings → Security → Dependabot → "Dependabot security updates"

  • Automatically opens PRs for dependencies with known CVEs

  • Grouped security updates (beta): combines multiple updates in one PR

  • Dependabot alerts: view all known vulnerabilities without auto-PRs

  • Dismiss alerts: mark as false positive, used in tests only, or tolerable risk

Code Scanning (CodeQL)

# .github/workflows/codeql.yml
name: CodeQL Analysis

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 1 * * 1'  # Weekly Monday 1am

jobs:
  analyze:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      actions: read
      contents: read

    strategy:
      matrix:
        language: [javascript-typescript, python]

    steps:
      - uses: actions/checkout@v4

      - uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}
          queries: security-extended  # security-and-quality for more checks

      - uses: github/codeql-action/autobuild@v3

      - uses: github/codeql-action/analyze@v3
        with:
          category: "/language:${{ matrix.language }}"
          upload: true

Secret Scanning

  • Scans pushes for known secret patterns (AWS keys, GitHub tokens, Stripe keys, 200+ patterns)

  • Blocks pushes containing secrets (with push protection enabled)

  • Partner program: GitHub notifies providers (AWS, Stripe) automatically when their secrets are detected

  • Custom patterns: define regex for your own secret formats

  • Bypass: if you must push a test secret, use `git push --push-option=skip` or acknowledge in UI

# View secret scanning alerts
gh api /repos/owner/repo/secret-scanning/alerts | jq '.[] | {state, secret_type}'

# Resolve an alert
gh api /repos/owner/repo/secret-scanning/alerts/42 \
  --method PATCH \
  --field state=resolved \
  --field resolution=false_positive

SBOM & Supply Chain Security

# Generate SBOM (Software Bill of Materials)
gh api /repos/owner/repo/dependency-graph/sbom \
  --jq '.sbom.packages[].name' | head -20

# Dependency review in PRs (block PRs adding vulnerable deps)
# .github/workflows/dependency-review.yml
name: Dependency Review
on: [pull_request]
jobs:
  dependency-review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/dependency-review-action@v4
        with:
          fail-on-severity: high
          deny-licenses: GPL-2.0, GPL-3.0
GitHub

Advanced Git Workflows & Interview Questions

Advanced Git Workflows & Interview Questions Branch Strategies Git Flow (complex, suits release-heavy products): main — production code develop — integration br

Advanced Git Workflows & Interview Questions

Branch Strategies

Git Flow (complex, suits release-heavy products):
  main        — production code
  develop     — integration branch
  feature/*   — feature branches off develop
  release/*   — stabilization before merge to main
  hotfix/*    — urgent fixes off main

GitHub Flow (simple, suits continuous deployment):
  main        — always deployable
  feature/*   — branch off main, PR back to main
  Deploys happen on merge to main

Trunk-Based Development (advanced, suits large teams):
  main (trunk) — everyone integrates frequently (at least daily)
  short-lived  — feature branches live < 2 days
  Feature flags — hide incomplete features behind flags
  Requires: strong CI, good test coverage, feature flags infrastructure

Merge Strategies — When to Use Which

  • Merge commit: preserves full history, shows exactly when branches diverged and merged. Good for release branches. Creates a merge commit.

  • Squash merge: combines all PR commits into one clean commit on main. Good for feature branches with messy WIP commits. Loses individual commit authorship.

  • Rebase merge: replays PR commits on top of main, linear history. Good when you want clean history without squashing. Rewrites commit SHAs.

  • Rule of thumb: squash for features, merge commit for release/hotfix, rebase for clean individual commits.

Undoing Things Safely

# Undo last commit — keep changes staged
git reset --soft HEAD~1

# Undo last commit — keep changes unstaged
git reset HEAD~1

# Undo last commit — discard changes (DESTRUCTIVE)
git reset --hard HEAD~1

# Undo a published commit (safe — creates new commit)
git revert abc123   # revert specific commit
git revert HEAD     # revert last commit

# Undo a file change
git checkout -- src/file.ts   # restore to last commit
git restore src/file.ts       # modern syntax

# Unstage a file
git restore --staged src/file.ts

# Fix last commit message (only before push!)
git commit --amend -m "Correct message"

# Add forgotten file to last commit (before push)
git add forgotten-file.ts
git commit --amend --no-edit

Cherry-Pick & Interactive Rebase

# Apply specific commits to current branch
git cherry-pick abc123
git cherry-pick abc123..def456   # range

# Interactive rebase — rewrite history (before push only)
git rebase -i HEAD~5    # last 5 commits

# Commands in interactive rebase:
# pick    — keep commit as-is
# reword  — keep but edit message
# edit    — stop and allow amending
# squash  — combine with previous commit
# fixup   — like squash but discard this commit's message
# drop    — remove commit entirely

Interview Questions

  • Git Flow vs GitHub Flow? Git Flow: complex, good for versioned releases (libraries, mobile apps). GitHub Flow: simple, good for web apps with continuous deployment. Trunk-Based: advanced, suits large teams with feature flags.

  • Squash vs rebase vs merge commit? Squash: one clean commit, lose individual history. Rebase: linear history, rewrites SHAs (risky if shared). Merge: preserves history, creates merge commit. Choose based on team conventions and whether the branch is shared.

  • How to undo a published commit? git revert — creates a new commit that undoes the changes. Never git reset --hard on published branches (rewrites shared history).

  • Monorepo vs polyrepo? Monorepo: shared CI, easy refactors across packages, atomic cross-package changes, but slower CI and more tooling complexity. Polyrepo: independent deployment, isolated concerns, but cross-repo changes are hard and dependency management is complex.

  • CODEOWNERS purpose? Automatically assigns reviewers based on file paths. Ensures domain experts review relevant changes. Enforced via branch protection (require CODEOWNER review).

  • How does GitHub Actions caching work? Cache key is a hash. On hit: restore. On miss: run steps, then save cache at end. Keys support restore-keys for fallback. Cache is scoped to branch + OS. Max 10GB per repo.

  • What is a protected branch? Branch with rules preventing direct pushes, requiring PR reviews, passing CI, and/or CODEOWNER approval before merge. Prevents accidental or unauthorized changes to main.

Keep your GitHub knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever