Skip to content

How to Write a Declarative Jenkins Pipeline

DodaTech 2 min read

In this tutorial, you'll learn about How to Write a Declarative Jenkins Pipeline. We cover key concepts, practical examples, and best practices.

The Problem

Your Jenkins job fails with WorkflowScript: 1: Expected a symbol @ line 1, column 1 or No such DSL method 'stage' found. This happens when you mix Declarative and Scripted syntax, or when the Jenkinsfile has structural errors. Declarative pipelines provide a simpler, structured syntax with built-in error handling, while Scripted pipelines offer full Groovy flexibility but require more careful coding.

Quick Fix

1. Write a basic Declarative pipeline

// Jenkinsfile at repository root
pipeline {
    agent any

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Build') {
            steps {
                sh 'mvn clean compile'
            }
        }

        stage('Test') {
            steps {
                sh 'mvn test'
            }
            post {
                always {
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }

        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                sh 'mvn deploy'
            }
        }
    }

    post {
        failure {
            echo 'Pipeline failed! Check the logs.'
        }
        success {
            echo 'Pipeline succeeded!'
        }
        always {
            cleanWs()
        }
    }
}

2. Use environment variables safely

pipeline {
    agent any

    environment {
        DOCKER_REGISTRY = 'registry.example.com'
        APP_VERSION = readMavenPom().getVersion()
    }

    stages {
        stage('Build Image') {
            steps {
                sh """
                    docker build -t ${DOCKER_REGISTRY}/myapp:${APP_VERSION} .
                """
            }
        }
    }
}

3. Run parallel stages

pipeline {
    agent any

    stages {
        stage('Test Suite') {
            parallel {
                stage('Unit Tests') {
                    steps { sh 'mvn test' }
                }
                stage('Integration Tests') {
                    steps { sh 'mvn verify' }
                }
                stage('Lint') {
                    steps { sh 'mvn checkstyle:check' }
                }
            }
        }
    }
}

4. Add conditional execution with when

stage('Deploy') {
    when {
        branch 'main'
        environment name: 'DEPLOY_ENABLED', value: 'true'
    }
    steps { sh './deploy.sh' }
}

5. Validate the Jenkinsfile syntax

curl -X POST -u user:token \
  --data-urlencode "jenkinsfile@Jenkinsfile" \
  "https://jenkins.example.com/pipeline-model-converter/validate"

Expected output:

Jenkinsfile successfully validated.

6. Key differences: Declarative vs Scripted

Feature Declarative Scripted
Syntax pipeline { } block node { } block
Stage structure Predefined stages { } Any Groovy structure
Error handling Built-in post { } blocks Manual try/catch/finally
Conditional execution when { } directive if/else in Groovy
Best for Simple to moderate pipelines Complex workflows

7. Use the Blue Ocean UI for visual pipeline debugging

# Open in browser
echo "https://jenkins.example.com/blue/organizations/jenkins/${JOB_NAME}/activity"

Blue Ocean shows a visual representation of each stage, making failures easier to spot.

Prevention

  • Start with Declarative for all new pipelines — it enforces a consistent structure
  • Use Scripted only when you need dynamic stage generation or complex conditionals
  • Validate the Jenkinsfile with the pipeline linter before committing
  • Keep the Jenkinsfile at the repository root with the exact name Jenkinsfile
  • Use Shared Libraries to reuse pipeline code across multiple projects

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro