Shared Libraries in Jenkins
Write your pipeline logic once and use it in a hundred projects. Learn how Jenkins Shared Libraries work, how to design them well, and how to keep them fast, secure, and easy to change.
Picture a company with fifty software teams. Each team has its own Jenkinsfile, and each Jenkinsfile contains almost the same two hundred lines: fetch code, build, test, scan for security problems, package, deploy. One day the security team announces a new scanning rule. Now fifty people must edit fifty files, and at least five of them will forget, make a typo, or quietly skip it.
A Jenkins Shared Library solves this. It is a central place where the common pipeline logic lives, written once, reviewed once, and improved once. Every team’s Jenkinsfile shrinks to a few lines that say “use the standard build, please.” When the standard changes, every team receives the change on its next run.
In this guide you will learn what a shared library is, how Jenkins finds and loads it, how to organise it, how to version and test it, and which mistakes hurt teams most. We assume you have never seen one before and explain each new word when it first appears.
1What Is a Shared Library?
A shared library is a collection of reusable pipeline code, stored in a source repository and loaded by many Jenkins pipelines.
Start with the basics
Jenkins is a free automation server that builds, tests, and delivers software. A pipeline is a written list of steps that Jenkins follows, stored in a text file called a Jenkinsfile. Pipelines are written in a language called Groovy, which runs on the same platform as Java.
A library, in programming, is a bundle of ready-made code that other programs can borrow. A shared library in Jenkins is exactly that: a bundle of pipeline code kept in its own repository, so that many Jenkinsfiles can borrow from it instead of repeating themselves.
Think of a company cookbook. Every restaurant in the chain could invent its own recipe for tomato sauce, and they would all taste a little different. Instead, head office writes one tested recipe in a shared cookbook. Each kitchen simply says “make the house tomato sauce.” When head office improves the recipe, every kitchen improves. The Jenkinsfile is the order slip, and the shared library is the cookbook.
What can it contain?
A shared library can offer three kinds of things:
- Custom steps: your own named commands, such as “buildAndPublish,” that behave like the built-in ones.
- Helper classes: organised, reusable code for more complicated logic.
- Resource files: templates, scripts, and configuration files that pipelines can load as text.
A shared library is ordinary code in a normal repository. It has branches, tags, code reviews, and history, just like the applications it builds.
2Why Shared Libraries Exist: The Problem and the History
Reuse is one of the oldest ideas in software. Pipelines simply needed to learn it too.
A short history
Early Jenkins jobs were configured by clicking through web forms, and every job was a private island. When Pipeline became a central part of Jenkins around version 2.0 in 2016, pipelines turned into code stored beside the application. That was a big step forward, because code can be reviewed and versioned. But it created a new problem: code that is copied gets out of date. The Pipeline Shared Groovy Libraries plugin arrived to give pipelines the same tool every programmer already uses to avoid copying: a library.
The problem without a library
Copy and paste
- Every team owns a slightly different copy.
- Fixes reach only the teams that notice.
- New joiners copy an old, broken example.
Giant Jenkinsfiles
- Files grow to hundreds of lines.
- Application people must understand deployment details.
- Mistakes hide in the noise.
The library solution
With a shared library, the platform or DevOps team owns the complicated parts. Application teams supply only what is unique to them: the name of their service, the language they use, and where they deploy. Security rules, notification messages, approval gates, and cloud settings are written once by specialists and used by everyone.
A city does not let every household build its own power plant. A central utility supplies electricity, and each home only plugs in. A shared library is the utility for your pipelines.
Real-world motivation
Organisations of all sizes run into this need as they grow. Even the Jenkins project’s own infrastructure team publishes a shared library so that its many build jobs behave consistently. The pattern scales from a company of five developers to one with thousands.
3Core Concepts: The Anatomy of a Library
A shared library is a repository with a fixed folder layout. Jenkins knows what each folder means.
vars
Holds global variables, which are the most common way to publish a custom step. Each file is named after the step it provides. This folder is the front door of your library.
src
Holds Groovy classes in the standard Java-style folder layout. Use it for larger, more structured logic that steps in vars can call.
resources
Holds any non-code files: shell scripts, configuration templates, report layouts. Pipelines load them as text with a special step.
Help files
A text or markdown file with the same name as a step in vars becomes its help page in the Jenkins documentation view.
test
Not special to Jenkins, but teams keep their automated tests here so the library is checked like any product.
Readme and changelog
Plain documents that tell users what each version changed. Users read these before upgrading.
Key vocabulary
| Word | Meaning | Where it appears |
|---|---|---|
| Global variable | A named object or step available in every pipeline that loads the library | vars folder |
| Custom step | A global variable that pipelines call like a built-in command | vars folder |
| Class | A structured bundle of data and behaviour | src folder |
| Resource | A plain file shipped with the library | resources folder |
| Version | A branch, tag, or commit that names a exact state of the library | Loading instruction |
You do not need all three folders. Many excellent libraries contain only a vars folder with a handful of steps. Start there and add src and resources when you truly need them.
4Architecture and Components
Three parties take part: your application repository, the library repository, and the Jenkins controller in the middle.
The players
The application repository holds the code being built and its short Jenkinsfile. The library repository holds the shared code. The controller is the Jenkins brain that reads the Jenkinsfile, fetches the library, and schedules work. An agent is the worker machine that runs the actual steps.
flowchart LR
APP["Application Repository with short Jenkinsfile"] --> CTRL["Jenkins Controller"]
LIB["Shared Library Repository"] --> CTRL
CFG["Global Pipeline Libraries Settings"] --> CTRL
CTRL --> CACHE["Library Cache on Controller"]
CTRL --> AG["Agent Machine"]
AG --> STEPS["Pipeline Steps Executed"]
STEPS --> TGT["Deployment Targets"]
CTRL --> HIST["Build History and Logs"]
Reading the diagram
The settings on the controller say where each library lives and which version is the default. When a run begins, the controller reads the Jenkinsfile from the application repository, sees which library it asks for, and fetches that library from its own repository. It may keep a copy in a cache so the next run is faster. The controller then interprets the pipeline and sends steps to an agent. Results and logs return to the controller and are stored in the build history.
A film director (controller) gets a short shooting script (Jenkinsfile) that says “use the standard car chase.” The director looks up the standard car chase in the studio’s stunt handbook (library), and then tells the stunt crew (agents) exactly what to do.
Where library code actually runs
An important detail: much of the library’s Groovy logic is interpreted on the controller, while commands such as compiling or testing happen on the agent. This split has consequences for performance and security, which we cover in later chapters.
5How Loading Works Internally
There is no magic. Jenkins follows a short, predictable routine every time a pipeline asks for a library.
Step one: the request
The Jenkinsfile asks for a library by name, and optionally a version. The common way is an annotation placed at the very top of the file. A second way is a special step named library that loads a library in the middle of a run. The annotation form is read before the pipeline starts. The step form happens while it runs and is useful when the library name or version is decided dynamically.
Step two: finding the definition
Jenkins looks at its list of configured libraries. It checks folder-level settings first and then the global settings. Each entry says how to reach the repository, what the default version is, and whether users may override that version.
Step three: the checkout
Jenkins performs a checkout, which means it downloads the requested version of the library from the source repository into a working area on the controller. If caching is switched on, Jenkins may reuse a previously downloaded copy of the same version instead of downloading again.
Step four: building the classpath
A classpath is the list of places where a program looks for code it can use. Jenkins adds the library’s src folder to the classpath, and it makes the files in vars available as global names. From that moment, the pipeline can call a step named after any file in vars, and can import any class from src.
Step five: running with the special interpreter
Pipeline code does not run as plain Groovy. It runs through a system called CPS, short for continuation passing style. In simple words, CPS lets Jenkins pause a pipeline, save exactly where it was, and continue later. That is how a pipeline can wait an hour for an approval, or survive a controller restart. Library code loaded this way is transformed by CPS as well.
Because of CPS, some ordinary Groovy tricks behave strangely inside pipelines. Certain sorting and looping helpers may fail or run slowly. Libraries have a special marker, called NonCPS, for methods that must run in normal Groovy. Use it sparingly and only for short calculations that never call pipeline steps.
Reading a novel with a bookmark. You can stop at any page, close the book for a week, and resume at the exact sentence. CPS is Jenkins’ bookmark. It costs a little extra effort at each page, but it makes long journeys safe.
Step six: freezing the version
Once a run has loaded a library version, that version stays fixed for the entire run. If somebody pushes a change to the library’s main branch during your run, your run does not notice. This is very good for consistency, and it means that a run can always be traced to one exact library state.
6Data Flow and Lifecycle of a Library Call
Follow one library-based run from the moment someone pushes code to the moment the result is stored.
sequenceDiagram
participant D as Developer
participant A as App Repository
participant J as Jenkins Controller
participant L as Library Repository
participant G as Agent
D->>A: Push code
A->>J: Notify of change
J->>A: Fetch Jenkinsfile
J->>J: Read library request and version
J->>L: Checkout requested version
L-->>J: Library code
J->>J: Build classpath and load steps
J->>G: Send steps to run
G-->>J: Logs and results
J->>J: Store build record with library version
The lifecycle in plain steps
Change
A developer pushes code. A webhook or a poll tells Jenkins about it.
Read
Jenkins fetches the short Jenkinsfile and sees which library and version it wants.
Fetch
Jenkins downloads that exact version of the library, or reuses a cached copy.
Load
Global steps and classes become available to the pipeline.
Execute
Library steps run. Some logic executes on the controller, and shell commands run on agents.
Record
Jenkins stores the log, the result, and which library version was used.
What happens when the library changes
Suppose the library team releases a fix. Pipelines that follow the main branch pick it up on their next run. Pipelines that pin a tag stay on the old version until their owners choose to upgrade. This gives each organisation a choice between “always the latest” and “only when I say so,” and we discuss the trade-off in the versioning chapter.
Print the library version at the start of every run, for example “Using ci-library version 2.4.0.” When something breaks, this single line tells you whether the library changed or the application did.
7Global Variables and Custom Steps (the vars Folder)
The vars folder is where most libraries begin, and where many of them stay.
One file, one step
Each Groovy file in vars defines one global variable. The file name becomes the name of the step. A file called buildApp gives every pipeline a step called buildApp. The name must be a valid identifier and, by convention, uses camel case: words joined together, each new word starting with a capital letter. Dashes and spaces are not allowed.
The call method
To make the variable behave like a step, the file defines a method named call. When a pipeline writes the step name followed by arguments, Jenkins runs that method. The method can accept simple values, a map of named settings, or a block of steps wrapped in braces, which lets your custom step run other steps inside it, just like the built-in timeout or withCredentials steps do.
Kinds of steps you might write
Action step
Performs one job, such as sending a standard chat message, tagging a release, or publishing an artifact.
Wrapper step
Runs the work you place inside it, adding setup and cleanup around it, such as timing, locking, or notifications on failure.
Map step
Accepts a named settings map, so callers write only the options they care about and everything else takes a sensible default.
Pipeline step
Defines an entire declarative pipeline inside the library. The Jenkinsfile then only supplies settings.
Documenting steps
Place a text or markdown file with the same name as the step in the vars folder. Jenkins shows its content in the built-in global variable reference, so users can read your instructions right inside Jenkins.
Think of steps as buttons on a microwave. “Popcorn” hides a lot of timing and power settings behind a single label. A good custom step is a well-named button: it does one clear thing, and the person pressing it does not need to know the internals.
Global variables are shared by name across the whole run. Do not store per-run data in variables that live outside a method, because state can leak between calls. Keep data inside methods, or in objects you create and pass around deliberately.
8Classes in src and Files in resources
When logic grows beyond a few lines, structured classes and loadable files keep it tidy.
The src folder
The src folder follows the standard layout used by Java projects: folders that match package names, and one class per file. Use it for logic that has several parts, needs its own tests, or is used by several steps. A step in vars stays small and calls into a class in src that does the heavy thinking.
Giving classes access to pipeline steps
Here is a detail that surprises beginners. A class in src does not automatically know about pipeline steps such as sh or echo. The pipeline must hand it a reference to the running pipeline, commonly named steps. The class then calls steps through that reference. This design makes classes easy to test, because in a test you can hand in a fake pipeline.
Serializable classes
Because a pipeline can be paused and resumed, everything it holds in variables must be savable to disk. In Groovy, this means classes should declare that they are serializable. If a class holds something that cannot be saved, such as an open network connection or a file handle, the pipeline may fail after a restart with a confusing error. Keep such objects short-lived and inside a single method.
The resources folder
Files in resources are not code. They are shipped with the library and loaded by a step called libraryResource, which returns the file’s content as text. Typical uses include shell scripts, email templates, configuration templates, and policy files. A step can load a file, replace placeholder words with real values, write the result to the workspace, and use it.
| Need | Best home | Why |
|---|---|---|
| A reusable command called from Jenkinsfiles | vars | Becomes a step with help text |
| Complex logic with several methods | src | Structured, testable, organised by package |
| A long shell script | resources | Keeps scripts out of Groovy strings |
| A settings template | resources | Plain text that specialists can edit |
A toolbox has a drawer of ready-to-use tools (vars), a cabinet of spare parts and bigger machines (src), and a folder of printed instruction sheets (resources). You keep them separate so that finding the right thing takes seconds.
9Configuring and Loading a Library
A library must be registered with Jenkins before pipelines can use it. There are several places to do that, and each has consequences.
Where to register
| Level | Who can use it | Trust level |
|---|---|---|
| Global (system settings) | Every pipeline on the controller | Trusted: may call almost anything |
| Folder | Only jobs inside that folder | Untrusted: runs inside the sandbox |
| Loaded directly in the pipeline | That pipeline only, via a repository address | Untrusted: runs inside the sandbox |
Important settings
- Name: the label pipelines use to request the library.
- Default version: the branch, tag, or commit used when a pipeline asks for no specific version.
- Load implicitly: if enabled, every pipeline receives the library automatically, without asking. Useful for a small set of universal helpers, risky for large ones.
- Allow default version to be overridden: if enabled, a pipeline may request any other version, including a private experimental branch.
- Include changes in recent changes: shows library commits in the change list of a run.
- Retrieval method: how Jenkins reaches the repository, usually through a source control plugin for Git or another system.
- Caching: reuse downloaded versions for a set time to speed up runs and lighten load on the repository server.
Ways for a pipeline to ask
Annotation at top of file
The simplest way. Name and optional version follow the at-sign. An underscore is often written after it, because the annotation needs something to attach to.
The library step
Loads a library while the pipeline runs. Handy when the version depends on a parameter or branch name.
Repository identifier
The library step can name a repository right in the pipeline, without any global registration. Convenient, but sandboxed and unreviewed.
Several libraries
A pipeline can request more than one library, each with its own version.
Overriding the default version is powerful. A developer who can override it can run pipeline code from any branch of the library repository. Turn this off for sensitive libraries, or make sure that every branch of the library is protected by code review.
10Versioning and Release Strategy
A change to a shared library can affect a hundred pipelines at once. Versioning is how you keep that power under control.
What a version can be
In a source repository, a version can be a branch (a moving line of work), a tag (a fixed label on one commit), or a commit identifier (an exact snapshot). A branch follows changes automatically. A tag or commit never moves, which makes it stable.
Semantic versioning
Many teams use semantic versioning, which writes versions as three numbers: major, minor, and patch. A patch change fixes a bug without changing behaviour. A minor change adds a feature without breaking anyone. A major change may break existing users and therefore needs care and announcements. For example, version 2.4.1 tells users they are on the second major generation, with four feature additions, and one fix.
flowchart TD
C["Library change proposed"] --> R["Code review and automated tests"]
R -->|"Fails"| F["Fix and retry"]
R -->|"Passes"| M["Merge to main branch"]
M --> T{"Type of change"}
T -->|"Bug fix"| P["Tag patch release"]
T -->|"New feature"| N["Tag minor release"]
T -->|"Breaking change"| B["Tag major release and write migration notes"]
P --> U["Teams upgrade on their schedule"]
N --> U
B --> U
Two philosophies
Follow the main branch
- Everyone gets fixes and improvements instantly.
- Policy changes reach all teams at once.
- Easy for users; no upgrade work.
Risks of following main
- A bad change breaks every pipeline together.
- Users cannot delay an upgrade.
- Failures seem random to users, who changed nothing.
Pin to a tag
- Runs are stable and reproducible.
- Teams upgrade when they are ready.
- Breakages are limited to teams that upgraded.
Risks of pinning
- Teams forget to upgrade and fall behind.
- Old versions keep old bugs and security gaps.
- Several versions must be supported at once.
A balanced approach
Many platform teams publish moving major-version branches or tags, such as a label meaning “latest version 2.” Users pinned to that label receive safe bug fixes and new features automatically, but never a breaking major change. A mandatory upgrade deadline for old majors keeps everybody roughly current.
Phone apps offer automatic updates or manual updates. Automatic keeps you secure but can surprise you. Manual keeps you steady but can leave you exposed. A good policy, for phones and for libraries, sits somewhere sensible in between.
11Designing Good Steps: Settings, Defaults, and Whole-Pipeline Templates
The quality of a library is judged by how pleasant its steps are to call.
Prefer named settings over long argument lists
A step that takes six values in a fixed order is easy to call wrongly. A step that takes a map of named settings is easy to read: each value announces its purpose. New settings can be added later without breaking old callers, because callers who omit them simply receive defaults.
Choose strong defaults
The default should be what most teams want, and it should be the safe option. A caller who supplies nothing should still get a secure, sensible build. Every extra setting that must be supplied is a chance for someone to get it wrong.
Validate the settings
The step should check what it receives and stop early with a clear message when something is wrong, such as an unknown environment name or a missing service name. A message like “buildApp: setting ‘language’ must be java, node, or python, but got ‘jave'” is worth a hundred puzzled chat messages.
The whole-pipeline template
The most powerful pattern is defining an entire declarative pipeline inside a library step. The Jenkinsfile then contains only one line, the step name, and a small settings map. The library owns every stage: checkout, build, test, scan, package, deploy, and notify. The platform team can change those stages for all users at once. The price is reduced freedom: teams with unusual needs must ask for a new option.
Toolkit of steps
The library offers many small steps. Each team composes its own pipeline from them. Great freedom, but teams may compose them badly.
Template with hooks
The library owns the stages but lets teams add their own steps at set points, such as before deploy. Balanced freedom.
Full template
The library owns everything, and teams only set options. Best for consistency and compliance, hardest for special cases.
A toolkit is a box of Lego bricks. A guided template is a Lego kit with a few spare bricks. A full template is a finished model in a display case. Pick based on how much creativity you want teams to have and how much consistency you must guarantee.
Start with a toolkit of small steps. Watch how teams combine them. When you see the same combination everywhere, promote it into a template. Designing from real usage beats designing from guesses.
12Security: Trusted and Untrusted Code
A shared library runs with power. Whoever can change it can change every pipeline that uses it.
The sandbox
Jenkins protects itself from careless or malicious pipeline code with the Groovy sandbox. Inside it, code may only call methods on an approved list. Anything else is blocked until an administrator approves it. The Jenkinsfile itself normally runs inside the sandbox.
Trusted libraries
Libraries registered in the global settings are trusted. Their code runs outside the sandbox and may call almost any method, including ones that read Jenkins internals. That freedom is useful for advanced platform work, and it is dangerous for the same reason. Treat every global library as if it were part of Jenkins itself.
Untrusted libraries
Libraries registered at folder level, or loaded directly in a pipeline, are untrusted. They run inside the sandbox just like a Jenkinsfile. They are safer, but they may hit approval requests when they need blocked methods.
| Question | Trusted (global) | Untrusted (folder or direct) |
|---|---|---|
| Runs in sandbox? | No | Yes |
| Can call internal Jenkins methods? | Yes | Only if approved |
| Who should control it? | A small, vetted platform team | Individual teams within their own folder |
| Main risk | Full power if the repository is compromised | Approval fatigue and blocked features |
Protect the library repository
The repository is now part of your security perimeter. Require code review for every change, protect the main and release branches so nobody can push directly, restrict who may create tags, and turn on signing where you can. Remember that anyone who can merge into a version that pipelines follow can run code in all of those pipelines.
Never put secrets in the library
A library is read by many people. Passwords, keys, and tokens do not belong in it. Use the Jenkins credentials system, and let library steps reference credentials by name. A step can request a credential through the usual credentials-binding steps, so the secret appears only during the run and is masked in logs.
Be careful with steps that accept a free-form command or script text from callers and run it. Such a step turns your trusted library into a remote control for whoever can edit a Jenkinsfile. Offer fixed, reviewed actions instead.
The cookbook in every kitchen is trusted because head office wrote it. If a stranger could slip a page into the book, every restaurant would cook whatever the stranger wanted. Protecting the library repository is guarding the printing press.
13Performance and Scalability
Libraries add a little cost to every run. At scale, small costs add up.
Where time goes
- Fetching: downloading the library from its repository at the start of runs. With hundreds of runs per hour, this can strain the repository server.
- Interpreting: the CPS system executes Groovy code more slowly than plain Groovy, especially in loops that call many steps.
- Controller load: a lot of library logic executes on the controller. Heavy calculations there slow down the whole Jenkins for everyone.
Practical remedies
Turn on library caching
Jenkins can keep fetched versions on the controller and reuse them for a set period. Runs start faster, and the repository server rests. Tags and commits are ideal for caching because they never change.
Move heavy work to agents
Do data crunching in shell commands or small programs run on agents, not in Groovy on the controller.
Keep steps small
Avoid loops that call dozens of steps. One shell command that does the whole job is cheaper than fifty tiny steps.
Keep the library lean
Very large libraries take longer to fetch and load. Split unrelated areas into separate libraries if size grows.
The NonCPS marker, carefully
Short pure calculations, such as sorting a list or processing a string, may run faster and more reliably when marked as NonCPS. The rule is strict: a NonCPS method must never call pipeline steps, and it must finish quickly. Break the rule, and pipelines can fail in strange ways.
Scaling the number of users
As adoption grows, so do the demands on the library team. Automate testing, publish a clear changelog, and provide a self-service way to ask for new options. Without these, the library team becomes a bottleneck, and teams start copying code again to escape the queue.
14High Availability and Reliability
A shared library becomes a dependency of everything. Dependencies must be dependable.
What if the library repository is down?
If Jenkins cannot reach the repository at the start of a run, the run cannot load the library and fails immediately. With caching turned on, a cached copy of a pinned version can still be used, which softens the blow. For critical pipelines, host the library repository on a highly available service, and consider a mirror that Jenkins can use as a fallback.
Survive restarts
Pipelines can survive a controller restart, but only if everything they hold can be saved. Make library classes serializable, avoid storing live connections in variables, and keep long waiting steps such as approvals in the pipeline itself, not in library code that holds state in memory.
Idempotent steps
A step is idempotent if running it twice has the same effect as running it once. Deploy and publish steps in a library should follow this rule, so that a retried run does no harm. Ask of every library step: what if this runs again with the same input?
Blast radius
The blast radius is how many things break when one thing breaks. A shared library has a large blast radius by nature. Reduce it in three ways: test every change well, release gradually, and let critical teams pin versions so they upgrade only after others have proven the release safe.
Canary
Release the new version first to a few friendly pipelines that follow a pre-release tag.
Observe
Watch failure rates and run times for those pipelines for a day or more.
Widen
Move the moving major-version tag forward so all followers receive the release.
Roll back if needed
Move the tag back to the previous release, and every follower recovers on its next run.
A bakery testing a new recipe gives a free sample to a few regulars before changing the whole menu. If the sample is a hit, the recipe goes on the menu. If not, the bakery has only disappointed a handful of people, not the whole town.
Moving a tag rewrites history that other systems may have cached. Document exactly how and when tags are allowed to move, and never move a fixed version tag such as 2.4.1. Only labels that are meant to move should ever move.
15Monitoring, Logging, and Metrics
A library used by hundreds of pipelines needs the same attention as any production service.
Logging from library steps
Steps should tell the reader what they are doing. Print a short line when a step begins and when it ends, and include the important settings. Use clear prefixes such as the step name so that a long log can be searched easily. Avoid printing secrets or very long dumps of data.
Record the library version
Jenkins records which library version each run used. Make it visible: print it at the start of the run and, if useful, add it to the build description. When a failure appears across many jobs at once, comparing library versions is the fastest way to find the cause.
Measure usage
Track which steps are used, by how many pipelines, and how often. Steps that nobody uses are candidates for removal. Steps used by everybody deserve extra testing and care. A simple approach is to have each step write a short standard line to the log or send a small counter to a monitoring system.
Measure health
After every release, compare failure rates and step durations before and after. A sudden jump in failures on one version is a strong hint that the release has a problem. Adoption numbers tell you how many teams still use old versions and need reminders to upgrade.
Deprecation warnings
When a step or setting is going away, make it print a visible warning that names the replacement and the removal date. Warnings in the log reach the people who need to act. Without them, removal day arrives as a surprise breakage.
Send a notification to the owning team when a library step fails in a way that looks like a library problem rather than an application problem, such as a validation error from inside a step. That gives you an early signal of confusing messages or missing documentation.
16Testing Shared Libraries
Code that runs a hundred pipelines must not be tested by running a hundred pipelines.
Why testing matters more here
An error in an ordinary pipeline hurts one team. An error in a shared library hurts every team that follows it. Tests are the cheapest insurance you can buy.
A layered strategy
Unit tests for classes
Classes in src can be tested like any Groovy code, on a laptop, in seconds, by handing them a fake pipeline object.
Step simulation
A testing framework called Jenkins Pipeline Unit can simulate steps in vars, letting you check which commands would run for chosen settings.
Integration on a test Jenkins
Run real pipelines against a disposable Jenkins to confirm that steps really work with real plugins and agents.
Sample projects
Keep a few tiny demo applications, one for each supported language, and build them with the new library version on every change.
Pre-release tags
Publish a release-candidate tag, and let volunteers try it before promotion.
Negative tests
Feed steps wrong settings on purpose, and confirm they stop with clear messages.
Make the library test itself
The library repository should have its own pipeline. On every proposed change, it runs the unit tests, the simulated steps, and the sample projects. A change that fails is blocked from merging. Since the library is the tool that everybody uses for pipelines, having a good pipeline for the library is only fair.
Design for testability
Keep steps in vars thin, and put logic into classes in src, where it is easiest to test. Avoid hidden dependencies on global state. Pass in everything a class needs, including the pipeline reference. Code that is easy to test is usually easy to understand as well.
A publisher proofreads a school textbook far more carefully than a personal letter, because thousands of students will trust it. A shared library is the textbook of your build system.
17Deployment, Cloud, and Configuration as Code
A library is only useful if Jenkins knows about it. Modern teams register libraries with code, not clicks.
Registering libraries automatically
The Jenkins Configuration as Code plugin lets you describe Jenkins settings in a text file, including the list of global libraries with their names, default versions, and repositories. Storing that file in source control means a lost or rebuilt Jenkins gets its libraries back automatically, and every change to library settings is reviewed.
flowchart LR
LREPO["Library Repository"] --> TEST["Library Test Pipeline"]
TEST --> TAG["Release Tags"]
JCASC["Configuration as Code File"] --> JC["Jenkins Controller"]
TAG --> JC
APPS["Many Application Repositories"] --> JC
JC --> POD["Ephemeral Cloud Agents"]
POD --> CLOUD["Cloud Environments"]
JC --> REP["Reports and Metrics"]
Cloud agents
Modern setups often start a fresh worker container for every run and delete it afterwards. Library steps should therefore assume nothing about the worker: no pre-installed tools, no leftover files. If a step needs a tool, it should say so, and the pipeline should run in an image that includes it. Steps should be written to work the same way on any worker.
Environment mapping inside the library
A library is a great place to hold the mapping from friendly names to real settings. The caller says “staging,” and the library knows the account, region, and cluster for staging. Account numbers and server addresses then live in one reviewed place instead of hundreds of Jenkinsfiles. When the staging cluster moves, one edit fixes everyone.
Multiple libraries for multiple audiences
Large organisations often keep more than one library: a small, tightly controlled trusted library for platform features, and one or more folder-level libraries owned by individual departments for their own conventions. This gives central control where it matters and local freedom elsewhere.
Use case: moving a cloud region
A company moves its staging environment from one cloud region to another. Because every pipeline calls the library’s “deploy” step with the word “staging,” the platform team edits one mapping, tests it, and releases a patch. On their next run, two hundred pipelines deploy to the new region, and no application team touched a file.
18Advantages, Disadvantages, and Trade-offs
Shared libraries are powerful, and like every powerful tool, they have costs.
Advantages
- Write once, fix once, improve for everyone.
- Short, readable Jenkinsfiles.
- Consistent security and compliance rules.
- Specialists own complex logic; application teams stay focused.
- Full history, review, and testing through normal source control.
- Faster onboarding for new teams and projects.
Disadvantages
- A bug can break many pipelines at once.
- Extra indirection: readers must open another repository to see what a step does.
- CPS quirks make Groovy code harder to write.
- The library team can become a bottleneck.
- Global trusted libraries widen the security surface.
- Debugging spans two repositories.
Central control versus team freedom
| Choice | Leans toward control | Leans toward freedom |
|---|---|---|
| Pipeline shape | Full template | Toolkit of small steps |
| Version policy | Everyone follows main | Everyone pins a tag |
| Library placement | One global library | Folder libraries per team |
| Change process | Central team only | Open contribution with review |
The abstraction cost
A step hides details. That is its purpose, and also its danger. When something goes wrong, a developer sees only a failure inside a step they did not write. If the step’s logs and documentation are poor, the developer is stranded. The library team must invest in clear messages, good docs, and quick support to make the abstraction pay off.
There is no universal answer. A young company with three teams may need only a few helper steps. A regulated enterprise with hundreds of teams may need full templates with strict versioning. Match the design to your size and your risks.
19Design Patterns and Anti-patterns
A pattern is a solution that many teams discovered independently. An anti-pattern is a trap that many teams fell into.
Patterns worth copying
Thin step, thick class
Keep the step in vars short. It gathers inputs and calls a class in src that does the real work, where it can be tested.
Settings map with defaults
Callers give named options. The library merges them with defaults and validates the result.
Hook points
Templates run caller-supplied blocks at set points, such as before build and after deploy, allowing local additions without forks.
Wrapper steps
Steps that surround a block with common setup and cleanup: timing, locking, notifications, workspace tidying.
Moving major tag
A label like “v2” moves forward with safe releases, so users get fixes without breaking changes.
Resource templates
Long scripts and configuration templates live as files in resources, easy for specialists to edit.
Anti-patterns to avoid
The God Library
One giant library that does everything for everyone, with hundreds of steps and no clear owner. It loads slowly, breaks unpredictably, and nobody dares to change it.
Better approach
Split by purpose, such as build, deploy, and notifications. Give each area a clear owner and a small, stable surface.
The Silent Breaking Change
Changing what a step does without a new major version, so pipelines that followed the old behaviour suddenly fail.
Better approach
Follow semantic versioning honestly. Deprecate first, warn loudly, and remove only in a new major release.
The Mystery Box
Steps with no documentation, unclear names, and cryptic errors. Users must read the source to learn how to use them.
Better approach
Write a help file for every step, give clear names, and make error messages say what to do next.
Secrets in the Library
Passwords or tokens written into library files because “only we can see it.” Many people can, and history is forever.
Better approach
Reference credentials by name and let Jenkins inject them at run time.
Hidden Global State
Storing per-run data in variables that live outside methods, so that runs or calls interfere with each other.
Better approach
Keep data local, pass objects explicitly, and make classes serializable.
20Best Practices and Common Mistakes
These lessons are usually learned the hard way. You can learn them the easy way.
Best practices
- Start small. Begin with a few well-loved steps, and grow from real needs.
- Name steps by what they do for the user, not how they work inside.
- Use settings maps with strong, safe defaults.
- Validate inputs early and fail with helpful messages.
- Test every change automatically before it can merge.
- Version honestly with semantic numbers and a changelog.
- Document each step with a help file and an example.
- Print the library version at the start of every run.
- Keep steps idempotent so retries are safe.
- Protect the repository with review, branch protection, and limited tag creation.
- Deprecate before you delete, with warnings and dates.
Common mistakes and their fixes
| Mistake | What happens | Fix |
|---|---|---|
| Forgetting the underscore after the annotation | The pipeline fails to compile with a strange error | Add the underscore, or follow the annotation with an import |
| Steps file name with dashes | The step cannot be found | Use camel case names without dashes or spaces |
| Class cannot call sh or echo | Method not found errors in classes | Pass the pipeline reference into the class and call through it |
| Non-serializable object held in a variable | Failure after a restart or pause | Make classes serializable or keep such objects inside one method |
| Everyone follows main with no tests | A bad merge breaks all pipelines | Add tests, canary releases, and moving major tags |
| Changing a step meaning without a major release | Surprise failures in unrelated teams | Follow semantic versioning strictly |
| Heavy computation in library Groovy | Controller slows down for everyone | Move heavy work to shell commands on agents |
| Expecting a change to appear immediately | Cached copy still in use | Check caching settings and the version being loaded |
Library code runs through the pause-and-resume system, so a method that seems to work in a plain Groovy script may fail inside Jenkins. Always test inside real Jenkins or with a pipeline simulator, not only as plain Groovy.
21A Worked Example: Designing a Company Library
Let us design a library for a fictional company, step by step, without writing any code.
The situation
A company called ShopNest has forty services in three languages: Java, Node, and Python. Every service has its own pipeline, and audits show the pipelines have drifted apart. Some skip security scans. Others deploy without approval. The platform team is asked to fix this with a shared library.
Step by step
Study the existing pipelines
Read a sample of Jenkinsfiles and list what they have in common: checkout, build, test, scan, package, publish, deploy, notify. The library must cover this shared shape.
Decide what varies
Only a few things truly differ: service name, language, deployment environment, and whether the service needs a database migration. These become the settings.
Choose the shape
Because audits demand consistency, the team chooses a guided template with hook points: the library owns the required stages, and teams may add extra steps at approved places.
Lay out the folders
In vars: one main pipeline step plus small helper steps such as notify and tagRelease. In src: classes for build tool logic per language. In resources: the security scan policy and message templates.
Set defaults and validation
Environment defaults to development. Production requires a change ticket. Unknown languages are rejected with a clear message.
Test and version
The library gets its own pipeline running unit tests and three sample applications, one per language. Releases use semantic version tags and a moving major tag.
Register and roll out
The library is registered through configuration as code. Five volunteer services adopt it first. After two weeks of good results, the platform team invites everybody else.
What the Jenkinsfile looks like afterwards
Each service now has a Jenkinsfile of about five lines. It requests the library at the moving major tag, calls the main pipeline step, and supplies the service name, language, and target environment. Everything else, including security scanning and approvals, comes from the library.
| Concern | Before | After |
|---|---|---|
| Lines per Jenkinsfile | Around 200 | Around 5 |
| Security scan | Present in some services | Present in every service |
| Change to scan policy | Forty edits | One edit and one release |
| Onboarding a new service | Copy an old file and hope | Write five lines |
The team did not start by designing every possible option. It started from real pipelines, chose a small set of settings, and grew from feedback. This keeps the library simple and the adoption smooth.
22Migrating from Copy-Pasted Pipelines
Most teams do not begin with a library. They arrive at one after living with the pain of duplication.
A safe migration path
Find the duplicates
Search Jenkinsfiles for repeated blocks. The blocks that appear most often are your first candidates.
Extract one step
Move a single repeated block into a library step, keeping behaviour identical. Prove it by running an existing pipeline with the step in place.
Adopt gradually
Switch one pipeline, then a few, then many. Do not force everyone in one week.
Unify differences
Where copies drifted apart, decide the right behaviour and expose real differences as settings. This is where the audits get satisfied.
Grow into a template
When most pipelines call the same steps in the same order, offer a single template step that does it all.
Retire the old copies
Delete leftover blocks from Jenkinsfiles, and update your starter templates so new projects begin with the library.
Winning over teams
People resist change when it feels like loss of control. Show that the library saves them work: shorter files, fewer breakages, and faster fixes. Offer a clear way to request options and a promise of quick replies. A library that helps teams is adopted; a library that blocks teams is bypassed.
Copies often hold hidden, useful behaviour that nobody wrote down, such as a special workaround for one service. Ask the owners before you delete anything, and keep a supported way to customise behaviour.
23Governance: Owning a Library Over Time
A library is a product. Its users are your colleagues, and they expect quality, support, and honest communication.
Ownership
Every library needs a named owning team. That team decides which requests are accepted, reviews contributions, publishes releases, and answers questions. A library without an owner slowly decays: nobody fixes bugs, nobody removes old steps, and nobody feels responsible when it breaks.
A contribution model
The best libraries welcome contributions from users. Publish clear guidelines: how to propose a change, what tests are needed, and how reviews work. Application teams know their needs best, and letting them contribute reduces the bottleneck. The owning team still reviews every change for quality, safety, and fit.
Communication
- Keep a changelog that says what changed in each version, in plain language.
- Announce breaking changes early, with migration notes and dates.
- Provide a chat channel or ticket queue for questions.
- Publish a support policy: which versions receive fixes and for how long.
The life of a step
Proposed
A team explains the need and why existing steps do not cover it.
Built
Code, tests, and documentation arrive together in one reviewed change.
Released
A new minor version is tagged and announced.
Deprecated
When better options exist, the step warns users and names its replacement.
Removed
Only in a new major release, after usage numbers show that nobody depends on it.
Regular reviews
Every quarter, look at usage numbers, failure rates, and support questions. Which steps confuse people? Which options are never used? Which versions are still followed by many pipelines? These reviews turn a pile of code into a well-kept product.
A public park needs gardeners, rules, and a suggestion box. Without care it fills with weeds and litter. A shared library is a park that hundreds of pipelines walk through every day.
24Real-World and Industry Examples
These scenarios show how organisations use shared libraries. They are illustrative, so you can map them to your own situation.
Scenario: the security rule that reached everyone in a day
A new vulnerability scanner becomes mandatory. In an organisation without a library, the security team would file hundreds of requests. With a shared library, the platform team adds the scanner to the standard build step, tests it on sample projects, and releases a patch. By the next morning, every pipeline that follows the moving major tag is scanning.
Scenario: open-source infrastructure teams
The Jenkins project itself has infrastructure repositories that publish shared library code used by many of its plugin build jobs, giving hundreds of plugin repositories a consistent way to build, test, and release. It is a public example of the same idea at a community scale.
Scenario: the regulated bank
A financial company must prove that every production change had approval and a scan. The library’s deploy step refuses to run for production without a valid ticket number and a completed scan record, and it writes both facts to an audit log. Auditors need to review one library, not hundreds of Jenkinsfiles.
Scenario: mergers and mixed toolchains
After a merger, two companies must combine their engineering practices. A shared library gives them a common front door: teams from both sides call the same steps, while the library handles the differences behind the scenes, such as different artifact stores or cloud accounts.
Scenario: fast onboarding for a growing startup
A startup adds a new service every few weeks. A starter repository contains a Jenkinsfile of five lines that calls the library. New engineers get a working build, test, and deploy flow on day one, without learning Jenkins internals.
What these examples share
In each case, the library separates what is common from what is unique. Common things, such as security, approvals, notifications, and deployment mechanics, are owned by specialists. Unique things, such as the service name and language, stay with application teams. The result is speed for teams and control for the organisation.
25How Other CI Tools Handle Reuse
Every automation system eventually needs a way to share pipeline logic. Comparing them shows what is essential.
| Tool | Its reuse mechanism | Notable difference |
|---|---|---|
| Jenkins | Shared Libraries | Full programming language; very flexible, but with CPS quirks |
| GitHub Actions | Reusable workflows and composite actions | Defined in workflow files; versioned through repository tags |
| GitLab CI/CD | Include files and shared templates | Configuration files that merge into the pipeline |
| CircleCI | Orbs | Packaged, versioned bundles published to a registry |
| Azure Pipelines | Templates | Template files referenced from other repositories |
What Jenkins does differently
Most other tools reuse through configuration files with placeholders. Jenkins reuses through real code. That means more power: loops, conditions, classes, and tests. It also means more responsibility, because code can be badly written in ways that configuration cannot. Jenkins teams must treat the library as software and give it software-grade care.
What is universal
The lessons apply everywhere: version reuse carefully, test shared pieces well, keep interfaces small and stable, protect the shared repository, document everything, and deprecate before deleting. If you learn these habits with Jenkins, you can carry them to any tool.
26Advanced Topics: Dynamic Loading, Multiple Libraries, and Overrides
Once the basics feel comfortable, these features help you handle larger organisations and unusual needs.
Loading a library dynamically
The annotation form is decided before the run begins, so it cannot depend on values discovered during the run. The library step can. A pipeline might read a parameter that names the wanted version, or check the branch name, and then load a matching version of the library. This is useful for testing a library change: a developer runs a real pipeline with a parameter set to the experimental branch, without touching the shared default.
Using several libraries together
A pipeline can ask for more than one library, each with its own version. A common arrangement is a stable company library plus a team library for special conventions. If two libraries define a step with the same name, the result can be confusing, so agree on naming rules. A short prefix that identifies the owning library helps to avoid collisions.
The implicit library
A library marked as loaded implicitly is available to every pipeline without an annotation. It is convenient for tiny, universal helpers such as a standard logging step. It is risky for anything large, because every pipeline pays the fetch and load cost, and any bug affects everybody, including pipelines that never wanted the library.
Overriding versions safely
Overrides enabled
Developers can point a pipeline at any branch, which speeds up experiments. It also means anyone who can edit a Jenkinsfile can run library code from any branch.
Overrides disabled
Pipelines can only use the configured default version. Safer for sensitive libraries, but experiments need a separate test library or a test Jenkins.
Protected branches only
Overrides are allowed, but every branch in the library repository requires code review, so all reachable code has been reviewed.
Libraries and multibranch pipelines
In a multibranch job, each branch of the application has its own Jenkinsfile, so each branch can request a different library version. This lets a release branch keep an older, proven library version while the main branch tries the newest one. It is a natural way to test upgrades on real code before rolling them out everywhere.
Retrieval sources
The library repository can be hosted on any system that a Jenkins source-control plugin supports. Some organisations keep it on the same hosting platform as their applications; others use a separate, more tightly controlled server. Choose based on who should be allowed to change it.
A hospital keeps its approved treatment guides in a central binder. A doctor can try an experimental protocol only in a study with special approval. Version overrides work the same way: possible, but wrapped in review, so that experiments do not reach ordinary patients by accident.
27Writing Groovy for Pipelines: Habits That Save Pain
Library code is Groovy, but Groovy inside Jenkins has its own manners.
Keep it simple
Groovy allows very clever, compact code. In a pipeline library, clever code is a liability. Because of the pause-and-resume machinery, some advanced language features behave unexpectedly. Prefer plain loops, plain conditions, and small methods with clear names.
Watch out for closures in odd places
A closure is a small block of code that can be passed around. Some functional helpers, such as sorting with a custom rule or transforming lists with inline blocks, may not work well inside pipeline code unless they are wrapped in a method marked NonCPS. When in doubt, use simple loops.
Be careful with string building
Groovy strings can insert variables inside text. This is handy but hazardous when the result becomes a shell command, because a value that contains special characters can change what the shell does. Pass values to commands as separate arguments or environment variables, and validate them before use. This mirrors the injection warning from the security chapter.
Prefer standard steps over clever code
Jenkins offers many built-in steps for reading and writing files, parsing structured data, and handling credentials. These steps are designed for pipelines and behave properly with pause and resume. Use them before reaching for raw Groovy or Java classes.
| Habit | Why it helps |
|---|---|
| Small methods | Easier to test and to understand |
| Explicit types for settings | Errors appear early and messages are clearer |
| No hidden global state | Runs do not disturb each other |
| Built-in steps first | Safe with pause and resume |
| Short NonCPS methods only | Avoids strange failures |
| Meaningful error messages | Users can fix problems without asking |
Read your library code as a newcomer would. If a colleague cannot understand a step in five minutes, simplify it or add a comment. Boring code is reliable code.
28A Release Checklist for Library Maintainers
Checklists prevent forgetful mistakes. Here is one you can adapt.
Before merging a change
- Every new or changed step has a help file and an example.
- Unit tests and step simulations pass.
- Sample applications for each supported language build successfully with the change.
- Error messages were read aloud and make sense to a newcomer.
- No secrets, personal data, or internal addresses are written into files.
- A second person reviewed the change, including its effect on existing users.
Before tagging a release
- The size of the change is classified honestly as patch, minor, or major.
- The changelog lists what changed, in plain language, and names any deprecations.
- Migration notes exist for anything that could break users.
- A pre-release tag was tried by volunteer teams.
After the release
- Failure rates and step durations are compared with the previous version.
- The moving major tag advances only after the release looks healthy.
- The announcement reaches all users through the agreed channel.
- Someone is on call for questions during the first day.
- Rollback steps are known and rehearsed, meaning the moving tag can return to the earlier release.
Never skip the checklist because a change looks tiny. Some of the widest outages in shared systems began as one-line edits that nobody thought needed testing.
Measuring success
A healthy library shows a clear pattern: adoption grows, the average Jenkinsfile shrinks, incident counts caused by pipeline drift fall, and questions to the library team are mostly about new features rather than confusing errors. Track these signs and share them with the organisation, so that the value of the library stays visible.
29Making the Business Case: Cost and Return
Building a library takes effort. Leaders will ask what the organisation receives in return.
The costs
A library needs engineers who design steps, write tests, answer questions, and publish releases. It needs hosting for the repository, monitoring for its health, and time for documentation. Beginning teams often underestimate the support work, which grows as more pipelines adopt the library.
The returns
Faster changes
A policy change that once took weeks of coordination across dozens of teams becomes a single release.
Fewer incidents
Standard, tested pipelines reduce accidents caused by forgotten scans, skipped approvals, and inconsistent deployments.
Easier compliance
Auditors review one library and its history instead of hundreds of separate files.
Faster onboarding
New engineers and new services start with a working flow on the first day.
Simple arithmetic
Suppose fifty teams each spend two hours per quarter fixing or updating their own pipeline copies. That is one hundred hours every quarter, before counting the incidents that duplication causes. A library team that spends a fraction of that effort on one shared implementation pays for itself quickly, and the saving grows with every additional team.
Start where the pain is
Do not pitch the library as a grand platform. Pick the most painful duplicated task, such as security scanning or deployment approval, deliver a small step that solves it, and measure the result. Real numbers from a small success persuade better than a large plan.
30Troubleshooting Shared Libraries
When something feels wrong, walk through these questions in order.
Was the library found?
Check the library name and the settings. A misspelled name or a missing registration is the most common cause of an immediate failure.
Which version was loaded?
Read the log line that names the version. Compare it with the version you expected. Cached copies and default versions often explain surprises.
Is the step name valid?
File names in vars must be camel case identifiers. A dash or a space makes the step invisible.
Did the annotation compile?
Check for the underscore or import that must follow the annotation, and for typing mistakes in the version.
Is it a sandbox problem?
A message saying a method is not permitted means untrusted code called something unapproved. Decide whether the call is needed, and only then ask an administrator to approve it.
Is it a pause-and-resume problem?
Errors about objects that cannot be saved point to non-serializable data. Make the class serializable or narrow its lifetime.
Did the library change recently?
Look at the changelog and recent merges. If many pipelines fail at once, suspect the library first.
Keep a tiny diagnostic step in the library that prints the library version, the settings received, and a few basic facts about the worker. Ask users to include its output whenever they report a problem.
31Glossary of Terms
Every important word from this guide in one place, explained in plain language.
| Term | Plain meaning |
|---|---|
| Agent | A worker machine or container that executes pipeline steps. |
| Blast radius | How many pipelines or systems break when one shared piece breaks. |
| Canary release | A release given first to a few users to check for problems before everyone gets it. |
| Classpath | The list of places where a program looks for code it can use. |
| Controller | The central Jenkins server that schedules runs and stores configuration. |
| CPS | Continuation passing style, the technique that lets pipelines pause and resume. |
| Custom step | A named command defined in a library and called like a built-in step. |
| Global variable | A named object or step provided by a file in the vars folder. |
| Groovy | The programming language used to write Jenkins pipelines. |
| Groovy sandbox | A safety layer allowing only approved method calls. |
| Hook point | A place in a template where callers can add their own steps. |
| Idempotent | Safe to repeat, because a second run leaves the same result as the first. |
| Jenkinsfile | The text file holding a pipeline definition, kept beside the source code. |
| Library resource | A plain file shipped in the resources folder and loaded as text. |
| NonCPS | A marker for methods that run as normal Groovy, without pause and resume. |
| Semantic versioning | A numbering scheme of major, minor, and patch parts that signals the size of a change. |
| Serializable | Able to be saved to disk and restored, required for data held by pipelines. |
| Shared library | A repository of reusable pipeline code loaded by many pipelines. |
| Tag | A fixed label attached to one exact commit in a repository. |
| Template step | A library step that defines a whole pipeline and accepts only settings. |
| Trusted library | A global library whose code runs outside the sandbox. |
| Untrusted library | A folder-level or directly loaded library that runs inside the sandbox. |
| Version pinning | Requesting a fixed version, such as a tag, so that it never changes unexpectedly. |
32Frequently Asked Questions
Short answers to the questions people ask most.
33Summary and Key Takeaways
A shared library turns pipeline logic from scattered copies into a maintained product.
You began with fifty teams editing fifty files and finished with a company cookbook that every kitchen follows. Along the way you learned the three folders, how Jenkins fetches and loads a library, how versions, tests, and security keep the power under control, and how ownership and communication keep a library healthy for years.
Key Takeaways
- A shared library is reusable pipeline code in its own repository. It replaces copy and paste with one reviewed, versioned source.
- The three folders have clear jobs. Vars holds custom steps, src holds structured classes, and resources holds plain files.
- Design steps like products. Use named settings, safe defaults, early validation, and clear error messages.
- Version honestly and release gradually. Semantic versions, a moving major tag, and canary releases limit the blast radius.
- Trust is a security decision. Global libraries run outside the sandbox, so protect their repository with review and branch rules, and keep secrets out.
- Test everything. Unit tests, step simulation, and sample applications catch problems before hundreds of pipelines do.
- Own it like a product. A named team, a changelog, deprecation warnings, and quarterly reviews keep the library useful.