Tools Used in DevOps · Bitbucket Pipelines

Bitbucket Pipelines

Bitbucket Pipelines is Atlassian's CI/CD service built directly into Bitbucket Cloud: instead of standing up a separate CI server and wiring it to your repository with webhooks and a service account, you commit one YAML file — bitbucket-pipelines.yml — and every push, pull request, and tag can build, test, and deploy in Docker containers Bitbucket provisions and tears down for you. It is not trying to out-feature Jenkins or out-market GitHub Actions; its entire pitch is that if your team already lives in Jira for issues, Bitbucket for code, and Opsgenie for paging, the CI/CD tool that requires zero new vendor, zero new SSO integration, and shows build status directly on the Jira ticket is usually the right default rather than a compromise.

☺ Explain it like I'm 10

Picture a school where the classroom (Bitbucket, where your code lives) already has a supply closet built into the wall (Pipelines) instead of you needing to walk to a separate building to get scissors and glue. You write a short list on an index card — "cut here, glue there, then hang it on the wall" — and tape it to the closet door. Every time you turn in new homework, the closet reads your card and does exactly those steps by itself, using its own tools that get put away clean afterward. And because the closet is built into the same classroom where your teacher (Jira) already tracks your assignments, the teacher's gradebook updates itself the moment your homework reaches the wall — nobody has to walk over and tell her.

🦫Your host for this topic: Benny the Beaver — the builder who turns a branching diagram into a pipeline that actually ships code. Benny's the one who'll tell you honestly that the YAML here is nearly identical in spirit to GitLab CI/CD's, and that most of what makes it worth learning on its own is everything wired around it, not the syntax itself.

What Bitbucket Pipelines is and the problem it solves

☺ Like you're 10: It's CI/CD that comes pre-installed with your code host instead of being a separate tool you have to go set up and connect.

Before a repository host offered CI/CD natively, "set up continuous integration" meant provisioning a Jenkins box (or a hosted equivalent), creating a service account with repo access, configuring a webhook so pushes actually triggered a build, and then maintaining that whole second system indefinitely — patched, backed up, and kept in sync with whatever the repository host changed. Bitbucket Pipelines, which Atlassian launched in 2016, collapses that into a feature flag and a YAML file: enable Pipelines on a Bitbucket Cloud repository, commit bitbucket-pipelines.yml at the repo root, and Bitbucket's own infrastructure runs every step in a fresh Docker container it provisions, executes, and discards — no controller to patch, no agent fleet to keep alive, no separate authentication story between the CI system and the code it's building.

The trade this makes is the same one every "built into the platform" tool makes: less to operate, less to decide, and a hard boundary at whatever Atlassian has chosen to expose. It also comes with a boundary worth naming immediately, because it surprises teams migrating from self-hosted Bitbucket: Pipelines is a Bitbucket Cloud feature. Bitbucket Data Center (the self-hosted product, formerly Bitbucket Server) has no equivalent built in — Data Center installs typically pair with a separate CI tool such as Bamboo or Jenkins, triggered off Bitbucket's webhooks and build-status API the same way any other external CI would be. If your organization runs Bitbucket on its own infrastructure rather than on bitbucket.org, nothing on this page's YAML syntax applies until — or unless — you migrate to Cloud.

◆ Key idea

Every step in a Bitbucket Pipelines run is a fresh, isolated Docker container — nothing persists between steps except what you explicitly hand forward as an artifact, or what you explicitly cache. This is a deliberate design choice shared with GitLab CI/CD and GitHub Actions, and it's the opposite default from a self-managed Jenkins agent, whose workspace can quietly accumulate state across builds unless someone cleans it. It makes a Bitbucket pipeline harder to get subtly wrong through leftover state — and it means the two lines "declare it as an artifact" and "declare it as a cache" are doing almost all the work of getting files from one step to the next.

The Atlassian ecosystem: Jira, deployments, and Opsgenie in one flow

☺ Like you're 10: Because the code, the ticket, and the pager are all made by the same company, a deploy in one of them quietly updates the other two without anyone copy-pasting a status by hand.

This is the section that actually decides most real adoptions, and it's worth being concrete rather than hand-waving at "they integrate." Three specific wires matter.

Smart commits and Jira's Development panel

