Skip to content

Commit 4597f8e

Browse files
authored
Incorporate dependency management strategies (#583)
* Updated dependency section * Added more info about our config. * Incorporated template repo documentation * Grammatical fixes * Consolidated paragraphs
1 parent 3ec1797 commit 4597f8e

File tree

2 files changed

+205
-54
lines changed

2 files changed

+205
-54
lines changed

docs/contributing/dependencies/index.md

Lines changed: 200 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,43 @@ Bitwarden uses [Renovate](https://www.mend.io/renovate/) for automating dependen
99
will automatically create pull requests for dependencies on a weekly cadence. Security updates will
1010
generate pull requests immediately.
1111

12+
## Renovate configuration
13+
14+
Renovate is configured by a `.github/renovate.json` (or `.github/renovate.json5`) file in each
15+
repository. We follow an internal template for consistency. The template is available in the
16+
[template repository](https://github.com/bitwarden/template/blob/main/.github/renovate.json).
17+
18+
It is recommended that all repositories extend the
19+
[default](https://github.com/bitwarden/renovate-config/blob/main/default.json) configuration from
20+
our [shared configuration repository](https://github.com/bitwarden/renovate-config) that is included
21+
when a new repository is created from the template.
22+
23+
The default configuration:
24+
25+
- Combines minor and patch changes into one rollup pull request, per package manager.
26+
- Uses a dependency dashboard so we can see what pull requests are not yet created but still manage
27+
the workload.
28+
- Manages updates with semantic versioning and lock file updates. Rebases are disabled.
29+
- Allows an unlimited number of pull requests to be created.
30+
- Includes major updates (the latest) as individual pull requests. Monorepo updates are also
31+
grouped.
32+
- Schedules runs to happen on the weekend when more Actions workers are likely available for the
33+
organization, but also on a two-week basis to better align with the release schedule.
34+
- Certain build pipeline dependencies are pinned to specific versions.
35+
36+
See [Management Strategies](#management-strategies) below for more detail on why we have chosen
37+
these configuration options.
38+
39+
All package managers are recommended to be left enabled should a repository expand over time to
40+
include new ones, within reason for what might be in the scope of the repository. Update schedules
41+
and how many pull requests are up to the individual repository. Exceptions, other package managers,
42+
and dependency-specific configuration may be needed.
43+
44+
Consider [best practices](https://docs.renovatebot.com/dependency-pinning/#so-whats-best) with
45+
pinning dependencies, especially at the root. Development dependencies such as formatters and
46+
linters deserve communication and coordinated rollout across all teams so that code style is
47+
consistent per our standards and the editor configurations seen in the template repository itself.
48+
1249
## Ownership
1350

1451
Bitwarden's repositories fall under two categories: team-owned and shared.
@@ -20,8 +57,7 @@ is responsible for reviewing, approving, and merging dependency updates. Some re
2057
might be team-owned are that it's primarily developed by that team, or to balance out the number of
2158
dependencies teams have to manage.
2259

23-
Some examples of team-owned repositories are [`directory-connector`][dc], which is owned by the
24-
_Admin Console_ team, and [`key-connector`][kc], which is owned by the Auth team.
60+
Some examples of team-owned repositories are [`directory-connector`][dc] and [`key-connector`][kc].
2561

2662
### Shared repositories
2763

@@ -35,8 +71,9 @@ Examples of shared repositories are [`server`][server] and [`clients`][clients].
3571

3672
When adding a new dependency to a shared repository it **must** be owned by a team. It's up to the
3773
engineer adding the dependency to determine the appropriate team and get their approval for the
38-
addition before merging the PR. Please check [Renovate configuration](#renovate-configuration) for
39-
more information on how to assign ownership.
74+
addition before merging the PR. Please check
75+
[Renovate configuration](#assigning-reviewers-through-renovate) for more information on how to
76+
assign ownership.
4077

4178
## Example PR
4279

@@ -55,11 +92,163 @@ version is **13 days**, and **13%** of repositories have adopted this version. R
5592
change. For more details read
5693
[Renovate documentation about Merge Confidence](https://docs.renovatebot.com/merge-confidence/).
5794

58-
## Workflow
95+
## Management strategies
96+
97+
:::success Our Goal: A sustainable review cadence
98+
99+
The overarching purpose behind our use of these strategies is ensuring that our dependencies are as
100+
up-to-date as possible, while opening PRs for review at a frequency and level of complexity that is
101+
sustainable for all responsible teams.
59102

60103
Renovate is currently scheduled to automatically create pull requests every 2 weeks. The goal of our
61104
dependency management process is for the teams to review and merge the opened pull requests in the
62-
same 2-week cadence. To avoid a large backlog of PRs and out-of-date packages accumulating.
105+
same 2-week cadence -- to avoid a large backlog of PRs and out-of-date packages accumulating.
106+
107+
:::
108+
109+
### Dependency pinning strategies
110+
111+
#### Pinned dependencies
112+
113+
:::info Why do we pin dependencies?
114+
115+
For background on why we pin, see [Renovate's](https://docs.renovatebot.com/dependency-pinning/)
116+
documentation.
117+
118+
:::
119+
120+
For our client projects that are not intended to be consumed as libraries our strategy is to pin all
121+
dependencies. In addition, we use `package-lock.json` and `Cargo.lock` lock files, as recommended by
122+
Renovate.
123+
124+
#### Unpinned dependencies
125+
126+
For projects that are intended to be consumed as libraries (e.g. our SDKs) our strategy is not to
127+
pin dependencies so that consumers are not locked in to a particular version. As Renovate
128+
[recommends](https://docs.renovatebot.com/dependency-pinning/):
129+
130+
> It is usually a bad idea to pin all your dependencies because it will introduce an unnecessarily
131+
> narrow range (one release!) and cause most users of your package to bloat their `node_modules`
132+
> with duplicates.
133+
134+
Instead, the [recommended configuration](https://docs.renovatebot.com/presets-config/#configjs-lib)
135+
for libraries is to pin only `devDependencies` and leave the others as ranges.
136+
137+
### Dependency grouping strategies
138+
139+
We use grouping in Renovate to group related dependencies into a single PR.
140+
141+
There are two different levels at which our grouped dependencies are configured:
142+
143+
1. Our Renovate configuration uses the “monorepo presets” that Renovate
144+
[defines](https://docs.renovatebot.com/noise-reduction/#package-grouping). These are groups of
145+
dependencies that Renovate has grouped together through the `group:monorepos` configuration,
146+
which we have enabled. If we use any of these pre-grouped dependencies, Renovate will
147+
automatically group them together into a single PR.
148+
2. Grouping that we have put in place in our Renovate configuration. We have several different ways
149+
that we configure to do this, which we’ll detail below.
150+
151+
#### Why we group dependencies in our configuration
152+
153+
If a group of dependencies are closely related, we may decide that they should move as a unit
154+
through the review and QA process. We’ll do this if we want to:
155+
156+
- Group by **domain** to avoid errors resulting in inter-dependency version requirements
157+
- If a dependency relies on other related dependencies to have been updated along with it, we need
158+
to group these together to avoid errors when building. Sometimes we do this for all update
159+
types, and sometimes we only do this for major updates, depending on when we feel like
160+
inter-version dependencies exist.
161+
- Group by **domain** or by **package manager** to reduce the noise of too many PRs
162+
- This is a [trade-off](https://docs.renovatebot.com/noise-reduction/#noise-reduction), and we
163+
acknowledge that doing so will make identification of any issues stemming from the update
164+
harder.
165+
- Sometimes we only do this for minor or patch updates, as we acknowledge the trade-off with
166+
identifying breaking changes and want to have separate PRs for major updates.
167+
168+
:::info Grouping and team ownership
169+
170+
Grouping does not equal team ownership. In other words, a PR that has dependencies owned by multiple
171+
teams would have multiple reviewers on it. As a matter of practice, we try to assign all grouped
172+
dependencies to a single team, so that only a single team is responsible for the review.
173+
174+
:::
175+
176+
### Dependency update type strategies
177+
178+
:::info What are update types?
179+
180+
“Update type” refers to the classification of a version as major, minor, or patch.
181+
182+
:::
183+
184+
### Combining update types
185+
186+
For all of our dependencies, we default to:
187+
188+
- Combining minor and patch updates into a single PR for a given dependency, and
189+
- _Not_ combining major updates with any minor or patch updates for a given dependency
190+
191+
We do this because we want to reduce the noise of separate minor and patch updates, but we also
192+
don’t want to miss out on incremental minor/patch updates while we do the larger body of work for a
193+
major update. For example, if we’re updating Angular to version 20, we would want to receive minor
194+
and patch updates for version 19 while the PR for the version 20 update is open and being evaluated.
195+
196+
This combination of updates is for an individual dependency. This is distinct from grouping multiple
197+
dependencies into a single PR, which is covered in
198+
[Dependency grouping strategies](#dependency-grouping-strategies).
199+
200+
### Managing patch updates
201+
202+
In order to maintain a sustainable number of update PRs for our teams to review, we have elected to
203+
send most patch updates through Renovate's
204+
[Dependency Dashboard Approval](https://docs.renovatebot.com/configuration-options/#dependencydashboardapproval)
205+
flow. This means that Renovate will _not_ automatically generate a PR for these updates; rather, it
206+
will list them in a "Pending Approval" section of the Dependency Dashboard. If a team desires to
207+
update the dependency to pull in a bug fix that affects our code, they can trigger the pull request
208+
to be created from the dashboard.
209+
210+
### Dependency reviewer assignment strategies
211+
212+
In order for a dependency update PR to successfully move through the review process, it must be
213+
assigned a reviewer. We do this one of two ways:
214+
215+
- Through GitHub `CODEOWNERS`
216+
- Through reviewer assignment in Renovate
217+
218+
#### Assigning reviewers through `CODEOWNERS`
219+
220+
When a team owns the file updated by a dependency update (e.g. `package.json` and
221+
`package-lock.json`), they will be added as reviewers on the pull request without any configuration
222+
in Renovate.
223+
224+
This will be the case for:
225+
226+
- Repositories owned by a single team, and
227+
- GitHub Action workflows on shared repositories
228+
- In this case, the workflow files themselves are updated by Renovate, meaning that the team who
229+
owns the workflow will be added to the PR.
230+
231+
#### Assigning reviewers through Renovate
232+
233+
When the package and lock files are not owned by `CODEOWNERS` (as is the case for any of our shared
234+
repositories), we explicitly assign ownership of each dependency by specifying the responsible team
235+
in Renovate configuration in the repository.
236+
237+
Renovate uses a concept called
238+
[`PackageRules`](https://docs.renovatebot.com/configuration-options/#packagerules) that allows us to
239+
specify ownership of dependencies and ensure the appropriate team is added as reviewers. Below is an
240+
example assigning `@angular/core` to the Platform team.
241+
242+
```json
243+
{
244+
"matchPackageNames": ["@angular/core"],
245+
"description": "Platform owned dependencies",
246+
"commitMessagePrefix": "[deps] Platform:",
247+
"reviewers": ["team:team-platform-dev"]
248+
}
249+
```
250+
251+
## Dependency review workflow
63252

64253
:::info Major upgrades
65254

@@ -95,9 +284,13 @@ A typical dependency workflow involves the following steps:
95284
2. For **deprecations**, create high priority Jira tickets on the affected teams' backlogs with a
96285
due date at least one sprint before the next scheduled major release of the dependency.
97286
4. Verify CI status.
287+
288+
- This may also include re-running any failed workflows due to insufficient permissions when
289+
Renovate created the pull request.
290+
98291
5. If test coverage is lacking, check out locally and manually confirm a few key areas.
99292
6. Review the proposed code changes and approve the PR.
100-
7. Write a Jira ticket containing testing notes for QA.
293+
7. Update the ticket to include testing notes for QA.
101294
- Testing notes should include:
102295
- What areas of the codebase are affected by the dependency to help isolate future problems.
103296
- Recommendation for manual QA testing **only** if the developer identifies this as a high-risk
@@ -159,29 +352,6 @@ In those cases the team can comment on the PR with a reason for not yet upgradin
159352
or defer it until a later date. If a team closes a PR it is expected that its members monitor the
160353
dependency and revisiting the upgrade in the future.
161354

162-
## Renovate configuration
163-
164-
Renovate is configured by a `.github/renovate.json` (or `.github/renovate.json5`) file in each
165-
repository. We follow an internal template for consistency. The template is available at the
166-
[template repository](https://github.com/bitwarden/template/blob/main/.github/renovate.json).
167-
168-
Renovate uses a concept called
169-
[`PackageRules`](https://docs.renovatebot.com/configuration-options/#packagerules) that allows us to
170-
specify ownership of dependencies and ensure the appropriate team is added as reviewers. Below is an
171-
example assigning `@angular/core` to the Platform team.
172-
173-
```json
174-
{
175-
"matchPackageNames": ["@angular/core"],
176-
"description": "Platform owned dependencies",
177-
"commitMessagePrefix": "[deps] Platform:",
178-
"reviewers": ["team:team-platform-dev"]
179-
}
180-
```
181-
182-
For repositories maintained by a single team there is no need to use `packageRules` to assign
183-
ownership. Instead ensure appropriate code owners are set up.
184-
185355
[dc]: https://github.com/bitwarden/directory-connector
186356
[kc]: https://github.com/bitwarden/key-connector/
187357
[server]: https://github.com/bitwarden/server/

docs/contributing/template-repository.md

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -70,32 +70,13 @@ The editor configuration used above is accessed by many linters to drive results
7070

7171
## Dependency management
7272

73-
[Renovate](https://github.com/renovatebot/renovate)
73+
The template includes [Renovate](https://github.com/renovatebot/renovate)
7474
[configuration](https://github.com/bitwarden/template/blob/main/.github/renovate.json) for managing
7575
dependencies that is derived from a
76-
[shared configuration repository](https://github.com/bitwarden/renovate-config). It:
77-
78-
- Combines minor and patch changes into one rollup pull request, per package manager.
79-
- Uses a dependency dashboard so we can see what pull requests are not yet created but still manage
80-
the workload.
81-
- Manages updates with semantic versioning and lock file updates. Rebases are disabled.
82-
- Allows an unlimited number of pull requests to be created.
83-
- Includes major updates (the latest) as individual pull requests. Monorepo updates are also
84-
grouped.
85-
- Schedules runs to happen on the weekend when more Actions workers are likely available for the
86-
organization, but also on a two-week basis to better align with the release schedule.
87-
- Certain build pipeline dependencies are pinned to specific versions.
88-
89-
All package managers are recommended to be left enabled should a repository expand over time to
90-
include new ones, within reason for what might be in the scope of the repository. Update schedules
91-
and how many pull requests are up to the individual repository. Exceptions, other package manager,
92-
and dependency-specific configuration may be needed.
93-
94-
Consider [best practices](https://docs.renovatebot.com/dependency-pinning/#so-whats-best) with
95-
pinning dependencies (especially at the root), like those used for local linting above. Development
96-
dependencies such as formatters and linters deserve communication and coordinated rollout across all
97-
teams so that code style is consistent per our standards and the editor configurations seen in the
98-
template repository itself.
76+
[shared configuration repository](https://github.com/bitwarden/renovate-config).
77+
78+
See [Dependency Management](./dependencies/index.md) for more information on our Renovate
79+
configuration.
9980

10081
## Issue templates
10182

0 commit comments

Comments
 (0)