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

  1. Keep the manual quarterly update batch (status quo).
  2. Run Renovate self-hosted on a schedule, grouped pull requests, no auto-merge.
  3. Run Renovate self-hosted with auto-merge for patch and devDependency updates.
  4. 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.

More information