A commit message written as PROJ-123 #comment Fixed the null pointer #time 2h is a smart commit: Bitbucket parses the Jira issue key at the front, and Jira's PROJ-123 issue gets a comment and a logged two hours of work without anyone opening Jira. That's table stakes and most Git hosts can approximate it. The part that's genuinely Bitbucket-specific is what happens next: every Jira issue linked to a commit, branch, or pull request grows a Development panel showing exactly which commits touched it, which branch it's on, whether its pull request is open or merged — and, because Bitbucket Pipelines reports deployment status back to Jira automatically through the same integration, which environment it has actually reached. A product manager can open a Jira ticket and see "deployed to Staging, not yet in Production" without pinging an engineer or opening Bitbucket at all. That live deployment-tracking view is the feature most Jira-and-Bitbucket shops mention first when asked why they didn't bother evaluating a separate CI tool.

Deployment history lives next to the code, not in a separate dashboard

Every deployment: step (covered in full below) shows up on the repository's own Deployments dashboard in Bitbucket — which commit is live in which environment right now, who triggered it, and the full history of what shipped when. There's no second tool to open to answer "what's actually in production," because the CI/CD system and the code review system are the same product.

Opsgenie: deploy-aware alert suppression, scripted rather than built in

There is no dedicated Opsgenie step baked into Pipelines the way deployment: is — this wire is something you build, not something you toggle on — but it's a natural one given the ecosystem, and it directly implements the deploy-aware suppression pattern on-call culture & sustainable operations and the Opsgenie tool page both argue for: a script step calls Opsgenie's maintenance-mode API to open a short suppression window right before a production deploy, and a later step — or the same step, on the way out — closes it once the canary bake finishes. The effect is that a deploy's expected, momentary noise never has to be manually remembered by whoever happens to be on-call that week; the pipeline remembers it for them.

# excerpt of a production deployment step — the Opsgenie calls are plain script, not a first-party keyword
- step:
    name: Deploy to production
    deployment: production
    trigger: manual
    script:
      - >
        curl -s -X POST "https://api.opsgenie.com/v1/maintenance"
        -H "Authorization: GenieKey $OPSGENIE_API_KEY"
        -d '{"description":"checkout deploy","time":{"type":"for-5-minutes"},
             "rules":[{"entity":{"id":"checkout-oncall","type":"team"},"state":"enabled"}]}'
      - ./deploy.sh
      - npm run smoke-test
Push to branch Pull request opened Tag pushed Custom / scheduled exactly one of these per run Priority match branches → pull-requests → tags → custom · only one wins Selected pipeline isolated containers, no shared state Build Test A Test B Deploy Bitbucket Deployments Jira issue Dev panel auto-synced Opsgenie maintenance one push, one selected pipeline, one deployment fanning out to three places the fan-out on the right is what "already on Jira and Opsgenie" actually buys you

Anatomy of bitbucket-pipelines.yml

☺ Like you're 10: One file at the top of your repo lists what to do when code changes — and Bitbucket only ever follows one list per push, never several at once.

The file lives at the repository root and is nothing more than a mapping from trigger type to a list of steps. Four top-level trigger sections exist under pipelines:default, branches, pull-requests, and tags — plus a custom section for pipelines that only run manually or on a schedule, never automatically on a push.

image: node:18   # default image for every step below, unless a step overrides it

