# Orbs authoring best practices

## General

This guide covers best practices for creating orbs.

### Give your orb a descriptive name

An orb "slug" is made up of a _namespace_ and _orb_ name separated by a forward slash. The namespace represents the individual, company, or organization that owns and maintains the orb, while the orb name itself should describe the product, service, or action provided by the individual orb.

 

Proper Orb Slug

Bad Orb Slug

`circleci/node`

`circleci/node-orb`

`company/orb`

`company/cci-plugin`

### Categorize your orb

Categorizing your orb allows it to be searchable on the [Orb Registry](https://circleci.com/developer/orbs) by category. To see how you can categorize your orb using the CircleCI CLI, refer to the relevant section in the [Orb Authoring Process](https://circleci.com/docs/orbs/author/orb-author/#categorizing-your-orb) guide.

### Ensure all orb components include descriptions

Commands, Jobs, Executors, Examples, and Parameters can all accept descriptions. Ensure each and every component of your orb has a helpful description and provides any additional documentation that may be needed.

`````````
description: "Utilize this command to echo Hello to a step in the UI."
steps:
  - run:
      name: "Running Echo command"
      command: echo "Hello"
`````````

Create detailed descriptions that fully explain the benefit and usage of the orb element. Descriptions are an excellent place for more specific documentation related to each component.

### Ensure your orb-publishing context is restricted

If using the Orb Developer Kit, your CircleCI Personal Access Token is saved to a context in your Organization. Ensure you restrict this context so that jobs accessing it will only run when triggered or approved by you or other approved users. For more information, see the [Using Contexts](https://circleci.com/docs/guides/security/contexts/#restrict-a-context) guide.

## Structure

### @orb.yml

The `@orb.yml` file acts as the "root" of our project and contains much of the meta-data for our orb, which will appear on the Orb Registry as well as the CLI.

#### All orbs should include a description

When orbs are published to the Orb Registry they are searchable by their name and description. Besides giving your users a better idea of the purpose and functionality of your orb, good descriptions are important for search optimization.

#### Include display links

Orbs utilize a special config key [`display`](https://circleci.com/docs/orbs/author/create-test-and-publish-a-registry-orb/#orbyml) that can hold a `source_url` for linking to your Git repository, which in turn holds the orb source code and `home_url` to link to the product or service home page if applicable.

`````````
display:
  home_url: "https://www.website.com/docs"
  source_url: "https://www.github.com/EXAMPLE_ORG/EXAMPLE_PROJECT"
`````````

### Commands

#### Most orbs will contain at least one command

Most orbs will contain at least a single command. Commands are used to execute shell commands and special CircleCI steps automatically on the user’s behalf. In less common situations, for instance, if a tool _requires_ the use of a particular Docker container, an orb may not contain commands and only provide jobs.

#### Use the minimal number of steps required.

When writing a [Reusable Command](https://circleci.com/docs/reference/reusing-config/#authoring-reusable-commands) for your orb, you may input any number of [steps](https://circleci.com/docs/reference/configuration-reference/#steps). Each step should be properly named as it will appear in the user’s UI. To limit the amount of "noise" in the UI, attempt to use as few steps as possible.

**Deploy Command GOOD:**

`````````
description: "A demo of a command to install a CLI, authenticate, and deploy an app"
parameters:
  api-token:
    type: env_var_name
    default: MY_SECRET_TOKEN
steps:
  - run:
      name: "Deploying application"
      command: |
        pip install example
        example login $<<parameters.api-token>>
        example deploy my-app
`````````

**Deploy Command BAD:**

`````````
description: "A bad example of a deploy command. Steps should be named, and combined when possible."
parameters:
  api-token:
    type: env_var_name
    default: MY_SECRET_TOKEN
steps:
  - run: pip install example
  - run: example login $<<parameters.api-token>>
  - run: example deploy my-app
`````````

#### Check for root

Before adding `sudo` to your commands, check to see if the user is already the root user. This can be done dynamically with environment variables.

`````````
if [[ $EUID == 0 ]]; then export SUDO=""; else # Check if we are root
  export SUDO="sudo";
fi

$SUDO do_command
`````````

### Jobs

#### Consider "pass-through" parameters

Inside your job, if you are utilizing any commands or executors, you must include a copy of each parameter from each of those components into your job. You can then "pass-through" the parameters given to the job, to each referenced component.

For example, here is a partial snippet of the [Node orb’s `test` job](https://circleci.com/developer/orbs/orb/circleci/node#jobs-test):

**Test Job:**

`````````
description: |
  Simple drop-in job to test your NodeJS application automatically.
parameters:
  version:
    default: 13.11.0
    description: >
      A full version tag must be specified. Example: "13.11.0" For a full list
      of releases, see the following: https://nodejs.org/en/download/releases
    type: string
executor:
  name: default
  tag: << parameters.version >>
`````````

**Default Executor:**

`````````
description: >
  Select the version of NodeJS to use. Uses CircleCI's highly cached convenience
  images built for CI.

  Any available tag from this list can be used:
  https://circleci.com/developer/images/image/cimg/node
docker:
  - image: 'cimg/node:<<parameters.tag>>'
parameters:
  tag:
    default: '13.11'
    description: >
      Pick a specific cimg/node image version tag:
      https://circleci.com/developer/images/image/cimg/node
    type: string
`````````

As you can see, this job utilizes an executor named `default` which accepts a `version` parameter. In order to enable the user of this _job_ to set the `version` parameter in the _executor_, we must create the parameter in our job, and pass the parameter to our other orb components.

#### A Docker image parameter might be preferable to an executor

Does your orb have multiple jobs which require a specific execution environment? If so, you may choose to implement a custom executor. Will your job run on most Linux platforms? Consider just using the `docker` executor directly in your job, and parameterize the image.

#### Consider _post_ and _pre_ steps, and step parameters

Jobs on CircleCI can have steps injected into them, either before or after the job, or somewhere in-between with the use of parameters. Jobs are often easier to set up for users than assembling commands into a custom job (where applicable). Injectable steps allow for more flexibility in jobs and may allow new functionalities in your orb.

See the following:

*   [Pre and Post Steps](https://circleci.com/docs/reference/configuration-reference/#pre-steps-and-post-steps)
    
*   [Step Parameter](https://circleci.com/docs/reference/reusing-config/#steps)
    

### Executors

#### Orbs do not always require an executor

In orb development, executors are often used to either provide or utilize a specific execution environment when we have multiple jobs which can only run in that environment. For example, if your orb relies on a specific Docker container and includes two jobs and no commands, it makes sense to abstract the execution environment into a single [Reusable Executor](https://circleci.com/docs/reference/reusing-config/#authoring-reusable-executors) to be used for both jobs.

Executors are especially useful outside of orbs, as a way to create [matrix tests](https://circleci.com/blog/circleci-matrix-jobs/) for custom jobs.

### Examples

Orb [Usage Examples](https://circleci.com/docs/orbs/use/orb-concepts/#usage-examples) provide an excellent way for orb developers to share use-cases and best practices with the community. Usage examples act as the main source of documentation users will reference when utilizing an orb, so it is important to include clear and useful examples.

Be sure to name your usage examples so they reflect the use-case they demonstrate.

 

Good Usage Example Names

Bad Usage Example Names

`deploy-to-service`

`example`

`install-cli`

`demo`

#### All public orbs should contain at least one usage example.

Orbs intended for consumption by other organizations should include at least one usage example, with a description.

#### Use-case based examples

Each included usage example should be named for a specific use-case to instruct the user in how to accomplish a task. Example: `install_cli_and_deploy`, `scan_docker_container`, or `test_application_with_this-tool`

#### Show correct orb version

Each usage example must present a full example including showing the orb being imported. The version number displayed in the usage-example should match the currently published orb. If your orb is currently on version `0.1.0`, and you were to open a pull request to publish version `1.0.0`, your usage examples should be updated to reflect version `1.0.0` of the orb in use.

### Parameters

#### Secrets should _never_ be directly entered

Any information that could be considered "secret" such as API keys, auth tokens, and passwords, should never be entered directly as a parameter value. Instead, use the [`env_var_name` parameter](https://circleci.com/docs/reference/reusing-config/#environment-variable-name) type, which expects the string value of the name of the environment variable that contains the secret information. The `env_var_name` parameter type will fail validation if the parameter value is not a POSIX-compliant environment variable name. This validation rule is designed to assist in preventing users from accidentally entering the secret value directly as a string.

If your orb requires an "API key", you should create a parameter named `api_key` of type `env_var_name`. The developer using your orb would then pass the name of the environment variable containing the API key as the value of the `api_key` parameter.

`````````
parameters:
  api_key:
    type: env_var_name
    default: API_KEY # An environment variable named API_KEY contains the API key
steps:
  - run:
      environment:
        PARAM_API_KEY: << parameters.api_key >>
      command: |
        # Use the API key
`````````

In this example, the string value of `API_KEY` is saved in the environment variable `PARAM_API_KEY`. In order to get the value of the environment variable with the name `API_KEY`, you must evaluate with [Bash parameter expansion](https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html).

`````````
steps:
  - run:
      environment:
        PARAM_API_KEY: << parameters.api_key >>
      command: |
        API_KEY_VALUE=${!PARAM_API_KEY}
`````````

#### Accepting parameters as strings or environment variables

When creating a parameter that accepts a non-secret string value, it is often useful to also accept an environment variable that could be reused in multiple places. For example, setting your `AWS_REGION` is likely not a secret in the traditional sense, you may want to manually enter a region, or override a default value directly. It may also be easier to reference an environment variable that contains the region, for example, if you were using a [context](https://circleci.com/docs/guides/security/contexts/) to store your AWS metadata for multiple projects.

CircleCI ships with the [`envsubst`](https://circleci.com/docs/guides/security/env-vars/#environment-variable-substitution) CLI built in, which allows us to evaluate environment variables in strings.

`````````
parameters:
  region:
    type: string
    default: $REGION # This value is expected to be a string, but may evaluate to an environment variable
steps:
  - run:
      environment:
        REGION: 'us-west-2'
        PARAM_REGION: << parameters.region >>
      command: <<include(script/your_script.sh)>>
`````````

When building orbs, it is typical to [write your code in a separate file](https://circleci.com/docs/orbs/use/orb-concepts/#file-include-syntax) to avoid some of the pitfalls of YAML, as well as gaining access to syntax highlighting and other features in your IDE. In this example, we are including a script named `your_script.sh` which contains the following code:

`````````
REGION_VALUE=$(circleci env subst "$PARAM_REGION")
echo "Expect REGION_VALUE to be us-west-2: $REGION_VALUE"
`````````

Going through this substitution process allows us to accept a string value, or an environment variable, and use it in our script. Keep in mind, it is recommended to [use the `env_var_name` parameter type for secrets](#secrets-should-never-be-directly-entered) which should never be entered directly.

#### Parameterize the installation path

When installing any binary into a potentially unknown user-defined Docker image, it is hard to know what permissions will be available. Create an `install-path` parameter, ideally with a default value of `/usr/local/bin`, and install binaries to this location (if possible). This often avoids the issue of requiring "root" privileges in environments where that may not be possible.

## Deployment

#### Always follow strict semantic versioning

Semantic versioning is a critical update and release practice in which version numbers communicate either bug fixes and patches, new functionality, or breaking changes. Introducing a breaking change as a patch update, for example, can lead to users of that orb automatically receiving updates that block their CI process. Before updating your orbs, make sure you have read over and understood [semantic versioning](https://circleci.com/docs/orbs/use/orb-concepts/#semantic-versioning).

### Keep a changelog

Keeping a concise changelog allows users of an orb to quickly see what has changed in a particular version. While git does provide a log of changes, it can be difficult to discover the difference between two versions, especially when commits don’t necessarily align to a release. Changelogs should conform to the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) guidelines.

## Promotion

#### Share your orb with the community!

Have you published an orb to the Orb Registry? We’d love to hear about it. Come make a post on [CircleCI Discuss](https://discuss.circleci.com/c/ecosystem/orbs).