CI-CD Pipelines interview questions: pipeline-as-code

Reviewed by Mark Dickie · Last updated

CI/CD pipeline-as-code is the practice of defining build, test, and deployment stages in version-controlled files rather than configuring them through a UI. For interview preparation, you should be comfortable reading and writing declarative pipeline definitions (Jenkinsfile, GitHub Actions YAML, GitLab CI YAML), explaining how triggers work, and describing how reusable templates reduce duplication across teams.

Expect questions on stage and step structure, conditional execution, artifact passing between jobs, secret management, and the trade-offs between imperative scripting and declarative config. You may also be asked to spot a broken pipeline file or explain what happens when a step fails mid-run.

ConceptWhat to know
Pipeline fileVersion-controlled definition of stages, steps, and triggers
Declarative vs scriptedDeclarative reads as config blocks; scripted gives you a full programming language
Templates / reusable stepsShared definitions that multiple pipelines include to avoid copy-paste
TriggersPush, PR, schedule, manual, or upstream artifact events that start a run
SecretsInjected credentials that never appear in logs or committed files

What does a pipeline-as-code interview typically test?

  1. Reading a YAML or Groovy pipeline file and explaining what each stage does
  2. Identifying why a build would fail early and what the runner skips afterward
  3. Writing a minimal pipeline that builds, tests, and deploys with a manual approval gate
  4. Explaining how environment variables and secrets differ from hard-coded values
  5. Describing how matrix builds or reusable templates cut down on repeated config
  6. Comparing how Jenkins, GitHub Actions, and GitLab CI each model the same set of stages

Pipeline-as-code questions reward clarity. State the stage flow, name the trigger, and call out where secrets come from. The quiz below covers the same patterns you will see in a live interview.

Key facts

  • Tarmac has 15 CI-CD Pipelines interview questions on this topic, 10 of them on this page, at difficulty 1–4 of 5.
  • Tarmac tracked 4,170 job postings asking for CI-CD Pipelines in August 2026.
  • Roles asking for CI-CD Pipelines advertise a median base salary of £77,500, across 716 job postings as of August 2026.
  • Tarmac last reviewed these CI-CD Pipelines interview questions on 14 September 2026.

At a glance

Questions10 shown · 15 in the bank
Difficulty1–4 of 5
FormatsTrue / false, Flashcard, Multiple choice, Find the bug, Multiple answer, Short answer, Ordering

What you'll review

  1. pipeline as code

Practice questions

Try one before you open the answer. Pick an option and press Check; it's marked on the spot.

CI-CD Pipelines/cicd-pipeline/pipeline-as-code

Writing a CI/CD pipeline as version-controlled YAML automatically encrypts any secret value that's typed directly into that file, because the file lives in a private repository.#

Options

Show answer

False. Pipeline-as-code version-controls the pipeline's structure, not its secret handling, so a literal value typed directly into a YAML file is stored and stays as plain text in the repository and its full commit history regardless of whether the repo is private. Genuine secrets handling means referencing a platform secret store by name — GitHub Actions secrets.*, a masked GitLab CI/CD variable, or a Jenkins credentials() binding — so the committed file only ever contains a reference, never the value.

Why:

False. Pipeline-as-code version-controls the pipeline's structure, not its secret handling — a literal value written into a YAML file is stored, and remains, as plain text in the repository and in every commit in its history, private repo or not. Real secrets handling means referencing a platform secret store (secrets.DEPLOY_KEY in GitHub Actions, a masked CI/CD variable in GitLab, a Jenkins credentials() binding) so the pipeline file only ever contains a name, never the value. A private repo restricts who can read the file at all, but it does nothing to encrypt what's already written inside it — and once a literal secret is committed, rotating it and scrubbing history is the only real fix.

CI-CD Pipelines/cicd-pipeline/pipeline-as-code

What is 'pipeline as code', and why is it preferred over configuring pipelines through a UI?#

Show answer