pipelines:
  default:                       # runs on any push that no more specific rule below matches
    - step:
        name: Build and test
        caches:
          - node
        script:
          - npm install
          - npm test

  branches:
    main:                        # a push to `main` specifically — this wins over `default`
      - step:
          name: Build
          script:
            - npm install
            - npm run build
          artifacts:
            - dist/**            # hand the build output forward to the next step
      - step:
          name: Deploy to production
          deployment: production
          trigger: manual        # someone must click "Run" in the UI to actually deploy
          script:
            - ./deploy.sh dist/

  pull-requests:
    '**':                        # every pull request, regardless of source or target branch
      - step:
          script:
            - npm install
            - npm test

  tags:
    'v*':                        # any tag matching v* — v1.4.0, v2.0.0-rc1, and so on
      - step:
          script:
            - npm run release

  custom:
    nightly-full-suite:           # never runs automatically — triggered by schedule or by hand
      - step:
          script:
            - npm run test:full
⚠ Only one pipeline runs per push — and it's the most specific match, not every match

This is the single most common source of "why didn't my pipeline run" confusion. A push is evaluated against branches patterns first; if one matches, that pipeline runs and default is not also run alongside it. If no branches pattern matches, Bitbucket falls back to default. pull-requests and tags are evaluated only for their respective event types, and custom pipelines never fire on a push at all — they exist purely for manual or scheduled triggers. A common mistake is adding a new branches: release/* block and being surprised that the team's existing default checks silently stopped running against release branches; they didn't stop, they were simply never running there once the more specific rule started matching first.

Inside a step, the fields that do the real work are name (shown in the UI), image (override the top-level default for just this step), script (an ordered list of shell commands, the only genuinely required field), caches, artifacts, services, size (1x through 8x, trading more memory and CPU for more build-minute consumption per minute of runtime), and max-time (a per-step timeout in minutes). definitions: at the bottom of the file is where reusable services, caches, and — via plain YAML anchors — reusable step bodies live, which matters once a file has more than a couple of near-identical steps:

definitions:
  steps:
    - step: &unit-test
        name: Unit tests
        image: node:18
        caches: [node]
        script:
          - npm ci
          - npm test

pipelines:
  default:
    - parallel:
        - step: *unit-test
        - step:
            <<: *unit-test           # YAML merge key: inherit everything from &unit-test
            name: Unit tests (Node 20)
            image: node:20           # …then override just this one field

That anchor-and-merge trick is doing the job a native build matrix does in GitHub Actions — Bitbucket has no strategy.matrix keyword of its own, so "run the same steps across a few images or a few environments" is expressed as plain YAML reuse rather than a dedicated feature. It's more verbose, but it's also just YAML, with no separate mini-language to learn.

Step-level path filtering exists too, and it's worth knowing for a monorepo: a condition.changesets.includePaths block skips a step entirely unless the push touched a matching path, which is how a monorepo avoids running the frontend suite on a backend-only commit without hand-rolling a shell script to diff the changeset yourself.

- step:
    name: Frontend tests
    condition:
      changesets:
        includePaths:
          - "frontend/**"
    script:
      - npm --prefix frontend test

Parallel steps and deployment environments

☺ Like you're 10: Some jobs can run side by side instead of one after another to save time, and shipping to a real environment gets its own gate so nobody deploys to production by accident.

Parallel steps

Wrapping a list of steps in - parallel: runs them concurrently instead of one after another, in separate containers, each starting from the same point in the pipeline and each free to fail or succeed independently:

pipelines:
  default:
    - step:
        name: Install dependencies
        script:
          - npm ci
        artifacts:
          - node_modules/**
    - parallel:
        - step:
            name: Unit tests
            script:
              - npm run test:unit
        - step:
            name: Lint
            script:
              - npm run lint
        - step:
            name: Type check
            script:
              - npm run typecheck
    - step:
        name: Build
        script:
          - npm run build

The pipeline as a whole only proceeds past the parallel group once every step inside it finishes; by default a failure in one parallel step doesn't cancel its siblings mid-run, they're allowed to finish and report their own result, and the whole group is marked failed if any one of them failed. Newer accounts can opt individual parallel groups into fail-fast behavior, which cancels the rest of the group the moment one step fails instead of waiting out the slowest one — worth checking the current Bitbucket documentation for the exact YAML shape, since this is one of the newer additions to the parallel-steps feature and has room to keep evolving.

Deployment environments

A step gains a deployment: key naming an environment — Test, Staging, and Production are the three built in on every plan; custom environment names beyond those three are a paid-tier feature, so confirm current plan limits before assuming you can name a fourth. Declaring deployment: production does three things at once: it records the deploy on the repository's Deployments dashboard, it's what triggers the Jira Development-panel sync described above, and — critically — it unlocks environment-scoped variables and, on paid plans, deployment permissions that restrict who is even allowed to trigger a deploy to that specific environment.

- step:
    name: Deploy to staging
    deployment: staging
    script:
      - ./deploy.sh $STAGING_API_KEY   # STAGING_API_KEY resolves from staging's own scoped variables
- step:
    name: Deploy to production
    deployment: production
    trigger: manual                     # gate: a human must click Run — this is the default safety net
    script:
      - ./deploy.sh $PRODUCTION_API_KEY  # a DIFFERENT secret, scoped only to the production environment

That last line matters more than it looks: a variable of the same name defined separately in the Staging and Production deployment environments resolves differently depending on which environment the running step is deploying to, without a single if statement in your script. It's the same "one secret per boundary, never shared across it" discipline Secrets & Credential Management argues for generally, expressed here as a first-class scoping feature rather than something you have to build yourself. Combine trigger: manual with deployment permissions and you get the same "someone with the right role clicks a button, and only then does production move" gate that deployment strategies covers as a general pattern — Bitbucket just gives it a name and a place to configure who's allowed to press it.

Pipes, variables, and self-hosted runners

☺ Like you're 10: Instead of writing every integration by hand, you can borrow a pre-made snippet — and if the job needs to reach a machine only your company can see, you can run the step there instead of in Bitbucket's cloud.

Pipes: parameterized integrations instead of hand-rolled scripts

A Pipe is a small, versioned, parameterized Docker image published to Atlassian's Pipes marketplace — the Bitbucket equivalent of a GitHub Actions marketplace action, just scoped narrowly to "run this one container with these inputs" rather than a full plugin API. Instead of hand-writing an AWS CLI invocation and remembering every flag, you reference the pipe and pass variables:

script:
  - pipe: atlassian/aws-s3-deploy:1.5.0
    variables:
      AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
      AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
      AWS_DEFAULT_REGION: 'us-east-1'
      S3_BUCKET: 'acme-static-assets'
      LOCAL_PATH: 'dist'
  - pipe: atlassian/slack-notify:2.1.0
    variables:
      WEBHOOK_URL: $SLACK_WEBHOOK
      MESSAGE: 'Deployed checkout to production'

Variable scoping, and how secrets stay out of the log

Variables can be set at the workspace level (shared across every repository), the repository level, or the deployment-environment level shown above, with deployment-scoped variables winning for a step running under that deployment: name. Any variable marked Secured is masked in build logs — Bitbucket replaces its value with asterisks anywhere it would otherwise print — the same string-match masking approach (not encryption) that GitHub Actions uses for its own secrets, and it carries the identical caveat: masking only catches the exact string, so a script that reformats a secret before printing it (base64-encodes it, reverses it, splits it across lines) can leak it in cleartext anyway. Treat masking as a safety net, not a guarantee, and keep genuinely sensitive material — production database credentials, signing keys — in a real secrets manager the pipeline reads from at runtime rather than in a Bitbucket variable at all, per Secrets & Credential Management.

Self-hosted runners

Bitbucket's own cloud infrastructure runs every step by default, but a step can instead target a self-hosted runner — a small agent you install on your own Linux, Windows, or macOS machine (or as a Docker container) inside your own network, labeled however you choose:

- step:
    name: Deploy to the internal staging cluster
    runs-on:
      - self.hosted
      - linux
      - internal-network
    script:
      - ./deploy-internal.sh

The two reasons teams reach for this are the same two reasons every hosted-runner alternative exists: reaching infrastructure that has no public ingress at all — an on-prem database, an internal Kubernetes cluster behind a VPN — without punching a hole in a firewall for Bitbucket's cloud IP ranges, and keeping specialized or expensive hardware (a GPU box, a particular embedded-systems toolchain) out of the metered cloud-minutes budget entirely.

Triggering and inspecting pipelines without the UI

There's no first-party CLI comparable to gh or glab for Bitbucket — most day-to-day interaction really is the web UI — but the REST API covers scripted access cleanly, and it's what most third-party tooling calls underneath:

# trigger a pipeline against a specific branch — the call most external automation wraps
$ curl -s -X POST -u "$BB_USER:$BB_API_TOKEN" \
    -H "Content-Type: application/json" \
    "https://api.bitbucket.org/2.0/repositories/acme/checkout/pipelines/" \
    -d '{"target": {"type": "pipeline_ref_target", "ref_type": "branch", "ref_name": "main"}}'
# NOTE: Atlassian has been moving Bitbucket Cloud auth from "app passwords" toward
# scoped API tokens — check current docs for whichever mechanism is live when you read this.

$ curl -s -u "$BB_USER:$BB_API_TOKEN" \
    "https://api.bitbucket.org/2.0/repositories/acme/checkout/pipelines/?sort=-created_on"  # recent runs
🦫 Benny's workshop · 20 min

On a throwaway Bitbucket Cloud repo, write a bitbucket-pipelines.yml with a default pipeline that installs, tests, and produces an artifacts-declared build output, then a branches: main pipeline that adds a deployment: staging step with no manual gate. Push to a feature branch first and confirm default runs; then merge to main and confirm the branch-specific pipeline runs instead of default, not alongside it. Add a second step with deployment: production and trigger: manual, confirm it sits waiting for a click, and check the repo's Deployments dashboard to see both environments' history side by side.

Gotchas and failure modes

☺ Like you're 10: A few defaults that feel free and unlimited on day one — minutes, memory, artifact storage — quietly run out later, usually right when you need them most.

Build minutes are a metered, plan-gated resource. Every Bitbucket Cloud plan includes a monthly build-minutes allowance, and a step's size: multiplier consumes minutes faster the larger it is — a 4x step doing heavy Docker builds can burn through a Free or Standard plan's monthly allowance in a fraction of the wall-clock time a 1x step would. Unlike a self-managed Jenkins controller running on hardware you already own, there's no way to "just run more builds" without either upgrading plan tier or trimming usage — confirm the current minute allowances and overage pricing on Atlassian's own pricing page rather than assuming last year's numbers still hold, since these change with some regularity.

Docker-in-Docker eats into the step's own memory budget. Building a container image inside a step requires opting into the built-in docker service, and that service's memory allocation is carved out of the same total the step itself has to work with — a step that's fine running tests can OOM the moment it also tries to build a sizeable image, and the fix is usually bumping size: rather than debugging the Dockerfile that "used to work."

- step:
    name: Build and push image
    size: 2x                 # more memory headroom, more build-minute cost per minute
    services:
      - docker
    script:
      - docker build -t acme/checkout:$BITBUCKET_COMMIT .
      - pipe: atlassian/docker-push:1.0.0
        variables:
          IMAGE_NAME: acme/checkout

Artifacts are pipeline-scoped and time-limited, not a real artifact store. Files declared under artifacts: pass forward only to later steps within the same pipeline run, expire after a limited retention window, and have a size cap — they solve "hand my build output to the deploy step three lines down," not "keep every release binary we've ever shipped." A team that needs the latter still wants JFrog Artifactory or Sonatype Nexus as the actual system of record, with Pipelines pushing to it as one step in the flow rather than trying to be it.

No first-party local runner. Unlike GitHub Actions, which has the third-party act tool for running a workflow locally in Docker, Bitbucket has no comparable official way to execute a full pipeline on your laptop before pushing — the closest you get is running the same base image manually and eyeballing whether your script survives it, or leaning on the online bitbucket-pipelines.yml validator to at least catch YAML structure errors before a push. A syntax mistake that the validator wouldn't catch — a typo'd variable name, a step that assumes an artifact that was never declared — is still only discoverable by actually pushing and watching it fail, which makes small, incremental YAML changes the safer habit over big rewrites.

Cloud-only, and that boundary doesn't move with a self-hosted migration. As covered above, this entire feature does not exist on Bitbucket Data Center. A team evaluating a move from Cloud to self-hosted Bitbucket for compliance reasons needs a real answer for CI/CD before that migration, not an assumption that Pipelines "comes along."

Bitbucket Pipelines vs. the alternatives

☺ Like you're 10: A few tools all do the same basic job — read a YAML file, run some steps — so the real decision is usually about which company already has your account, not which one is secretly better.

At the level of "define steps in YAML, run them in containers, gate a deploy behind a manual click," Bitbucket Pipelines, GitLab CI/CD, and GitHub Actions are close enough in capability that the deciding factor is rarely a missing feature — it's which code host your team already committed to, and what else that host is bundled with.

OptionModelBest whenCosts you
Bitbucket PipelinesBuilt into Bitbucket Cloud; YAML pipeline, ephemeral Docker steps, deployment environmentsThe team already runs Jira and Bitbucket — single vendor, single SSO, deployment status syncs into the Jira ticket automaticallyBitbucket Cloud only, metered build minutes, no built-in matrix keyword, no first-party local runner
GitHub ActionsBuilt into GitHub; YAML workflows, a large third-party marketplace of reusable actions, native matrix buildsAlready on GitHub; want the biggest reusable-action ecosystem and native build-matrix supportMarketplace actions are a real supply-chain surface to vet; GITHUB_TOKEN and pull_request_target footguns to learn
GitLab CI/CDBuilt into GitLab; YAML pipelines, and — distinctively — SAST/DAST/dependency scanning bundled in on higher tiersAlready on GitLab, or want security scanning included rather than assembled from separate toolsRunner fleet is genuinely yours to operate if you self-host; Docker-in-Docker footguns of its own
JenkinsSelf-hosted, controller/agent, an enormous plugin ecosystem, source-neutralNeed hardware or network access no SaaS runner offers, or must support multiple, unrelated source hosts from one CI systemYou own the box: patching, plugin compatibility, and agent capacity are entirely your problem
CircleCISource-neutral SaaS CI with orbs (its own reusable-config format) and strong caching/parallelism controlsMulti-source-host org, or fine-grained control over Docker layer caching and test splitting matters more than ecosystem bundlingA separate vendor relationship and billing surface layered on top of whichever code host you actually use

The practical rule most teams land on: if Jira and Bitbucket are already the system of record for tickets and code, evaluate Pipelines first and require a specific, named gap — a feature it genuinely lacks for your case, not a vague sense that "real CI" should be a separate tool — before looking elsewhere. The Scaling CI/CD Across Teams page covers the org-wide version of this decision — shared pipeline templates, standardizing a golden-path YAML across dozens of repos — and applies just as directly whether that shared template is a Bitbucket definitions.steps anchor, a GitHub reusable workflow, or a GitLab include:. This course's certifications page doesn't include a Bitbucket-specific credential — Atlassian's certification track focuses on Jira and Confluence administration rather than Pipelines — so treat the YAML on this page as a transferable skill you're learning for the job, not for an exam.

🎬 At the Ship-It Guild
🦫

Benny the Beaver: Pushed to a release branch and the pipeline didn't run at all. I know the YAML's right, I copied it from main.

🦊

Foxy: Copied it where, though? Show me the branches: block.

🦫

Benny: …it's only under main:. release/* isn't listed anywhere, so it fell through to default — and default doesn't have the deploy step.

🐦

Pip the Hummingbird: Which explains why nobody paged when that release branch's smoke test never ran either. No pipeline, no test, no page — silence isn't the same as "all clear."

👺

Gizmo the Gremlin: Or just copy the whole main: block under a default: catch-all and never think about branch patterns again. 🤑

🐢

Timmy the Turtle: And now every stray branch someone pushes triggers a full deploy attempt. Add the specific pattern, Gizmo — precision here is the whole point of having priority rules at all.

🦫

Benny: Adding release/* next to main: now. And I'm putting a comment above it this time so the next person doesn't lose an afternoon to it.

✓ Checkpoint

1. Bitbucket Pipelines is a feature of which specific Bitbucket product, and what does a team on the other product typically use instead? 2. A push matches both a branches pattern and would also match default — which one actually runs? 3. Name the three deployment environments available on every plan, and one thing declaring deployment: production on a step actually does. 4. Bitbucket has no native build-matrix keyword — what technique does this page use instead to avoid duplicating near-identical steps? 5. What's the difference between an artifact and a cache in terms of what problem each one solves? 6. Name one concrete way Jira's Development panel benefits from Bitbucket Pipelines specifically, beyond what a generic smart commit already provides.

Check your answers
  1. Bitbucket Cloud. Bitbucket Data Center (self-hosted) has no built-in Pipelines equivalent — teams there typically pair Bitbucket with a separate CI tool such as Bamboo or Jenkins, triggered via webhooks and the build-status API.
  2. The branches match. Bitbucket evaluates the most specific matching trigger and runs only that one pipeline — default is the fallback used only when no more specific branches/pull-requests/tags pattern matches, never run alongside a more specific match.
  3. Test, Staging, and Production (custom names beyond these three require a paid tier). Declaring deployment: production on a step records it on the repo's Deployments dashboard, feeds Jira's Development-panel deployment sync, and unlocks environment-scoped variables and (on paid plans) deployment permissions restricting who can trigger it.
  4. Plain YAML anchors and merge keys (&name to define, <<: *name to inherit and then override specific fields) defined once under definitions.steps, rather than a dedicated matrix/strategy keyword.
  5. An artifact passes files forward from one step to later steps within the same pipeline run (expires after a retention window, has a size cap — not a long-term store). A cache persists something like node_modules or a dependency directory across separate pipeline runs to speed up repeated installs, and is keyed rather than automatically forwarded.
  6. Any of: the Jira issue's Development panel shows exactly which environment (Test/Staging/Production) the fix has actually reached, sourced directly from Pipelines' deployment steps — not just that a commit exists, but where it's live right now — without anyone manually updating the ticket or opening Bitbucket.