When Directive in Jenkins
A complete, plain-English guide to running Jenkins pipeline stages only when the right conditions are true: every built-in condition, how Jenkins decides, and the traps that skip the wrong stage at the wrong time.
Picture a factory that makes toy cars. Every car goes through cutting, painting, and packing. But only the cars ordered by a shop with a special gift box also go through a “gift wrap” station. Nobody wants the gift wrap machine running on every car, and nobody wants a worker to remember by hand which cars need it. So the factory writes a simple rule on the station: “Run only when the order says gift box.” That rule is what the when directive does for a Jenkins pipeline.
Without such rules, pipelines turn into a wall of identical steps that run on every change, on every branch, at every hour. Tests take too long, deployments fire from the wrong branch, and cloud bills rise. With the when directive, one Jenkinsfile can behave like many: run quick checks on feature branches, run full tests on the main branch, and deploy only on a release tag. This guide teaches the whole topic step by step, assuming you know nothing about Jenkins yet.
1What Is the When Directive?
We begin with the simplest possible picture: what the directive is, where it sits, and what it does to a stage.
Jenkins is a tool that automates building, testing, and delivering software. You describe the work in a file called a Jenkinsfile. That file contains a pipeline, which is an ordered list of stages such as Build, Test, and Deploy. Each stage holds steps, the individual actions like running a command.
By default, a stage runs every time the pipeline reaches it. The when directive changes that. You place it inside a stage, and you describe one or more conditions. Before running the stage, Jenkins checks the conditions. If they are true, the stage runs. If they are not true, Jenkins skips the stage and moves on. In one sentence: the when directive is a gate placed at the door of a stage.
Think of a cinema. The ticket checker at the door of Screen 3 does not stop the whole building from working. The checker only decides who goes into Screen 3. Everyone else walks on to other screens. A when block is the ticket checker at one stage’s door. Other stages are untouched.
What you gain
One file, many behaviours
You keep a single Jenkinsfile for every branch and situation instead of maintaining copies.
Faster feedback
Slow stages run only when they matter, so developers on small changes get answers sooner.
Lower cost
Fewer unnecessary runs means fewer machine hours, fewer container starts, and a smaller bill.
Safer releases
Deployment stages can be limited to trusted branches or tags, so a stray feature branch cannot ship.
The when directive belongs to Declarative Pipeline syntax, the tidy style that starts with the word pipeline. It works at the level of a whole stage. It cannot skip a single step inside a stage. For that, you use a script section, which we compare in Chapter 12.
2Why Conditional Stages Exist
To see why the directive matters, imagine a pipeline without it and follow what goes wrong.
The world before conditions
In the early days of Jenkins, a job was a long list of shell commands that ran top to bottom. If you wanted the same code to behave differently on different branches, you created separate jobs, one per branch, each with slightly different settings. Ten branches meant ten jobs. A change to the build process meant editing ten places, and someone always forgot one.
Jenkins Pipeline, which became a built-in part of Jenkins with version 2.0 in 2016, moved the job description into a text file stored next to the code. The Declarative syntax arrived shortly after and reached its first stable release in early 2017. It came with the when directive from the start, because teams practising continuous delivery immediately needed a clean way to say “only deploy from the main branch”.
The problems it solves
- Wasted time. A documentation typo does not need a two-hour integration test suite.
- Wasted money. Stages that start large cloud agents or containers cost real money every time they start.
- Unsafe actions. A deployment that should only leave the main branch might run from an experiment.
- Copy and paste drift. Separate Jenkinsfiles for separate cases slowly disagree with each other.
- Noise. A page full of unrelated stage results hides the one that actually failed.
A fire drill is only needed when the fire alarm rings, not every ten minutes. A smart building has one rulebook: “If smoke is detected, open the emergency doors.” The building does not need a different rulebook for every hour of the day. The when directive lets a Jenkinsfile carry one rulebook for every situation.
3Jenkins Basics You Need First
A handful of Jenkins words appear in every condition. Learn them once here.
Controller and agent
The controller is the main Jenkins server that plans the work. An agent is a worker machine that does it. The controller is where conditions are usually decided.
Multibranch pipeline
A Jenkins job type that scans a Git repository and creates a pipeline for every branch, tag, and pull request it finds.
Branch
A line of development in Git. The main branch usually holds released code, and feature branches hold work in progress.
Pull request
A request to merge one branch into another, reviewed by teammates. Jenkins can build it to check the change before it merges.
Tag
A permanent label on one commit, often used for releases, such as a version number.
Commit and changeset
A commit is one saved change in Git. The changeset is the list of files changed since the last build.
Environment variables again
Many conditions read environment variables, which are named values that Jenkins keeps about the run. For example, when Jenkins builds a branch in a multibranch job, it stores the branch name in a variable called BRANCH_NAME. Several conditions read this value behind the scenes. Because these variables exist only in certain job types, the same condition can behave differently in different kinds of job. Keep this in mind, because it explains the most common surprise in the whole topic.
Some conditions, such as the branch and pull request ones, only make sense in a multibranch pipeline. In an ordinary single-branch pipeline job the information they depend on is not filled in, so they will not match the way you expect.
4Anatomy of the When Directive
Now we look at the shape of the block: where it goes, what it must contain, and how Jenkins reads it.
The shape
The word when is followed by curly braces. Inside the braces you list one or more conditions. Each condition is a small named test, such as branch followed by the branch name you care about. That is the whole structure.
The rules
It lives inside a stage
You cannot put it at the top of the pipeline. It gates a stage, and each stage may have its own.
It must contain at least one condition
An empty block is an error. Jenkins checks the file before running and reports the mistake.
Several conditions mean “all of them”
If you list more than one condition directly, Jenkins treats them as if they were wrapped in an “all of” group. Every one must be true for the stage to run.
True means run, false means skip
A skipped stage is not a failure. The build stays healthy and moves to the next stage.
Skipped stages are shown, not hidden
The build screen shows the stage greyed out, and the log records that the stage was skipped because of its when condition.
A club has a door rule: “Members only, and over 18.” Both parts must be true to enter. If you list two conditions in one when block, it is that kind of rule. Both are needed. If you want “members or guests with an invitation,” you must say “any of” explicitly, which we cover in Chapter 9.
What a skipped stage means for the rest
When a stage is skipped, everything inside it is skipped too: its steps, its own environment values, and its stage-level post actions. Later stages continue normally. This matters when a later stage depends on something the skipped stage was supposed to produce. If a Deploy stage expects a file made by a skipped Package stage, Deploy will fail for a reason that has nothing to do with Deploy. Keep related stages under matching conditions.
Before adding a condition to a stage, ask: “What later stage needs the output of this one?” Then give that later stage the same condition, or make it safe to run without the output.
5The Built-in Conditions at a Glance
Jenkins ships with a set of ready-made conditions. Here is the whole menu, each explained in a sentence, before we study the important ones in depth.
| Condition | Runs the stage when | Works in which job type |
|---|---|---|
branch | The branch name matches a pattern | Multibranch |
buildingTag | The build was started for a Git tag | Multibranch |
tag | The tag name matches a pattern | Multibranch |
changeRequest | The build is for a pull request, optionally filtered by details | Multibranch |
changelog | The commit message matches a text pattern | Any job with source control changes |
changeset | The changed files match a pattern | Any job with source control changes |
environment | A named environment variable equals a value | Any |
equals | Two values you supply are equal | Any |
expression | A small Groovy expression gives true | Any |
triggeredBy | The build was started by a chosen cause, such as a timer or a person | Any |
not | The nested condition is false | Any |
allOf | Every nested condition is true | Any |
anyOf | At least one nested condition is true | Any |
Notice the three groups. The first group asks about where the code came from: branch, tag, pull request. The second group asks about what changed: commit message and changed files. The third group asks about the state of the run: variables, expressions, and what started the build. The last three conditions are logic tools that combine the others.
Think of a doorman with a clipboard of questions. Where did you come from? What are you carrying? What time is it, and who invited you? Each built-in condition is one question on that clipboard. The doorman can ask one, several, or combine them with “and”, “or”, and “not”.
Jenkins and its plugins evolve. Your installed version may include extra options on some conditions, or differ slightly in details. The Pipeline Syntax help page on your own Jenkins server always shows the exact forms your installation supports.
6Branch, Tag, and Pull Request Conditions
These are the most used conditions. They answer the question: “Where did this build come from?”
The branch condition
The branch condition compares the current branch name to a pattern you give it. If you give the plain word main, only the main branch matches. You can also use a glob pattern, which is a simple wildcard style. A pattern such as release-* matches release-1, release-2.0, and so on. By default, patterns are read as globs. You can also switch the comparison style to an exact match or to a regular expression when you need more precision.
A post office sorts letters by postcode. A rule like “all postcodes starting with 560 go to the south van” is a wildcard rule. It saves you from listing every single postcode. A branch pattern with a star does the same job for branch names.
The biggest surprise: only in multibranch jobs
The branch condition depends on the branch name that Jenkins records for the run. That name is only recorded for multibranch pipelines. In a plain pipeline job, the value is not there, so the condition does not match, and your Deploy stage will be skipped even on the branch you meant. If your when conditions seem to be ignored, check the job type first. This is the number one reason people believe the when directive is broken.
Pull request builds in a multibranch job usually carry a branch name such as PR-42, not the name of the branch the author worked on. So a condition that says branch main will not match a pull request build, even if the pull request targets main. This is often what you want, and sometimes a shock.
The pull request condition
To detect a pull request explicitly, use changeRequest. Used alone, it is true when the build is for a pull request. You can add filters so it is true only for certain pull requests, such as those aimed at a particular target branch, or those from a certain author, or with a matching title. This lets you run extra checks on pull requests that target the release branch, while leaving other pull requests on a quicker path.
Pattern: quick checks for pull requests, full checks for main
A team gives the unit test stage no condition, so it always runs. They give the slow end-to-end test stage a condition for the main branch only. Pull requests get feedback in minutes, and main gets the full suite before any release.
The tag conditions
Release processes often start with a Git tag. The buildingTag condition is true when the build was started for a tag. The tag condition goes further and compares the tag name to a pattern, so a pattern like v* matches every version tag. Together they let you build a stage that publishes a release only when someone creates a proper release tag.
| Situation | Condition that fits |
|---|---|
| Run only on the main branch | branch with the name main |
| Run on every release branch | branch with a glob such as release-* |
| Run only on pull requests | changeRequest |
| Run on pull requests aimed at one branch | changeRequest with a target filter |
| Run only for any tag | buildingTag |
| Run only for version-style tags | tag with a pattern like v* |
7Changed Files and Commit Messages
Sometimes the question is not “where” but “what”. These conditions look at what actually changed.
The changeset condition
The changeset condition is true when at least one changed file matches a pattern. This is a favourite in monorepos, which are single repositories holding many services. If only the billing service folder changed, there is no reason to rebuild and retest the shipping service. A stage for the shipping service can carry a changeset condition for its own folder, and Jenkins will skip it whenever nothing in that folder moved.
A building inspector who is told only the kitchen was renovated does not re-inspect the bedrooms. They check the kitchen and go home. A changeset condition tells Jenkins which “rooms” changed so it inspects only those.
Points to know about changesets
- It looks at the changes Jenkins recorded for this build, which are the changes since the previous build. It is not a comparison of the entire repository.
- The very first build of a branch may have no recorded changes, so a stage guarded only by
changesetcan be skipped on the first run. Plan for that. - Patterns are globs by default, and there are options to control case sensitivity and to choose other comparison styles.
- A shared file changes everything. If several services depend on a common library folder, include that folder in each service’s condition.
- Pull request and branch builds can differ in what they record as changed, so test your condition on both.
The changelog condition
The changelog condition looks at the commit messages in the change list. It matches a text pattern, so a team can agree that a message containing “[skip deploy]” means “do not deploy this one”. It is simple and it works, but it relies on human discipline, and someone must remember to type the magic words. Use it for convenience switches, not for anything that protects production.
Prefer changeset over changelog whenever you can. Files that changed are facts. Words in a commit message are only habits.
Strengths of changeset gates
- Saves large amounts of build time in monorepos.
- Reads real facts about the change.
- Needs no special commit message habits.
Weaknesses of changeset gates
- Can be skipped on a first build with no recorded changes.
- Misses shared code that lives in another folder.
- Can hide a broken build until the next change touches that folder.
8Environment, Equals, Expression, and Parameters
When none of the ready-made source conditions fit, these flexible conditions let you test almost anything.
The environment condition
The environment condition checks whether a named environment variable equals a value. You give the variable name and the expected value. It is neat when a setting such as the deployment target is stored in a variable, and you want a stage to run only for one target.
The equals condition
The equals condition compares two values that you provide, an expected one and an actual one. It is the tidy way to test a build parameter. For example, a yes-or-no parameter asking “should we deploy?” can be compared to the value you expect.
The expression condition
The expression condition is the most powerful and the most dangerous. You write a small Groovy expression, and the stage runs when it gives true. You can test anything: a parameter, a variable, a calculation. Because it is free-form, it is also the easiest place to make a mistake.
The ready-made conditions are like a set of labelled light switches in a house. The expression condition is like handing a stranger a screwdriver and the fuse box. It can do anything the switches can, and much more, but a small slip can leave you in the dark.
The text-versus-truth trap
Environment variables are always text. Even if you set a variable to the word false, Jenkins stores the text “false”. In Groovy, any non-empty text counts as true when tested for truthiness. So an expression that simply asks whether the variable is set will say yes, and the stage will run even though the value says false. The safe habit is to compare the text to the exact word you expect, and never to test bare text for truth.
Build parameters behave differently. A parameter defined as a true-or-false type is a real boolean when read through the parameters object, and it can be tested directly. But the same parameter read through the environment becomes text again. Decide which way you read it, and stay consistent.
| How the value is read | Its type | Safe way to test it |
|---|---|---|
| Environment variable | Text | Compare to the exact word, such as “true” |
| Boolean build parameter, read as a parameter | True or false | Test it directly, or compare with equals |
| Boolean build parameter, read as an environment variable | Text | Compare to the exact word |
| Choice parameter | Text | Compare to the chosen option |
A variable defined in the same stage’s own environment block may not be visible yet when that stage’s when is evaluated. To be safe, define values that conditions depend on at pipeline level, and test the result on your own Jenkins version.
9Combining Conditions: Not, All Of, Any Of
Real rules are rarely one question. This chapter shows how to combine tests, like Lego bricks, into exactly the rule you need.
not
Flips a condition. “Not the main branch” runs the stage on every branch except main.
allOf
True only if every nested condition is true. This is what you get by default when you list several conditions.
anyOf
True if at least one nested condition is true. “Main branch, or any release branch.”
Nesting
You can place these inside each other to build rules such as “on main, or on a tag, but only if the deploy flag is on”.
flowchart TD
A["Stage reached"] --> B{"Is the build for the main branch"}
B -->|Yes| D{"Is the deploy flag on"}
B -->|No| C{"Is the build for a version tag"}
C -->|Yes| D
C -->|No| S["Skip the stage"]
D -->|Yes| R["Run the stage"]
D -->|No| S
The picture shows the thinking. First ask where the build came from: the main branch, or a version tag. If either is true, move on to the flag. If the flag is also on, run. Any other path ends in a skip. In the Jenkinsfile, this is an allOf group that contains one anyOf group and one flag check.
A caution on evaluation order
Jenkins generally works through nested conditions in order and may stop early once the answer is known, just as you stop reading a list of requirements once one fails. Do not build conditions that depend on a side effect of an earlier one, and keep every condition free of surprises such as running commands or changing values. A condition should only ask a question. It should never do work.
A theme park ride sign says: “You must be 120 cm tall, and either have a wristband or a paper ticket.” The staff check height first. If you are too short, they do not bother checking your ticket. Nested conditions work in the same spirit.
If a rule needs more than three or four nested layers, that is a sign to simplify. Compute one clear yes-or-no early in the pipeline, store it, and let the stage test that single value.
10Build Causes with triggeredBy
A build can start for many reasons. The triggeredBy condition lets a stage react to why the build began.
Every Jenkins build records a cause: the reason it started. Common causes include a person pressing the build button, a timer firing on a schedule, a change detected in source control, or another job finishing upstream. The triggeredBy condition is true when the build’s cause matches the one you name. You may also give extra detail, such as a specific user.
| Cause | Meaning | Typical stage that uses it |
|---|---|---|
| A timer | A scheduled run, often overnight | Long, expensive test suites |
| A person | Someone pressed the build button | Manual maintenance or cleanup stages |
| Source control change | Jenkins noticed a new commit | Normal build and quick tests |
| Upstream job | Another Jenkins job finished and started this one | Follow-on packaging or publishing |
A restaurant kitchen treats an online order, a phone order, and a walk-in order the same at the stove but differently at the counter. Where the order came from decides who packs it and how. The build cause tells a stage where its order came from.
Pattern: the nightly deep check
A team keeps day-time builds quick by giving a two-hour performance test a triggeredBy timer condition. It runs on the overnight schedule, and developers still get their results in minutes during the day.
Pattern: manual cleanup
A rarely needed cleanup stage carries a condition for a person starting the build. It never runs by accident from a commit, only when someone deliberately asks for it.
The cause names you can use depend on the plugins installed on your Jenkins. Check the Pipeline Syntax help page on your server to see the exact cause names available to you.
11How It Works Internally and When It Is Evaluated
This chapter lifts the lid on the directive. Knowing the order of events explains why an agent sometimes starts for a stage that then gets skipped.
The moment of decision
When Jenkins starts a run, the controller reads the Jenkinsfile and turns the Declarative syntax into an internal plan. As the run reaches each stage, the controller checks whether the stage has a when block. If it does, the controller works out the conditions using the information it holds about the run: the branch name, the change list, the environment variables, the parameters, and the build cause. The answer is yes or no. Yes means the stage runs. No means Jenkins marks it skipped and moves to the next one.
Most conditions are simple lookups against data the controller already holds, so they are fast. An expression condition runs a small piece of Groovy on the controller, and Jenkins normally runs that in a protected mode called the sandbox, which limits what it may call. This protection matters, and it is why some clever code you would happily run on your laptop is rejected in a pipeline until an administrator approves it.
The surprise: the agent may already be running
By default, if a stage names its own agent, Jenkins first gets that agent ready and only then evaluates the when block. That means a stage can start a machine, start a container, or allocate a cloud worker, and then discover that its condition is false and skip everything. For cheap agents this is unimportant. For expensive ones, such as large cloud machines or Kubernetes pods, it is real waste.
The fix is a small option inside the when block called beforeAgent. When you set it to true, Jenkins evaluates the conditions first and only starts the agent if the answer is yes.
sequenceDiagram
participant Ctl as Jenkins Controller
participant Cond as When Conditions
participant Agt as Agent for the Stage
Note over Ctl,Agt: Default behaviour
Ctl->>Agt: Request the stage agent
Agt-->>Ctl: Agent is ready
Ctl->>Cond: Evaluate the conditions
Cond-->>Ctl: False so skip the stage
Note over Ctl,Agt: With beforeAgent set to true
Ctl->>Cond: Evaluate the conditions first
Cond-->>Ctl: False so skip the stage
Ctl->>Ctl: Move on with no agent started
Imagine calling a taxi before checking whether the party is still on. If the party is cancelled, you paid the taxi for nothing. Checking first, then calling the taxi, is what beforeAgent gives you.
Three timing options
| Option | Effect | Why use it |
|---|---|---|
beforeAgent | Evaluate conditions before the stage’s agent starts | Avoid paying for agents that will not be used |
beforeInput | Evaluate conditions before the stage waits for human approval | Do not ask a person to approve a stage that will be skipped |
beforeOptions | Evaluate conditions before the stage’s options are applied | Avoid taking locks or starting timers for a skipped stage |
When more than one is set, Jenkins follows a fixed priority, with the options step first, the input step second, and the agent step third. Availability of some of these options depends on the Declarative Pipeline plugin version, so confirm what your server supports.
Make beforeAgent your habit for any stage that has its own agent, especially container or cloud agents. It costs nothing and often saves real money.
Nested and parallel stages
Stages can be nested inside other stages, and stages can run side by side in parallel groups. Each stage can carry its own when. If the outer stage is skipped, everything inside it is skipped without further questions. If the outer stage runs, each inner stage checks its own condition in turn. Matrix-style stages, which run the same work over combinations of values, can also carry a when. Check your Jenkins version for the exact places it is honoured.
Saved state
Jenkins saves the progress of a pipeline so it can resume after a restart. Conditions are evaluated as each stage is reached, using the data recorded for the run, so a restart in the middle of a run does not change the meaning of the conditions that have already been decided.
12When Versus Other Ways of Deciding
The when directive is not the only way to make a pipeline choose. Picking the right tool keeps your file clear.
| Tool | Scope of decision | Style | Best for |
|---|---|---|---|
The when directive | A whole stage | Declarative, readable | Deciding if a stage runs at all |
An if inside a script section | One or more steps | Groovy code | Skipping individual steps, or complex logic |
| Post conditions such as always, success, failure | Actions after a stage or the run ends | Declarative | Reacting to the result of the run |
Scripted Pipeline if statements | Anything | Full Groovy | Advanced flows where Declarative is too rigid |
| Build parameters | The values people choose | Input form | Human choices that feed a condition |
An important distinction is between before and after. The when directive decides before a stage whether it should run. The post conditions decide after the work is done what to do next depending on the result. Both use similar-looking words, but they answer different questions.
A fridge magnet that says “Only water the plants if the soil is dry” is a rule checked before watering. A note that says “If the plant wilts, call the gardener” is a rule checked after. The first is when. The second is a post condition.
Choose when for
- Gating whole stages such as Deploy or Integration Test.
- Rules that a colleague should be able to read at a glance.
- Clear skipped-stage display on the build screen.
Avoid when for
- Skipping one step within a stage. Use a script section.
- Long, tangled logic. Simplify or move it into a shared script.
- Anything that should depend on the outcome of the stage itself.
13Security Considerations
Conditions are often used to guard deployments. That makes it important to know what they can and cannot protect.
A condition is a rule, not a lock
A when block that says “deploy only from the main branch” is a rule written in the Jenkinsfile. Anyone who can change the Jenkinsfile can change the rule. On a pull request from a contributor, the Jenkinsfile in that pull request may be the one that gets used, depending on how your job is configured. A malicious change could remove the condition and add a step that steals secrets. Therefore a when block must never be your only line of defence.
Trusted Jenkinsfile source
Configure multibranch jobs so that changes to the pipeline file from untrusted contributors are not used for builds that can reach secrets.
Scoped credentials
Store deployment credentials where only trusted jobs or branches can use them, so a skipped rule cannot expose them.
Branch protection
Use your Git host to require reviews before code reaches the branches that trigger deployment.
Approval gates
For production, add a human approval step so a single condition is never the only thing between code and users.
Expression conditions and the sandbox
The expression condition runs Groovy on the controller. Jenkins guards this with the sandbox and a script approval process. When a Jenkinsfile in a repository uses something the sandbox blocks, an administrator sees an approval request. Approve these only after reading exactly what the code does. Blindly approving a method can open a path for someone to run harmful code on your controller.
Do not let user input drive a condition unchecked
If a build parameter is free text and you feed it into a condition, someone could type a value that triggers a stage you did not intend. Prefer parameters with fixed choices, and compare them to known values.
A skipped stage is silent. If a security scan is guarded by a when block and the condition is accidentally never true, the scan simply never runs, and everything looks green. Review conditions on security stages as carefully as the scans themselves.
14Monitoring, Logging, and Troubleshooting
When a stage runs that should not, or does not run that should, here is how to find out why without guessing.
What Jenkins shows you
- The stage view. Skipped stages are drawn in a faded style, so you can see at a glance which ones did not run.
- The console log. Jenkins writes a message that a stage was skipped due to its
whenconditional. This is your first clue. - The Pipeline Syntax page. It lists the exact condition forms available on your server.
- Replay. Lets you edit and rerun the pipeline to test a changed condition without committing.
flowchart TD
A["A stage ran or skipped unexpectedly"] --> B{"Is the job a multibranch pipeline"}
B -->|No| C["Branch and pull request conditions cannot match so use another test"]
B -->|Yes| D{"Did you print the values the condition reads"}
D -->|No| E["Print the branch name and variables in an earlier stage"]
D -->|Yes| F{"Is the value text compared as truth"}
F -->|Yes| G["Compare to the exact word instead"]
F -->|No| H{"Is it a first build or a pull request"}
H -->|Yes| I["Check the changeset and the PR branch name"]
H -->|No| J["Review nesting and the logic between allOf and anyOf"]
Common symptoms and causes
| Symptom | Likely cause | What to do |
|---|---|---|
| Deploy stage never runs on main | The job is not multibranch, so the branch name is not recorded | Use a multibranch job, or test something else |
| Stage skipped on a pull request into main | The build’s branch name is the pull request label, not main | Use changeRequest with a target filter |
| Stage runs even though a flag says false | The flag is text, and any non-empty text is treated as true | Compare to the exact word |
| Changeset stage skipped on the first build | No changes were recorded yet | Add another condition for a first build, or a manual override |
| Agent started for a skipped stage | Conditions are evaluated after the agent by default | Set beforeAgent to true |
| Later stage fails after a skip | It needed output from the skipped stage | Give it a matching condition |
| Approval prompt for a skipped stage | Conditions are evaluated after the input by default | Set beforeInput to true |
A helpful debugging habit
Add a small stage near the top, with no condition, that prints the branch name, the change identifier, the build cause, and the parameters. Run the pipeline once and read the values. Most puzzles solve themselves once you see the real values that the conditions receive. Remove this stage when you are done, and never print secrets in it.
Signals worth tracking over time
Count how often each guarded stage is skipped. A stage that is skipped one hundred percent of the time may have a condition that can never be true. A stage that never skips may have a condition that is too loose. Both are worth a look during regular pipeline reviews.
15Design Patterns and Anti-patterns
Patterns are habits that keep pipelines calm. Anti-patterns are habits that keep creating puzzles at two in the morning.
Patterns that help
Fast lane, slow lane
Quick checks run always. Slow or expensive checks are gated to main, to nightly builds, or to release tags.
Named decision variable
Work out one clear yes-or-no early, such as “should deploy”, and let stages test that single value instead of repeating long rules.
Guarded agents
Use beforeAgent on every stage with a costly agent so nothing starts for a skipped stage.
Monorepo per-folder gates
Give each service its own stage with a changeset for its folder plus shared code folders.
Tag-based releases
Publish only when a tag matching a version pattern is built, so releases are deliberate and traceable.
Manual escape hatch
Give important stages a boolean parameter to force them on, so a person can override a condition when needed.
Anti-patterns to avoid
Context
A team guards a stage with a bare Groovy expression that asks whether a text variable is set.
What goes wrong
The variable holds the word false. Any non-empty text counts as true, so the stage runs anyway. A test suite is skipped, or a deployment happens when it should not.
Better decision
Compare the text to the exact word expected, or read a typed boolean parameter as a parameter.
Context
Every stage carries a slightly different, hand-written condition, copied and adjusted over time.
What goes wrong
Nobody can explain the full behaviour of the pipeline. A change to the branching strategy means editing a dozen blocks, and one is always missed.
Better decision
Define one or two named decision values near the top, and have stages test those. When the rule changes, there is one place to edit.
Context
A security scan stage is guarded by a condition that only runs it on the main branch.
What goes wrong
Vulnerabilities enter through feature branches and are only found after merging, when they are costlier to fix. And if the condition breaks, the scan quietly never runs.
Better decision
Run cheap scans on every change. Gate only the heaviest ones, and alert when a critical stage has not run for a set period.
16Worked Scenario: One Pipeline, Five Situations
Let us put everything together. We follow a single imaginary Jenkinsfile through five different events and see which stages run each time.
The imaginary pipeline
A small team runs an online shop. Their multibranch Jenkinsfile has six stages. Compile and Unit Tests have no condition, so they always run. Integration Tests carry a condition for the main branch, and also run when the build was started by the overnight timer. Security Deep Scan runs only for the overnight timer. Publish Package runs only when a version-style tag is built. Deploy to Staging runs on the main branch only, and has beforeAgent set so no cloud machine starts for other builds.
| Stage | Feature branch push | Pull request into main | Merge to main | Version tag | Overnight timer |
|---|---|---|---|---|---|
| Compile | Runs | Runs | Runs | Runs | Runs |
| Unit Tests | Runs | Runs | Runs | Runs | Runs |
| Integration Tests | Skipped | Skipped | Runs | Skipped | Runs |
| Security Deep Scan | Skipped | Skipped | Skipped | Skipped | Runs |
| Publish Package | Skipped | Skipped | Skipped | Runs | Skipped |
| Deploy to Staging | Skipped | Skipped | Runs | Skipped | Skipped |
Reading the table like a story
A developer pushes to a feature branch
Only compile and unit tests run. The developer has a result in a few minutes and the shared machines stay free.
The developer opens a pull request into main
The build name is a pull request label, so the main branch condition does not match. Reviewers see the same quick checks.
The pull request is merged
Now the build is for the main branch. Integration tests run, then the staging deployment starts on its own cloud agent.
Someone creates a version tag
The tag build compiles, tests, and publishes the package. Nothing is deployed, because deployment is tied to the main branch.
The overnight timer fires
The heaviest checks run while everyone sleeps: integration tests and the deep security scan.
A hospital has many departments, but a patient with a sore throat does not visit all of them. Reception looks at the reason for the visit and sends the patient only to the departments that matter. The Jenkinsfile is the hospital, each stage is a department, and each when block is the receptionist’s rule for that department.
Draw a table like the one above for your own pipeline before you write any conditions. If a cell surprises you, fix the design first, then write the code.
Spotting a hidden problem
Notice that the version tag build skips Integration Tests. That means a tag could be published from a commit whose integration tests have never been run in that exact build. In this design the team relies on the fact that tags are created only from commits already tested on main. That is a reasonable choice, but it is a decision the team should make consciously, and write down. Tables like this one are excellent for uncovering such hidden assumptions.
17Performance, Scalability, and Cost
The directive is small, but its effect on speed and cost can be large. This chapter explains why.
The cost of a build you did not need
Every stage that runs uses some resource: processor time, memory, network, and often money. A large organisation may run thousands of builds per day. If each build runs ten minutes of unnecessary work, that adds up to hundreds of hours of wasted machine time every day. Gating expensive stages so they run only when they matter is one of the cheapest ways to reduce that waste.
The numbers above are only an illustration to show the scale, since one thousand builds times ten minutes is ten thousand minutes, or about one hundred sixty-seven hours. Your own numbers will differ, but the shape of the problem is the same.
Where the savings come from
Fewer stage runs
A skipped stage uses almost no resources, so a well-placed condition removes its cost entirely.
Fewer agent starts
With beforeAgent, a skipped stage never starts its container, virtual machine, or cloud worker.
Shorter queues
Jenkins has a limited number of executors. When fewer stages compete for them, other builds wait less.
Faster feedback
A developer who gets an answer in five minutes instead of fifty can fix a problem while it is still fresh in mind.
Scaling across many repositories
As an organisation grows from a handful of pipelines to hundreds, the rules for who runs what start to matter more than any single stage. Teams with many repositories often place their standard conditions inside a shared library, which is a central bundle of pipeline code that every project can use. Then a change such as “run the deep scan only at night” is made once and reaches every project. Without such sharing, each repository slowly develops its own slightly different rules.
Cost of the conditions themselves
Most conditions are nearly free because they read data Jenkins already has. The exception is a condition that does real work, for example an expression that calls a slow service or reads a large file. Since conditions are evaluated on the controller, a slow one delays the decision and can put extra load on the busiest machine in your system. Keep conditions quick and simple. If a decision needs real work, do that work in an earlier stage, store the answer, and let the condition read the stored answer.
A supermarket cashier asks one quick question: “Do you have a loyalty card?” A cashier who phoned head office to check every customer’s history would make the whole line wait. Keep your conditions like the quick question, not like the phone call.
Saving time by skipping a stage is only a win if the stage was truly unnecessary. If a skipped check would have caught a real defect, the savings are paid back later with interest. Always weigh speed against the protection the stage gives.
18Advantages, Trade-offs, Best Practices, and Common Mistakes
A balanced view of the directive, and the habits that keep it working for you.
Advantages
- Readable, declarative rules that teammates understand quickly.
- One Jenkinsfile can serve every branch and situation.
- Skips whole stages cleanly, with a visible record.
- Saves time, money, and machine capacity.
- Ready-made conditions cover most common needs.
- Composable through not, all of, and any of.
Disadvantages and trade-offs
- Works only at stage level, not step level.
- Some conditions need multibranch jobs.
- Skips are silent, so broken conditions can hide problems.
- Values are text, so truth tests are easy to get wrong.
- Evaluation timing defaults can start agents needlessly.
- Complex nesting becomes hard to read.
Best practices in one place
- Prefer facts over habits. Use branch, tag, and changeset conditions before commit message conditions.
- Use multibranch jobs when your rules depend on branches, tags, or pull requests.
- Set
beforeAgentto true on stages that have expensive agents. - Compare text to exact words. Never test bare text for truth.
- Keep conditions free of side effects. They should only ask questions.
- Keep related stages under matching conditions, so no stage waits for output that was skipped.
- Name your decisions. A single clear value near the top beats many copies of a long rule.
- Add an override. A boolean parameter lets a person force a stage when needed.
- Watch skip rates. A stage that never runs, or always runs, deserves a review.
- Protect deployment separately. Use scoped credentials, branch protection, and approvals in addition to conditions.
The mistakes seen most often
Mistake 1: branch condition in a plain job
A team writes a condition for the main branch, but the job is a single-branch pipeline job. The branch name is not recorded, so the deploy stage is always skipped. The fix is to use a multibranch job.
Mistake 2: forgetting the pull request naming
A team expects pull requests into main to match a condition for main. The build’s branch name is the pull request label, so they never match. The fix is to use changeRequest with a target filter.
Mistake 3: skipping a dependency
A Package stage is gated by a changeset, but the Deploy stage that uses its output is not. When no files change, Package is skipped and Deploy fails. The fix is to match the conditions.
Mistake 4: paying for skipped agents
A Kubernetes agent starts for a stage that will be skipped, every build. The fix is to set beforeAgent to true.
19Real-World Usage Patterns
Here is how the when directive tends to appear in real delivery pipelines. These are illustrative patterns in general terms, not claims about the private setups of any particular company.
Jenkins is one of the most widely used automation servers, and large engineering organisations have publicly described running it at scale. What tends to be common is a habit of careful gating: quick checks for everyone, heavier work for trusted branches, and deliberate release steps. The patterns below show that habit in practice.
Pattern: an online shop with fast pull requests
A shop’s pipeline runs compile and unit tests on every change. Browser-based end-to-end tests are gated to the main branch and nightly builds. Developers see results in minutes, while the shop still gets a full check before each release.
Pattern: a monorepo with many services
A company keeps fifty services in one repository. Each service has a stage with a changeset condition for its own folder and shared libraries. A change to one service rebuilds only that service and anything it depends on.
Pattern: version tags trigger releases
A software vendor publishes packages only when a tag that matches its version pattern is built. Nobody can publish by accident from an ordinary branch, and every published package traces to a tag.
Pattern: nightly deep checks
A bank keeps performance and security deep-scan stages behind a timer cause. They run overnight, and the results are waiting in the morning without slowing daytime work.
Pattern: promotion with approval
A delivery team deploys to a test environment automatically on main. The production stage has a branch condition, a beforeInput option, and a human approval, so nobody is asked to approve a deployment that would have been skipped anyway.
Jenkins is often used together with Git hosting services, Docker, Kubernetes, and cloud tools. The when directive is the piece that lets one Jenkinsfile respond sensibly to all the different events that these systems produce.
20Frequently Asked Questions
when directive belongs inside a stage. To keep an entire pipeline from doing anything on certain events, guard each of its stages, or control triggers and branch discovery in the job configuration.changeRequest condition. You can add filters, such as the target branch, to narrow it further.changeset condition for documentation files in a “not” group. The stage will then be skipped when the only changed files are documentation. Check how your Jenkins version reports changes for the first build and for pull requests.beforeAgent to true inside the when block to evaluate them first.when directive. Use an if inside a script section around that step.triggeredBy condition set to the timer cause. The exact cause names depend on installed plugins, so confirm them on your Pipeline Syntax help page.21Summary and Key Takeaways
Key Takeaways
- The
whendirective is a gate at the door of a stage. True means the stage runs, false means it is skipped, and a skip is not a failure. - Several conditions mean all of them. Use
anyOffor “either”,notfor “except”, and keep nesting shallow. - Know your job type. Branch, tag, and pull request conditions need a multibranch pipeline, and pull request builds carry a pull request name, not the source branch name.
- Every environment value is text. Compare to the exact word you expect, and never test bare text for truth.
- Set
beforeAgentto true on stages with costly agents so nothing starts for a stage that will be skipped. - Keep dependent stages consistent. If a stage is skipped, later stages that need its output must be skipped or made safe.
- A condition is a rule, not a lock. Protect production with scoped credentials, branch protection, trusted pipeline sources, and human approvals as well.