GitHub Actions — Interview Q&A Matrix
Parent stack: CI/CD Technology: GitHub Actions Levels: Junior · Senior · Architect Questions: 5
Each entry contains a Question, the Expected Depth, an Ideal Answer, and Red Flags.
Junior
Q1 — Workflow structure and triggers
Question: What are the main parts of a GitHub Actions workflow, and how does a workflow start?
Expected Depth:
- Names the core concepts: workflow, event, job, step, runner, and action.
- Knows that workflow files live in
.github/workflowsand use YAML. - Explains that events such as
pushandpull_requeststart a workflow. - Knows that jobs run in parallel by default and that steps in a job run in order.
Ideal Answer:
- A workflow is a YAML file in
.github/workflows. An event starts it, it contains one or more jobs, and each job contains ordered steps. Each job runs on a runner, which is a virtual machine. - A step either runs a shell command with
runor uses a reusable action withuses. - Jobs run in parallel unless one job declares
needsto wait for another.
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Print a message
run: echo "Building commit $GITHUB_SHA"actions/checkoutcopies the repository into the runner so later steps can use the code.- Common event types include
push,pull_request,schedulefor timed runs, andworkflow_dispatchfor a manual start.
Red Flags:
- Cannot name jobs, steps, or runners.
- Thinks steps in one job run in parallel.
- Does not know where workflow files must live.
Q2 — Actions, dependency caching, and build speed
Question: How do you set up a language toolchain and cache dependencies so a build workflow runs faster?
Expected Depth:
- Uses a setup action such as
actions/setup-javaoractions/setup-node. - Caches dependencies with a key based on a lock file.
- Understands cache hit, cache miss, and cache restore keys.
- Knows that a cache should not store secrets or build output that must always be fresh.
Ideal Answer:
- Use a setup action to install the toolchain. Many setup actions have built-in dependency caching, which is the simplest option:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "21"
cache: maven # built-in dependency cache
- run: mvn -B verify- When you need manual control, use
actions/cache. The key should include a hash of the lock file so the cache changes when dependencies change. Restore keys allow a partial match when the exact key is missing:
- uses: actions/cache@v4
with:
path: ~/.m2/repository
key: maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
maven-- A cache miss simply rebuilds the cache, so a wrong key makes builds slower but not incorrect.
- Do not cache secrets or final artifacts that must always be produced fresh. Use artifacts (
actions/upload-artifact) to pass build output between jobs, not the cache.
Red Flags:
- Installs the toolchain with manual shell scripts when a setup action exists.
- Uses a fixed cache key that never changes, so new dependencies are missed.
- Caches build output that must always be regenerated.
- Confuses the dependency cache with build artifacts.
Senior
Q3 — Secrets, token permissions, and untrusted input
Question: How do you keep a workflow secure when it handles secrets and runs on pull requests from forks?
Expected Depth:
- Uses repository or environment secrets instead of hard-coded values.
- Applies least privilege to the
GITHUB_TOKENwithpermissions. - Explains the difference between
pull_requestandpull_request_target. - Prevents script injection from untrusted event data.
- Pins third-party actions to reduce supply-chain risk.
Ideal Answer:
- Store credentials as secrets and read them through
secrets. Never print a secret or write it into logs. - Set the smallest token permissions the workflow needs. Start read-only and add specific write scopes only where required:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./gradlew build
env:
API_TOKEN: ${{ secrets.API_TOKEN }}pull_requestfrom a fork runs with a read-only token and no secrets, which is safe for untrusted code.pull_request_targetruns in the context of the base repository with secrets and a write token. If you check out and run the fork's code underpull_request_target, untrusted code can steal secrets. Avoid that combination.- Script injection happens when untrusted event data is placed directly inside a
runblock. A pull request title such as"; rm -rf /can run as a command. Pass the value through an environment variable and quote it, so the shell treats it as data:
- name: Print the title safely
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: echo "Title is: $PR_TITLE"- Pin third-party actions to a full commit SHA rather than a moving tag, so a changed tag cannot inject new code:
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0Red Flags:
- Hard-codes credentials in the workflow file.
- Leaves the default broad token permissions in place without review.
- Checks out and runs fork code under
pull_request_targetwith secrets. - Interpolates
github.eventdata straight into a shell command. - Uses only a floating tag for third-party actions in a sensitive workflow.
Q4 — Reusable workflows, matrix builds, and concurrency
Question: How do you avoid duplicated pipeline code across many repositories, test several versions at once, and stop wasted runs?
Expected Depth:
- Uses a reusable workflow (
workflow_call) or a composite action to share logic. - Uses a build matrix to test several versions or platforms.
- Uses
concurrencyto cancel superseded runs. - Knows when a reusable workflow is better than a composite action.
Ideal Answer:
- A reusable workflow shares whole jobs across repositories. It declares
workflow_callwith typed inputs and secrets:
# .github/workflows/java-build.yml
on:
workflow_call:
inputs:
java-version:
type: string
required: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: ${{ inputs.java-version }}
- run: mvn -B verify- A caller workflow uses it with
uses:
jobs:
call-build:
uses: ./.github/workflows/java-build.yml
with:
java-version: "21"- A matrix runs the same job for several values.
fail-fast: falselets every combination finish so you see all failures, not only the first:
strategy:
fail-fast: false
matrix:
java: ["17", "21"]- Concurrency cancels an old run when a new commit arrives on the same branch or pull request, which saves runner time:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true- Use a reusable workflow to share full jobs, including their runner and permissions. Use a composite action to share a sequence of steps inside one job.
Red Flags:
- Copies the same pipeline YAML into every repository.
- Uses a matrix but keeps
fail-faston when the team needs every result. - Never cancels superseded runs and wastes runner minutes.
- Cannot explain the difference between a reusable workflow and a composite action.
Architect
Q5 — CI/CD platform design: environments, OIDC, and runner strategy
Question: As the platform owner, how would you design GitHub Actions deployment pipelines for many teams so they are secure, auditable, and consistent?
Expected Depth:
- Uses environments with protection rules and required reviewers for deployments.
- Replaces long-lived cloud keys with short-lived OIDC credentials.
- Defines a runner strategy, including the risks of self-hosted runners.
- Standardizes pipelines with reusable workflows and required status checks.
- Adds supply-chain controls, auditing, and clear rollback.
Ideal Answer:
- Model each deployment target as an environment (for example
stagingandproduction). Environments add required reviewers, wait timers, branch limits, and scoped environment secrets, so a production deploy needs approval and leaves an audit record:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- run: ./deploy.sh- Remove static cloud keys. Use OpenID Connect so the workflow requests a short-lived credential from the cloud provider. This needs the
id-tokenpermission and a trust policy on the cloud role:
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::111122223333:role/deploy
aws-region: eu-west-1- Choose runners with care. GitHub-hosted runners are clean for each job. Self-hosted runners can reach private networks, but a fork pull request could run untrusted code on them, so do not use self-hosted runners for public repositories, and prefer ephemeral runners that are destroyed after one job.
- Standardize pipelines with reusable workflows owned by the platform team, and enforce them with branch protection and required status checks, so no code merges without passing the shared pipeline.
- Add supply-chain controls: pin actions to commit SHAs, restrict which actions are allowed in organization settings, and generate build provenance. Keep deployment logs and approvals for audit, and provide a tested rollback path such as redeploying the previous release.
Red Flags:
- Stores long-lived cloud keys as secrets instead of using OIDC.
- Deploys to production with no environment protection or approval.
- Runs fork pull requests on self-hosted runners with network access.
- Lets every team write its own unreviewed deploy pipeline.
- Has no action pinning, audit trail, or rollback plan.
Scoring Rubric
| Level | Pass bar |
|---|---|
| Junior | Explains workflow structure, triggers, actions, and caching correctly. |
| Senior | Secures tokens and secrets and shares pipeline logic with matrices and concurrency. |
| Architect | Designs environment protection, OIDC, runner strategy, and organization-wide standards. |