In a Jenkinsfile, what does the line `@Library('[email protected]') _` do, and why is there a bare underscore after it?
answer
- names a registered library, not a URL
- annotations need something to attach to
- after the @ is a revision
- branch, tag or commit — one is reproducible
basics
~20 sIt loads the Jenkins shared library registered as utils at version v1.4 — a branch, tag or commit in the library's repository — so the library's steps become callable in that Jenkinsfile. The underscore is a throwaway statement the annotation must attach to.
solid answer
~50 sThe `@Library` annotation, from the Pipeline: Shared Groovy Libraries plugin, tells Jenkins to fetch a library that an administrator has already registered under the name `utils` and make its code available to this build. The part after `@` selects the version: with a Git-backed library that can be a branch, a tag, or a full commit SHA, and it overrides the default version configured for the library — but only if that library was configured to allow overriding, otherwise the build fails at load time. Once loaded, every file in the library's `vars/` directory is available as a pipeline step, and classes under `src/` can be imported. The trailing `_` exists only because a Groovy annotation must be attached to something; it is a no-op statement standing in for the import you are not writing. In a Declarative pipeline the annotation goes at the very top of the file, above the `pipeline` block.
code
groovy · 12 lines@Library('[email protected]') _
pipeline {
agent any
stages {
stage('Deploy') {
steps {
deployApp app: 'billing', env: 'staging'
}
}
}
}go deeper
Be able to read the line out loud: it loads a named shared library at a chosen version, and the underscore is just something for the annotation to sit on. Know that the name is registered by an administrator, not a repository URL.
Explain when resolution happens — at build start, before any stage — and what becomes available afterwards: vars files as steps, src classes for import. Mention that a tag or SHA is reproducible while a branch is not.
Show that you treat the version reference as a blast-radius decision, and that you know an override can be forbidden by configuration. Be ready to say what a failed library load looks like and how you diagnose it.
Own the policy: which libraries exist, who may register them, whether Jenkinsfiles may pin versions, and how you keep the global var namespace from colliding as more teams publish libraries.
## What a shared library is A Jenkins shared library is an ordinary SCM repository — nearly always Git — containing Groovy code that pipelines load at build time. It is not a plugin and nothing about it is automatic. A Jenkins administrator first registers it: a **name** (`utils`), a **retrieval method** (the repo URL and credentials), and a **default version**. The name in the annotation is that registered name, not a URL and not a package. ## What the annotation actually triggers When a build starts, before any stage runs, Jenkins resolves each requested library, checks out the requested version into a workspace on the controller, compiles the Groovy, and puts it on the pipeline's classpath. Two things become reachable: - every file in the library's `vars/` directory becomes a global variable, and if it defines a `call` method it can be invoked as if it were a built-in step; - classes under `src/` can be imported or referenced by fully-qualified name. Because resolution happens up front, a typo in the library name or a version that does not exist fails the build immediately — you get no stages at all, which is a useful diagnostic: the failure is in loading, not in your pipeline logic. ## The part after the @ ```groovy @Library('[email protected]') _ // a tag @Library('utils@main') _ // a branch @Library('utils@0f3a1b2c9d') _ // a commit SHA @Library('utils') _ // the configured default version ``` For a Git-backed library, anything the SCM can resolve to a revision works. A tag or a SHA is immutable and therefore reproducible; a branch means the build gets whatever that branch pointed at the moment the build started, which is how a library change reaches many pipelines at once. A library configuration has a checkbox controlling whether Jenkinsfiles are permitted to name a version at all. If overriding the default version is not allowed, `@Library('[email protected]')` fails with an error saying the override is not permitted — a very common first encounter with shared libraries. ## Why the underscore Groovy annotations cannot float on their own; they must be attached to a declaration or statement. The idiomatic target is an import: ```groovy @Library('utils') import com.acme.Deployer ``` When you only want the library's `vars/` steps, there is no import to annotate, so the convention is to attach the annotation to `_`, a bare no-op statement that does nothing else. It is not special syntax — any statement would do; `_` is simply the community convention and what the Jenkins documentation uses. ## Loading more than one, and loading at runtime The annotation accepts a list: ```groovy @Library(['[email protected]', 'security-scans']) _ ``` There is also a `library` step that loads a library while the pipeline is already running: ```groovy def lib = library '[email protected]' ``` That form is useful when the version is computed at runtime, but it cannot be combined with `import` statements, and classes are reached through the returned object rather than by import. For the overwhelmingly common case — a fixed library at a fixed version — the annotation is the right tool. ## Placement in Declarative pipelines Declarative pipelines forbid arbitrary code outside the `pipeline` block, but the library annotation is explicitly allowed at the top of the file. It must come before `pipeline {`; put it lower and the file will not even parse as Declarative. ## Practical notes Library names live in a flat namespace per controller, and so do the global variables the libraries provide — two libraries that each define `vars/deploy.groovy` collide, so prefix var names when several teams publish libraries. And because loading a library means running someone else's Groovy inside your build, which library you load and who can merge to it is a security decision, not just a convenience.
- How would you load two shared libraries in the same Jenkinsfile, and pin only one of them?Pass a list to the annotation: `@Library(['[email protected]', 'security-scans']) _`. Each entry is resolved independently, so one can carry an explicit version while the other falls back to its configured default. Both libraries' vars steps become available in the same build, which is also why var names should be prefixed per library to avoid collisions.
- The build fails immediately with an error about the library version not being allowed. What is wrong?The library's configuration does not permit Jenkinsfiles to override its default version — the checkbox for allowing an override is off. Either an administrator enables it, or you drop the `@version` suffix and accept the configured default. It is a policy setting, not a problem with your branch or tag.
- When would you use the library step instead of the @Library annotation?When the version is not known until the pipeline is running — for example derived from a parameter or a file in the checkout. The `library` step loads at that point and returns an object through which classes are reached. The tradeoff: no import statements, so class references get verbose, and the load happens mid-build rather than up front.
saying these in an interview costs you the question
- Thinks the underscore is required Groovy import syntax
- Says @Library installs or downloads a Jenkins plugin
- Believes the version after @ must be a branch
- Assumes any Jenkinsfile may pick any library version
- Puts the annotation below the pipeline block in Declarative