Why this matters
Pipeline templates let Platform Engineers ship guardrails and best practices once and reuse them across many repos. This cuts duplication, prevents drift, and speeds up delivery.
- Bake in security steps (SBOM, SAST/DAST, secrets scanning) so every service runs them by default.
- Standardize build, test, and deploy across languages and frameworks.
- Enable fast onboarding: new teams reference a template and ship on day one.
- Roll out fixes centrally (e.g., update a vulnerable action/plugin once, propagate everywhere).
Who this is for
- Platform/DevOps engineers building a paved path for multiple teams.
- Backend engineers maintaining many services who want consistency.
- Tech leads introducing standards without blocking developer velocity.
Prerequisites
- Comfort with at least one CI system YAML or Groovy (e.g., GitHub Actions, GitLab CI, Jenkins).
- Basic understanding of build/test/deploy stages and environments.
- Familiarity with Git branches/tags and semantic versioning.
Concept explained simply
A pipeline template is a reusable definition of a CI/CD workflow. Teams call the template and pass parameters, while the platform controls the core steps and quality gates.
Mental model
Think of templates as factory fixtures on a production line: they define the safe path and tooling. Parameters are the knobs teams can turn. Version tags are the model numbers that ensure predictable behavior. Policies are the guards that stop unsafe changes.
Core components of a good template
- Inputs: parameters like runtime version, test command, build target, environment name.
- Defaults: sensible choices so most teams use zero-config.
- Guardrails: mandatory stages for security, caching, and artifact integrity.
- Versioning: pin templates by tag (e.g., v1.4.2) to avoid breaking builds.
- Observability: emit metrics/log lines with template name and version.
- Documentation: examples for common stacks (Node, Python, Java, Go).
Worked examples
Example 1: GitHub Actions reusable workflow
Create a reusable workflow in a central repo. Teams call it with uses: and pin a version tag.
# .github/workflows/reusable-build.yml
name: reusable-build
on:
workflow_call:
inputs:
language:
required: true
type: string
test_command:
required: false
type: string
default: npm test
node_version:
required: false
type: string
default: '20'
secrets:
registry_token:
required: false
jobs:
build_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Detect template version
run: echo "TEMPLATE=reusable-build TEMPLATE_VERSION=v1" >> $GITHUB_ENV
- name: Setup Node
if: ${{ inputs.language == 'node' }}
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node_version }}
- name: Install deps
if: ${{ inputs.language == 'node' }}
run: npm ci
- name: Test
run: ${{ inputs.test_command }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build-logs
path: logs/**
Consumer repo:
# .github/workflows/ci.yml
name: service-ci
on: [push, pull_request]
jobs:
use-template:
uses: org/platform-repo/.github/workflows/reusable-build.yml@v1
with:
language: node
test_command: npm run test:ci
secrets:
registry_token: ${{ secrets.ORG_REGISTRY_TOKEN }}
Example 2: GitLab CI template with include
# platform-repo/.gitlab/ci/templates/node-build.yml
.default-node:
image: node:20
before_script:
- echo "TEMPLATE=node-build TEMPLATE_VERSION=v1"
cache:
key: npm-${CI_COMMIT_REF_SLUG}
paths: [node_modules/]
node_build_test:
extends: .default-node
stage: test
script:
- npm ci
- npm test
artifacts:
when: always
paths:
- junit.xml
Consumer repo:
# .gitlab-ci.yml
include:
- project: 'org/platform-repo'
file: '/.gitlab/ci/templates/node-build.yml'
ref: 'v1'
stages: [test]
node_build_test:
variables:
NODE_OPTIONS: --max-old-space-size=4096
Example 3: Jenkins shared library
// vars/ciPipeline.groovy
def call(Map cfg = [:]) {
pipeline {
agent any
options { timestamps() }
stages {
stage('Init') {
steps { echo "TEMPLATE=ciPipeline TEMPLATE_VERSION=v1" }
}
stage('Build & Test') {
steps {
script {
def lang = cfg.get('language', 'java')
if (lang == 'node') {
sh 'npm ci && npm test'
} else {
sh './gradlew build'
}
}
}
}
}
post { always { archiveArtifacts artifacts: 'build/**/*, test-results/**/*', allowEmptyArchive: true } }
}
}
Consumer Jenkinsfile:
@Library('company-ci@v1') _
ciPipeline(language: 'node')
Governance & versioning
- Use semantic versioning. Minor updates add features safely; patch fixes are non-breaking; major versions can change behavior.
- Pin consumers to a tag (e.g., v1.4.2) or a major tag (v1). Avoid using the default branch directly.
- Provide a deprecation window and an upgrade guide when introducing breaking changes.
Rollout strategy at scale
- Pilot: adopt with 1–3 teams. Incorporate feedback.
- Document: show example usage for common stacks.
- Encourage: offer a template generator snippet or copy-paste blocks.
- Measure: capture adoption, success/failure rates, and average duration per stage.
- Enforce gradually: require a template version label or gate via a policy stage.
Observability & developer experience
- Emit clear logs with the template name and version.
- Fail fast with friendly messages and hints to fix inputs.
- Expose overridable steps via parameters but keep defaults safe.
- Provide a CHANGELOG in the template repo and surface changes in CI logs.
Common mistakes and how to self-check
- Too many parameters: Hard to maintain. Self-check: Can a new team use defaults without reading docs? If not, reduce inputs.
- No version pinning: Builds change unexpectedly. Self-check: Can you reproduce last week’s build? If not, enforce pinning.
- Skipping security steps: Inconsistent posture. Self-check: Are there mandatory security stages? Confirm they cannot be bypassed.
- Poor error messages: Teams get stuck. Self-check: Do failures say what to change and show an example?
- Hidden magic: Surprising behavior. Self-check: Is every automatic step documented with rationale and opt-out policy?
Exercises
Complete these hands-on tasks. You can compare with the solutions below each exercise. Your progress in the test is available to everyone; only logged-in users get saved progress.
Exercise 1: Design a minimal reusable build-and-test template
Goal: Create a template that runs checkout, install, test, and uploads an artifact. It must support at least Node and Java via parameters.
- Inputs: language (node|java), test_command (default sensible), runtime version (e.g., node_version or java_version).
- Include a log line that prints TEMPLATE and TEMPLATE_VERSION.
- Provide a consumer example that pins a version.
Exercise 2: Add versioning and a deprecation guard
Goal: Introduce a breaking change safely.
- Create v1 and v2. In v2 change a default (e.g., enable test coverage).
- In v1, add a warning log suggesting upgrade to v2 with a link text or message.
- Demonstrate consumer configs pinned to v1 and v2.
Exercise 3: Roll out to two services and measure
Goal: Show how you would adopt the template in two services and capture metrics.
- Add an environment variable or log line that prints TEMPLATE_VERSION in each pipeline run.
- Add a simple timing metric around the test stage (start/end timestamps in logs).
- Describe how you would calculate adoption and failure rate over one week.
Show solutions
These reflect the solutions provided in the exercise details below.
See the Solutions inside each exercise in the Exercises panel of this page.
Exercise checklist
- Template supports at least two languages via parameters.
- Template emits version info in logs.
- Consumers pin a template version.
- Deprecation warning present in old version.
- Rollout metrics plan clearly stated.
Practical projects
- Create a "company-ci" repository containing templates for Node, Python, and Java. Each must share a security stage and artifact retention policy.
- Build a migration script or doc snippet that updates N repos to use the new template pinned to v1, with a dry-run mode.
- Implement a weekly job that lints consumer pipelines for template pins and prints a report of repos on v1 vs v2.
Learning path
- Start: Understand your CI system’s reuse mechanism (reusable workflows, includes, shared libraries).
- Design: Define minimal parameters and safe defaults. Add mandatory security and artifact steps.
- Version: Publish v1 with semver tags and docs. Enforce pinning.
- Scale: Pilot with 2–3 services, measure, improve, then roll out broadly.
- Advance: Add deployment templates, environment strategies, and policy checks.
Next steps
- Pick one language and publish a v1 template this week.
- Pilot with one team and gather feedback within two cycles.
- Plan v2 with minor improvements and a measured rollout.
Mini challenge
In 10 minutes, draft a parameter list for a deploy template that supports canary and blue/green without letting teams bypass approval. Keep it under 7 parameters and identify which two are mandatory.
Quick Test
Take the quick test to check understanding. Available to everyone; only logged-in users get saved progress.