Automate dependency update proposals with Renovate
- ID
- ADR 9.2
- Status
- rejected
- Date
- 2026-09-25
- Deciders
- KBP architect, KBP core team
- Tags
- dependencies security tooling ci
Context and problem statement
KBP consists of a .NET backend and a Vue frontend. The backend has eight projects with NuGet lock
files and roughly fifty direct package references. The frontend ClientApp has about sixty npm
packages managed with pnpm. All packages are resolved through the EnBW Nexus proxy, and the
repository is hosted on Azure DevOps.
This record covers the shipped application only: the backend projects and ClientApp. The
supporting pnpm workspaces for the API stub, the endpoint tests, the load tests and the Playwright
end-to-end tests are out of scope and stay on manual updates.
Not everything in the repository is owned by the KBP team. The HEMS feature in the ClientApp
(src/features/hems) is maintained by another team. It contains a Kiota-generated API client whose
kiota-lock.json pins the generator version, and the @microsoft/kiota-* runtime packages in
package.json must match that version. Those packages are updated by the HEMS team together with
a regenerated client, not independently.
Today every package is updated manually, once every three months, in one controlled batch. The
cadence is deliberate: the automated safety net is thin. The Playwright suite is not run on pull
requests, the frontend has only a handful of unit tests, and the backend unit tests cover a subset
of the application. Each update therefore needs a human to judge the risk.
Nexus IQ
scans the dependency tree every night and reports vulnerable components, but remediation is manual
and in practice waits for the next quarterly batch.
Should the preparation of dependency updates be automated, and if so, how, without giving up the
controlled cadence that the thin test coverage requires?
Decision drivers
- The time between a vulnerability disclosure and its fix in production is bounded by the
quarterly cadence, which is too long for critical findings.
- The quarterly batch is one large pull request that mixes dozens of packages and release notes,
which makes review shallow and bisecting a regression expensive.
- Verification is weak, so every update needs a human decision. Anything that merges without
review is out of the question until UI tests run on pull requests.
- Azure DevOps hosts the repository today, and a move to GitHub is possible in the longer term.
The tool has to work on Azure DevOps now and survive that move without a second evaluation.
- The tool must resolve packages through the private Nexus npm and NuGet feeds.
- Reviewer capacity is limited. The tool must produce few, well grouped pull requests.
- Repository conventions apply to generated commits too: every commit references a work item and
every change reaches
develop and integration through pull requests.
- Packages owned by another team must not be proposed to the KBP team. The HEMS Kiota runtime
packages are the current case; the exclusion must be explicit and maintainable.
Considered options
- Keep the manual quarterly update batch (status quo).
- Run Renovate self-hosted on a schedule, grouped pull requests, no auto-merge.
- Run Renovate self-hosted with auto-merge for patch and devDependency updates.
- Use Dependabot, either on Azure DevOps today or after a move to GitHub.
Decision outcome
Rejected. The team keeps the manual update batch (option 1) and does not introduce Renovate.
The main risk is not the preparation work but the updates themselves: recent updates broke the
application in ways that the automated checks on a pull request do not catch. Grouped update pull
requests would still need the same manual regression testing and would arrive more often. The
updates that led to this decision:
- The Vite update from 8.0 to 8.1, with the move from Rollup to Rolldown, caused many visual
issues in the frontend.
- Backend updates run into version collisions with internal packages, because several of them
depend on shared NuGet packages. They have to be resolved by hand across packages, not one
update at a time.
- A Vuetify major version changed the CSS completely and broke many components. KBP has no
visual regression tests, so such breakage is only found by manual testing.
Instead, the team may run the manual update batch more often than once a quarter, so each batch
is smaller and easier to test. The vulnerability latency named in the decision drivers remains;
Nexus IQ keeps reporting vulnerable components every night.
The proposal below is kept unchanged so the reasoning stays readable.
Original proposal
Proposed: option 2, Renovate self-hosted on a schedule with grouped pull requests and no
auto-merge. It removes the preparation work and the vulnerability latency of the manual batch while
keeping every merge a human decision. It also keeps the quarterly rhythm by design: feature updates
are only opened in a scheduled window, and only vulnerability fixes may be opened at any time.
A later move of the repository to GitHub does not change this. Renovate runs on both platforms, so
renovate.json and the grouping move with the repository and only the way Renovate is invoked
changes, from an Azure DevOps pipeline to a GitHub Actions workflow or the hosted Mend app.
Dependabot would be the opposite bet: it has no supported home on Azure DevOps, so it would deliver
nothing until that migration actually completed.
The intended shape of the setup is:
- A scheduled Azure DevOps pipeline runs the Renovate CLI against this repository, similar to the
existing nightly Nexus IQ pipeline, using a pipeline identity with permission to create pull
requests. The configuration lives in the repository as
renovate.json.
- Updates are grouped by ecosystem and package family, for example the Vue and Vite toolchain, the
Vuetify UI library, the ESLint and Prettier tooling, and the Microsoft .NET runtime packages.
Each group is one pull request with the release notes attached.
- Packages owned by the HEMS team are excluded from Renovate: the
@microsoft/kiota-* runtime
packages are pinned to the Kiota generator version in the HEMS client’s kiota-lock.json and are
updated by that team together with the regenerated client. The exclusion is recorded in the
Renovate configuration with a comment naming the owning team, so it is reviewed when ownership
changes.
- Feature updates are opened only in a scheduled window at the start of each quarter, so the
batch arrives when the team plans to work on it. Vulnerability fixes are opened immediately and
separately.
- Nothing is auto-merged. Major updates additionally require an explicit opt-in before Renovate
opens the pull request.
- Every pull request references a standing dependency maintenance work item and follows the
two-pull-request flow into
develop and integration.
- Renovate only looks at the backend projects and
ClientApp. The API stub, the endpoint tests,
the load tests and the Playwright end-to-end tests are excluded in the configuration; they ship
nothing to production and stay on manual updates.
- The rollout starts with the backend NuGet packages for one quarterly cycle. Their updates are
covered by the backend unit tests and cannot break the UI, so the pipeline, the identity and the
grouping can be proven there.
ClientApp is added once the setup runs quietly.
Consequences
- Good, because the quarterly batch becomes a handful of grouped, reviewable pull requests instead
of one large hand-made bump.
- Good, because vulnerability fixes no longer wait for the batch and arrive as isolated pull
requests with a clear scope.
- Good, because lock file and transitive drift in the backend projects and
ClientApp becomes
visible instead of accumulating unnoticed between batches.
- Good, because each pull request runs the existing pull request pipeline (build, type check, lint,
unit tests), which is more verification than a manual bump usually gets per package.
- Good, because the choice survives a move to GitHub. Only the runner changes, not the
configuration, so the decision does not have to be reopened when the platform does.
- Bad, because a scheduled pipeline, a pipeline identity with pull request rights, and a Renovate
configuration have to be set up, understood, and maintained.
- Bad, because badly tuned grouping or scheduling produces noise. The configuration needs one or
two iterations before it is quiet.
- Bad, because the work item convention forces a standing maintenance work item per cycle for the
generated commits.
- Bad, because excluded packages keep a manual owner. The exclusion list has to be kept in step
with code ownership, and the HEMS team needs to know that Renovate deliberately skips their
packages.
- Neutral, because the human review effort per package does not shrink. What shrinks is the
preparation and the latency.
- Neutral, because the API stub, endpoint test, load test and Playwright workspaces keep their
manual update path. They are not shipped, so their drift costs developer time rather than
production risk. Extending Renovate to them stays possible and is a separate decision.
Confirmation
The decision is confirmed once the pilot on the backend NuGet packages has run for one quarterly
cycle and the following holds:
- The pilot pull requests were merged without a regression caused by the automation itself.
- Vulnerability fix pull requests reached
integration within one sprint of being opened.
- The team judges the pull request volume as reviewable and prefers it to the manual batch.
Auto-merge, even for patch versions, is revisited only when the Playwright end-to-end suite runs
on pull requests reliably. That is a separate decision.
Pros and cons of the options
Option 1: keep the manual quarterly batch
- Good, because nothing new to operate and the process is understood.
- Bad, because vulnerability fixes wait up to three months unless someone intervenes manually.
- Bad, because preparing the batch is repetitive work and the resulting pull request is hard to
review and to bisect.
- Bad, because transitive and lock file drift stays invisible until someone opens the batch.
Option 2: Renovate self-hosted, scheduled, grouped, no auto-merge
- Good, because it keeps the controlled cadence and the human gate while automating the
preparation and surfacing vulnerabilities immediately.
- Good, because Renovate supports Azure DevOps and GitHub, pnpm, and NuGet with lock files, and can
use the Nexus feeds through host rules.
- Good, because the configuration is versioned in the repository and reviewable like code.
- Bad, because it needs a pipeline, an identity, and configuration tuning.
- Bad, because it is a further tool in the toolchain that the team has to know.
Option 3: Renovate with auto-merge for patch and devDependency updates
- Good, because the review load would drop further for low-risk updates.
- Bad, because patch releases of UI libraries such as Vuetify or OpenLayers have broken layouts
before, and without UI tests on pull requests such regressions would reach
develop unseen.
- Bad, because it contradicts the reason the quarterly cadence exists. It is only acceptable once
the end-to-end suite runs on pull requests.
Option 4: Dependabot, on Azure DevOps today or after a move to GitHub
- Good, because on GitHub it is the built-in answer. It needs no pipeline and no identity of its
own there, and the team would not have to learn a tool outside the platform.
- Good, because the feature gap to Renovate has narrowed. Dependabot groups updates, understands
NuGet
packages.lock.json, and can hold back young releases, so the lock file support that used
to disqualify it for this backend is no longer an argument.
- Bad, because there is no supported way to run it on Azure DevOps. The community extension that
provided it,
tinglesoftware/dependabot-azure-devops and later mburumaxwell/paklo, was
archived on 1 September 2026 with the note that maintaining it “has become unsustainable as an
independently maintained project” and that no compatibility updates are planned.
- Bad, because Dependabot for Azure DevOps through GitHub Advanced Security is still a roadmap
item, is licensed per active committer, and covers security updates only. It would address the
vulnerability latency but not the version drift, which is the other half of this record.
- Bad, because choosing it means no automation at all until a migration to GitHub has completed,
while the problem this record is meant to solve stays exactly as it is.
- Bad, because it would not be operations-free on GitHub either. The hosted updater cannot reach
the EnBW Nexus feeds, which have no public host, so updates would need self-hosted Actions
runners labelled
dependabot that trust the internal certificate authority. That is roughly the
operating cost this record already books against Renovate.
- Bad, because its schedule is daily, weekly or monthly. The quarterly window that preserves the
controlled cadence would have to be approximated rather than configured.
- Bad, because
ClientApp is on a pnpm 10 lock file, for which Dependabot has an open parsing
defect at the time of writing. Renovate reads it today.
- Neutral, because rejecting it is not final. Renovate runs on GitHub as well, so if the migration
happens and Dependabot has become the lower-maintenance option by then, the switch is a small
decision taken with the pilot’s experience in hand.