We're glad you want to contribute to Reliability Kit!
- Requirements
- Getting set up
- Creating a new package
- Installing dependencies
- Testing
- Committing
- Releasing
- Issue management
- AI Tools
In order to contribute and make changes to this repository, you'll need some software installed:
-
Node.js on a version defined in
package.json:engines.node -
npm on a version defined in
package.json:engines.npm(usually bundled with Node.js)
The following software will make your life easier when making changes to Reliability Kit:
- Volta can manage your Node.js and npm versions automatically
To set up this repo to make changes locally, make sure you have all of the required software, then:
- Clone this repo and
cdinto it - Run
npm installto install dependencies - Run
npm testto verify that everything's working
To create a new package in this repo, run the following command. This will bootstrap the package files and make sure it's added to the release configuration and is auto-versioned correctly.
The name of the package must be lowercase with words hyphen-delimited.
npm run create-package <NAME>
It's important that you run npm install before committing. This ensures that the new package is registered in the main package-lock.json.
You'll need to manually add the package to the list of packages in the README once it's ready to be used by other teams.
Important
Normally we want the first release of a new package to be a pre-release (e.g. 0.1.0). To do this, you'll need to hard-code the release version in a commit.
Note
Wherever the package version appears, you should leave it as 0.0.0 to allow Release Please to correctly bump and release the first version.
Because we use a monorepo managed with npm workspaces, installing dependencies is slightly different to other projects. You'll need to adjust the way you install dependencies as follows. If you're reviewing a pull request on Reliability Kit, then please also check that dependencies have been installed in the expected places.
The top level package.json file should never include any dependencies apart from devDependencies. If you install the dependencies that individual packages rely on here then they will not have access to them after publishing.
If you need a new development dependency that is used across the whole repository, then you can install it as normal by running the following from the repo base path:
npm install --save-dev <DEPENDENCY_NAME>
If a specific package relies on a new dependency, then you must not cd into the package and run an npm install. This will end up creating a package-lock.json file within that folder and cause all sorts of dependency issues. Instead, you must use the --workspace flag, setting it to the package you want to install a dependency in:
npm install --workspace=packages/<PACKAGE_NAME> <DEPENDENCY_NAME>
This ensures that we continue to maintain a single package-lock.json file in the root of the repo.
Some packages may also require their own specific development dependencies, for example @types packages or specific modules required for testing that package alone. This can be done with the --workspace flag too:
npm install --save-dev --workspace=packages/<PACKAGE_NAME> <DEPENDENCY_NAME>
If a package within Reliability Kit relies on another Reliability Kit package, you'll still need to codify this relationship in the package.json file for the dependent package. You can do this normally with an npm install, using the full @dotcom-reliability-kit/<NAME> package name. For example if your package relies on the internal errors package then you'd run this:
npm install --workspace=packages/<PACKAGE_NAME> @dotcom-reliability-kit/errors
Reliability Kit includes some tooling to ensure that code quality and consistency is high.
Reliability Kit aims to support all versions of Node.js which are in the "Active LTS" or "Maintenance" release phase. We run all linting and tests on all the versions of Node.js that we support and we document the supported versions in the package.json engines.node field for the project and for each individual package.
As well as the LTS versions of Node.js, we also try to immediately support Node.js versions in the "Current" release phase as long as they're an even major version number. We never support odd version numbered Node.js as these do not transition into LTS or Maintenance phases.
We aim to support new even Node.js versions as soon as they're released, but we need to wait until we can automate testing for that version. That relies on a new CircleCI node image being published.
Once that's done, you need to do the following:
-
Add the new Node.js version to all
package.jsonfiles in the repo under theengines.nodeproperty. You must add new versions with a double pipe and follow theMAJOR.xformat, e.g. append the existing engines with|| 28.x. -
Update the CircleCI config matrix builds to add the new Node.js version to the array of versions. For this you'll need the full version number, e.g.
28.0.0. Find all instances ofnode-version:and update the array. -
Commit your changes under a
feat: support Node.js XXXcommit (see committing guide) and open a pull request. We consider officially supporting a new version of Node.js to be a feature-level release.
When a new version of Node.js moves from the "Current" to "Active LTS" release phase, we set that version of Node.js to be the default version for the project. You can do this by:
-
Update the CircleCI config default Node.js version to the new Active LTS version of Node.js. Find an instance of
node-versionwith adefaultproperty and set that string to the new version. -
Update the Volta config in
package.jsonso that we use the latest LTS version of Node.js when developing locally. -
Commit your changes under a
chore: switch active LTS versionscommit (see committing guide) and open a pull request. We consider switching the default version of Node.js as a chore-level commit, because it has no impact on our actual Node.js support – we still test against all other versions.
When a Node.js version reaches end-of-life and is no longer maintained, we can consider dropping support for it in Reliability Kit. This should be a discussion – just because a version is end-of-life it doesn't mean we've fully migrated away from it. When dropping support for a Node.js version in Reliability Kit you should consider all of the users we support.
If we decide to drop support for a Node.js version, e.g. we want to start using features available only in later versions, then do the following:
-
Remove the Node.js version from the
engines.nodeproperty in allpackage.jsonfiles in the repo. Remove the version as well as the double pipe which separates it from the next version. -
Update the CircleCI config Node.js versions in each matrix to remove the old Node.js version. Find instances of
node-versionwith an array and remove the relevant items. -
Commit your changes under a
chore!: drop support for Node.js XXXcommit (see committing guide) and open a pull request. We consider removing a Node.js version to be a breaking chore because it requires changes in dependent apps – they are forced to migrate Node.js versions.
JavaScript files are expected to pass the linting rules defined by the project (Biome). We attempt to run the linters on every commit, but you can also check lint errors manually either by installing the VS Code Biome extension or by running the following locally:
npm run lint
The linters are also run on pull requests and linting errors will block merging, so it's useful to check before opening a PR.
We do not write TypeScript code, but we do write thorough JSDoc and publish type declarations, then test against it which gives us all the benefits of TypeScript (more info).
We do not compile the code in our packages, but we do check that all variables are set to the correct types. If there are any type errors then you should see these in your editor if you're using VS Code. Otherwise type checking can be manually run as part of linting:
npm run lint
As with Biome, we check types in pull requests and errors will block merging, so it's useful to check before opening a PR.
We run unit tests with Jest across most of our packages and aim for 100% coverage. Tests are written within each package (e.g. packages/example/test/example.spec.js). You can run the tests with the following:
npm run test
To run the tests for a single package, use:
npm run test -w packages/<name>
Tests are also run on pull requests and failing tests will block merging, so it's useful to check before opening a PR.
We intentionally fail the unit tests if coverage drops below 100%. This library is meant to help our applications be more reliable and so it's important that we cover as many edge cases as possible. If you have a valid reason for some code not to be covered, e.g. an empty function as a default parameter, then use code comments to disable coverage for that line or block:
/* istanbul ignore next */
function example() { console.log('this is not covered'); }This is better than dropping the required coverage because:
-
We have to make an active and conscious decision to drop a piece of code below 100% coverage
-
The fact that code isn't covered is documented in the code
-
It opens up discussion in the pull request when we see an uncovered piece of code – maybe the reviewer will have some useful ideas about how to make it possible to cover
We require commit messages to be written using conventional commits. This is how our automated release system knows what a given commit means.
<type>: <description>
[body]
The type can be any of feat, fix, docs or chore.
The prefix is used to calculate the semver release level:
| type | when to use | release level |
|---|---|---|
| feat | a feature has been added | minor |
| fix | a bug has been patched | patch |
| docs | a change to documentation | patch |
| chore | repo maintenance and support tasks | none |
Indicate a breaking change by placing an ! between the type name and the colon, e.g.
feat!: add a breaking feature
We use commitlint to enforce standard commit messages. If you're reviewing pull requests in this project then be sure to check that all commit messages conform anyway.
Because of the way our release process works (as well as smaller focused PRs being easier to understand and review) it's important to consider the scope of the changes in your PR. When calculating the next version bump for a package, Release Please will look at all the files in the pull request rather than just the files related to the individual commits. This can cause unwanted version bumps, e.g. a docs changes in the root of the repo can end up creating extra patch releases of packages that aren't actually impacted by it.
This makes it important to consider whether the changes you're making outside of the packages folder really belong in the same PR as changes to packages. If you're making broad changes to the repo this can result in unnecessary version bumps for packages if you use anything other than a chore commit for them.
If superflous releases are generated after merging a PR, it's possible to fix it.
In order to preserve conventional commits, pull requests must be merged or rebased rather than squashed. This is enforced in the repo.
Our releases are managed by Release Please.
When a commit with the feat or fix commit type prefix or a breaking change (!) reaches the main branch, a pull request will be opened with a name matching something like chore: release main. Merging this pull request will automatically create GitHub releases for each package as well as publishing the new package versions to npm.
If the PR is left alone, it will continue to be updated with new releases as more commits appear on the main branch.
Before approving and merging the release PR, make sure you review it. You need to check the package versions that it updates to make sure you’re only releasing the things you expect.
Sometimes it's necessary to hard-code a release version, for example if commits have been pushed to main which don't match our commit format. A more common reason to do this is when you want to publish a prerelease package to experiment before marking it as stable (e.g. publishing a v0.1.0 before v1.0.0).
You can override the version that Release Please will release by adding a footer to one of the commits in a pull request:
Release-As: 0.1.0
See the Release Please documentation for more information.
Sometimes the releases that Release Please decides to create may be incorrect, because of the way it bumps packages for all changed files in a PR. Considering pull request scope is important, but sometimes it's unavoidable that some additional changes sneak in. In this case it's possible to change the releases that an already-merged PR will create.
Update the PR description with a special override to correct the release type and re-run the Release Please command locally:
npx release-please release-pr --token="XXXXXX" --repo-url="Financial-Times/dotcom-reliability-kit"
In this command, change XXXXXX to a GitHub token with write access to the Reliability Kit repo. This can be a personal or bot token, but it's best to use the one defined in the Dotcom Reliability Kit vault as RELEASE_PLEASE_GITHUB_TOKEN.
There's an example of a PR which we did this for here.
We use GitHub issues and a project board to track work that needs to be done on Reliability Kit.
You can track the work that we're doing in the roadmap on our project board, and feel free to open new bug reports and feature requests on the repo.
If you're managing issues, we have a suite of labels. As well as the usual bug or enhancement labels, we also use the following to help us categorise and prioritise work:
| label | when to use |
|---|---|
package: <NAME> |
Used to help us filter bugs and issues by package. Add these labels to an issue so we can see if a particular package is causing us more problems over time |
requested |
Used to indicate that a team outside of the maintainers of Reliability Kit have requested this feature, which helps us prioritise it against other work |
Because this project is in a public repo and publishes packages to the public npm registry, we cannot use AI tools to generate code. It's fine to use AI to read and understand the codebase or ask general questions to help you form a plan. See the FT's Guardrails for AI Coding Agents.