Pipeline as code means defining the CI/CD pipeline in a version-controlled file in the repository (e.g. .github/workflows/*.yml, .gitlab-ci.yml, Jenkinsfile) rather than clicking it together in a server's web UI. Because the definition lives with the code, it is reviewed in pull requests, versioned and diffable, rolls back with the code, and is reproducible across branches and new projects. UI-configured pipelines drift silently, have no audit trail or review, and are hard to recreate if the server is lost.

Why:

The core idea is that the pipeline definition is source-controlled alongside the application, so it gets the same review, history, and reproducibility as any other code. The practical payoff — and the usual interview follow-up — is auditability and disaster recovery: a UI-clicked pipeline has no diff, no review, and vanishes with the server, whereas pipeline-as-code is just another file you can restore, branch, and roll back.

CI-CD Pipelines/cicd-pipeline/pipeline-as-code

Which of these is a real way to validate a pipeline-as-code YAML file's syntax and schema before it's pushed and actually run?#

Options

Show answer

Dedicated linters exist for validating pipeline-as-code files before they run: actionlint statically checks GitHub Actions workflow YAML against its schema, and GitLab's CI Lint API — or the 'Validate' UI tab — checks a .gitlab-ci.yml file's syntax and resolved includes without executing the pipeline. Running these pre-push catches structural mistakes far more cheaply than waiting for a live pipeline failure.

Why:

Both major platforms have dedicated static validation for pipeline definitions: actionlint parses GitHub Actions workflow YAML against its schema and catches invalid expressions, undefined contexts, and shellcheck issues in run: steps, while GitLab exposes a CI Lint endpoint (and a 'Validate' UI tab) that checks .gitlab-ci.yml syntax and merges includes without running the pipeline. npm audit checks package vulnerabilities, not pipeline YAML, and there's no .yaml.lock auto-validation convention — those are invented distractors. Catching structural errors here is far cheaper than discovering them via a failed live run.

CI-CD Pipelines/cicd-pipeline/pipeline-as-code

This declarative Jenkinsfile is checked into the application's git repository — a standard pipeline-as-code setup. What's wrong with it?#

pipeline {
    agent any
    environment {
        AWS_SECRET_ACCESS_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
    }
    stages {
        stage('Deploy') {
            steps {
                sh 'aws s3 sync ./dist s3://my-app-bucket'
            }
        }
    }
}

Options

Show answer

The real AWS secret key is hardcoded as a literal string in a file that's committed to git, permanently exposing it in the repository's history to anyone with read access — it should come from Jenkins's credential store, e.g. AWS_SECRET_ACCESS_KEY = credentials('aws-secret-access-key-id')

Why:

The whole point of pipeline-as-code is that the file is versioned — which is exactly why a literal secret written into it is a serious problem: it's now permanently in git history, readable by anyone with repo access, and can't be revoked just by editing the file (a later commit still leaves the old value reachable in history). The fix is Jenkins's credentials() helper, which binds a credential stored in Jenkins's credential store to an environment variable at runtime — AWS_SECRET_ACCESS_KEY = credentials('aws-secret-access-key-id') — so the Jenkinsfile only ever contains a credential id, never the value. The other options describe non-issues: agent any, a top-level environment {} block, and sh steps inside stages are all valid, ordinary declarative-pipeline syntax.

CI-CD Pipelines/cicd-pipeline/pipeline-as-code

In GitHub Actions, what's the difference between a composite action and a reusable workflow, and when would you reach for each?#

Show answer

A composite action bundles a sequence of steps into one reusable step: it's invoked with uses: inside a job's steps: list, runs on the caller's runner as part of the caller's job, and has no automatic access to the caller's secrets context — secrets must be passed explicitly as inputs. A reusable workflow is a whole pipeline invoked with jobs.<job_id>.uses: at the job level (via on: workflow_call); it runs as its own job (or jobs) with its own runner, can declare inputs/secrets/outputs, and can be shared with secrets: inherit. Reach for a composite action to dedupe a handful of steps within a job; reach for a reusable workflow to dedupe a whole job graph across repos.

Why:

The two live at different levels of the pipeline. A composite action is step-level reuse — it executes inline in the calling job, so it shares that job's runner and environment but must receive secrets as declared inputs rather than reading secrets.* directly. A reusable workflow is job-level (or whole-pipeline-level) reuse: called via jobs.<id>.uses: org/repo/.github/workflows/file.yml@ref, it spins up as an independent job with its own runs-on, and can accept a secrets: inherit shortcut precisely because it's a distinct job boundary, not an inline step. Confusing the two is a common source of 'why can't my composite action see this secret' bugs.

CI-CD Pipelines/cicd-pipeline/pipeline-as-code

A team wants to call a reusable GitHub Actions workflow (.github/workflows/build.yml, which declares on: workflow_call) from another workflow in the same repository. Where does the call go?#

Options

Show answer

A reusable workflow that declares on: workflow_call is invoked at the job level in the caller: jobs.<job_id>.uses points at the workflow file, for example jobs: call-build: uses: ./.github/workflows/build.yml, with with: supplying any declared inputs. GitHub then runs it as its own job, distinct from the composite-action pattern where uses: sits inside a steps: list instead.

Why:

Reusable workflows are called at the job level, not the step level: the calling job's only key is uses: pointing at the workflow file (plus optional with:/secrets:), and GitHub runs it as its own job. That's the structural tell that separates a reusable workflow from a composite action, which is called as a step. Putting the call in steps: (b) is the composite-action pattern and fails schema validation for a workflow_call file; on: (c) declares what triggers a workflow, it isn't how you invoke one; needs: (d) only declares job-ordering dependencies, not a call.

CI-CD Pipelines/cicd-pipeline/pipeline-as-code

Which of these genuinely reduce duplication ('DRY') across pipeline-as-code definitions? Select all that apply.#

Options

Pick every one that applies.

Show answer

Two native mechanisms reduce duplication across pipeline-as-code files: GitLab's extends: keyword, which lets a job inherit configuration from a shared hidden template job, and GitHub Actions reusable workflows, called at the job level via uses:, which let many repositories share one pipeline definition. Copy-pasting the same block into every repository or hardcoding a different literal secret into each file are not DRY practices — the first duplicates exactly what these features exist to centralize, and the second is a security anti-pattern unrelated to reuse.

Why:

extends and reusable workflows are the two platforms' native mechanisms for defining shared configuration once and reusing it everywhere else, which is exactly what DRY means for pipeline-as-code. Copy-pasting a block into every repo is the opposite of DRY — it's the duplication these features exist to remove, and it means every future change has to be repeated N times by hand. Hardcoding a different literal secret per file isn't a DRY concern at all (secrets shouldn't be literal values in the file to begin with) and duplicates effort rather than removing it.

CI-CD Pipelines/cicd-pipeline/pipeline-as-code

You want to stop broken pipeline YAML from ever reaching a shared branch. Name two concrete pre-merge checks you'd add, and explain why relying on the actual pipeline run as your only validation isn't sufficient.#

Show answer

Add a schema/syntax linter as a required check on the PR itself — actionlint for GitHub Actions workflows, or GitLab's CI Lint API/'Validate' tab for .gitlab-ci.yml — so malformed YAML, undefined contexts, or bad expressions fail fast without ever touching a real runner. Pair it with a dry-run or a test consumer: run the changed reusable workflow/template against a throwaway or staging repo before merging, and/or preview it locally with a tool like act. Relying only on a live run isn't enough because by the time it runs it may already have side effects — deploying, spending compute/cost, mutating shared infrastructure — and because a syntactically valid pipeline can still be logically wrong in ways that only surface on specific code paths (a needs: referencing a job that was renamed, a conditional that's never true), so 'it ran once and passed' doesn't prove the definition is correct for every trigger it will actually see.

Why:

The mature answer names an actual tool (actionlint / GitLab CI Lint) rather than 'just be careful', and explains the real reason a live run is a poor validation gate: it can carry side effects (deploys, spend, shared-state mutation) and only exercises the one code path that happened to trigger, not every branch or condition the pipeline could hit. Candidates who only say 'run it and see' haven't thought about the blast radius of validating a deploy pipeline by actually deploying.

CI-CD Pipelines/cicd-pipeline/pipeline-as-code

Order the steps of safely rolling out a change to a shared reusable pipeline template (e.g. a GitHub Actions reusable workflow) consumed by many other repositories.#

Put these in order

Show answer

Rolling out a change to a shared reusable pipeline template follows a fixed sequence: first edit the template in a feature branch of the templates repository, then lint/validate the change and run it against one throwaway or staging consumer, then merge and cut a new version tag such as v2 while leaving the existing v1 tag untouched, then point one low-risk consumer repository at the new tag and confirm it behaves correctly, and finally roll the version bump out to the remaining consumers. Publishing a new tag instead of force-moving the old one, and proving the change on one pilot consumer first, keeps a bad change from breaking every consumer at once.

Why:

Changing a template that many repositories depend on needs the same staged-rollout discipline as any shared dependency: change it in isolation, validate the change before anyone consumes it, publish it as a new version rather than force-moving the old one (so nothing breaks for consumers still pinned to v1), prove it on a single low-risk consumer, then propagate. Force-moving the existing tag to point at new code, or rolling out to every consumer at once, both remove the safety net a versioned template exists to provide — a bad change would break everyone simultaneously instead of surfacing on one pilot repo first.

CI-CD Pipelines/cicd-pipeline/pipeline-as-code

Which of these are genuine causes of 'pipeline drift' — where a repository's actual CI/CD behavior diverges from what its version-controlled pipeline files describe? Select all that apply.#

Options

Pick every one that applies.

Show answer

Pipeline drift comes from platform-side configuration that lives outside the version-controlled files. GitHub Environment protection rules such as required reviewers are set only through the Settings UI with no equivalent in workflow YAML, and branch-protection required-status-checks reference job names as strings that silently go stale when a job is renamed in the YAML — both leave the platform's real behavior invisible to, or out of sync with, the code. Pinning a template to a commit SHA is a hardening practice that reduces drift rather than causing it, and running a workflow locally with a tool like act only previews a run without touching platform state.

Why:

Drift happens whenever platform-side state that affects pipeline behavior lives outside the versioned files. GitHub Environment protection rules (required reviewers, wait timers) are configured entirely through Settings, not through workflow YAML, so a repo's real approval gate can be invisible to anyone reading the code. Branch-protection required-status-checks reference job names as strings maintained in Settings; if a job is renamed in the YAML, the branch-protection entry silently goes stale and permanently blocks pull requests waiting on a check that will never report — a well-documented GitHub gotcha. Pinning to a SHA is the opposite of a drift risk — it's a supply-chain hardening practice that makes behavior more predictable. Running a workflow locally with act only previews a run; it never touches platform configuration, so it can't cause drift.

Related interview questions

Job market

See ci-cd-pipelines salaries and hiring demand from live job postings.

The other 5 questions

This page shows 10 and marks what you pick. That's as far as a page can go. A free account opens the other 5 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.

Start with this topic

Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.