What is the difference between Jenkins declarative and scripted pipeline syntax, and what do you give up by choosing scripted?
answer
- schema versus program
- one is checked before it runs
- node block versus pipeline block
- conditional stages: directive or if
- the small escape hatch inside steps
basics
~20 sDeclarative wraps the build in a fixed pipeline/agent/stages/steps schema that Jenkins validates before running; scripted is a node block of ordinary-looking Groovy with real control flow. Scripted trades away up-front validation, the when directive and restart-from-stage for programmability.
solid answer
~50 sBoth run on the same Pipeline engine; the difference is how much structure Jenkins imposes. Declarative fixes the shape — `pipeline { agent … stages { stage { steps … } } }` — and validates the whole file against that schema before executing anything, so a typo in the last stage fails in seconds. The fixed shape is also what powers the `when` directive, the Blue Ocean editor, and "restart from stage". Scripted is a `node { }` block containing Groovy: you get `if`, `for`, `try/catch`, variables and methods directly, but nothing is checked until execution reaches it, and the structural features do not apply. The usual guidance is to stay declarative and drop into a `script { }` block inside `steps` only for the small piece that genuinely needs imperative logic — that keeps the validated skeleton while allowing the odd loop. A file that is mostly `script { }` is a signal you have written scripted syntax the hard way.
code
groovy · 11 linesnode('linux') {
stage('Build') {
checkout scm
sh './gradlew build'
}
if (env.BRANCH_NAME == 'main') {
stage('Deploy') {
sh './deploy.sh'
}
}
}go deeper
Recognise both shapes on sight: a pipeline { } block is declarative, a node { } block is scripted, and know that new pipelines normally start declarative.
Explain the tradeoff concretely — schema validation, when, restart-from-stage and the visual editor on one side, real Groovy control flow on the other — and describe what a script block does inside declarative.
Show judgment about where the escape hatch belongs: keep script blocks small, know when a pipeline is genuinely a program, and recognise a declarative file that has degenerated into scripted.
Own the standard across many repositories: whether scripted is allowed at all, what teams do instead when they need dynamism, and what that policy costs in flexibility versus what it buys in reviewability.
## Two syntaxes, one engine Jenkins Pipeline is a single execution engine with two front ends. Both produce the same durable, resumable build; both call the same steps (`sh`, `checkout`, `archiveArtifacts`); both live in a `Jenkinsfile` in the repository. What differs is how much freedom the file has. **Scripted** came first. A scripted Jenkinsfile is a Groovy script whose top-level construct is usually `node`: ```groovy node('linux') { stage('Build') { checkout scm sh './gradlew build' } if (env.BRANCH_NAME == 'main') { stage('Deploy') { sh './deploy.sh' } } } ``` `node` allocates an executor and a workspace; everything inside is ordinary program flow. Stages here are just labelled blocks you may create conditionally, in a loop, or not at all. **Declarative** came later as a schema on top of the same engine: ```groovy pipeline { agent { label 'linux' } stages { stage('Build') { steps { sh './gradlew build' } } stage('Deploy') { when { branch 'main' } steps { sh './deploy.sh' } } } } ``` The stage list is now static, the conditional is a directive rather than an `if`, and source checkout happens implicitly. ## What declarative buys **Validation before execution.** Jenkins parses the whole declarative file and checks it against the schema before the first step runs. A malformed deploy stage fails in seconds with a line number instead of after a twelve-minute build. **Directives instead of code.** `when` for conditional stages, `environment` for variables, `options` for build settings, `parameters`, `triggers`, `tools`, and `post` for after-the-fact blocks. Each is declarative-only: scripted has to express the same intent as Groovy you write and maintain yourself. **Tooling that depends on a static shape.** The Blue Ocean editor can round-trip declarative files because the legal shapes are enumerable. "Restart from stage" can re-enter a completed build at a named stage because the stage list is known statically — a scripted pipeline whose stages are generated in a loop has no such list. The declarative linter can validate a file over the CLI or a controller endpoint without running it. **A readable ceiling.** Because a reviewer knows the schema, they can read an unfamiliar declarative Jenkinsfile top to bottom and know where everything is. Scripted files drift toward being applications. ## What scripted buys Real programmability. Conditional and generated stages, loops over a list of services, `try`/`catch`/`finally` around arbitrary regions, local helper methods and classes, values computed early and used everywhere. When the pipeline genuinely is a program — dynamically discovering modules to build, fanning out over a computed list — scripted expresses it directly and declarative fights you. ## The script escape hatch Declarative is not a wall. Inside any `steps` block you may open a `script { }` block, and its contents are scripted Groovy: ```groovy stage('Version') { steps { script { def props = readProperties file: 'gradle.properties' env.APP_VERSION = props['version'] } sh "echo building ${env.APP_VERSION}" } } ``` The idiomatic rule follows from that: **stay declarative, and drop into `script` only where you must.** Everything inside a `script` block loses the schema check, so keep those blocks small and few. When a file becomes mostly `script`, you have written scripted syntax with extra indentation, and you would be better off either using scripted honestly or moving the logic behind a step of your own. ## How to choose Start declarative. It covers the overwhelming majority of build-test-deploy pipelines, it is what most teams and most documentation assume, and its failure modes are cheap. Reach for scripted only when the pipeline's structure genuinely cannot be written down in advance, and be aware of what you are giving up: fail-fast validation, `when`, restart-from-stage, and the visual editor. One caveat that catches people mid-migration: the two syntaxes do not mix at the top level. A file is either a `pipeline` block or a script — you cannot wrap `node { }` around `pipeline { }`, and the `script` block only flows one way, letting scripted code sit inside declarative and never the reverse.
- Can you nest a pipeline block inside a node block to get both?No. The two syntaxes do not compose at the top level: a Jenkinsfile is either a declarative `pipeline` block or a script. The flow only goes one way — a `script { }` block inside `steps` lets scripted Groovy run within declarative, but there is no construct that puts declarative inside scripted.
- What is a sign that a declarative pipeline should really have been written as scripted?When most of the work sits inside `script { }` blocks, or when the stage list itself has to be computed at runtime. At that point you have lost the validation and restart features anyway, so you are paying declarative's constraints for none of its benefits. Either move the logic behind a custom step, or use scripted deliberately.
- Why does restart-from-stage work for declarative pipelines but not reliably for scripted ones?Restarting requires knowing the stage list before execution. Declarative's stages are static and validated up front, so Jenkins can re-enter the build at a named stage. In scripted, stages are created imperatively — inside conditionals or loops — so there is no static list to restart into.
saying these in an interview costs you the question
- Says declarative and scripted are different products
- Claims scripted is more powerful so always better
- Thinks script { } removes only indentation, not validation
- Believes when works in scripted pipelines
- Wraps almost the whole pipeline in one script block