The problem
Renovate and Dependabot solve detection: a new version exists, here is a PR. They do not solve judgment. A changelog might describe a security fix, a no-op internal refactor, or a breaking API removal, and from the bot's perspective these look identical — a version bump. Nor do they know whether the deprecated function the changelog warns about is one your code actually calls.
The second problem is worse: dependency updates rarely arrive alone. A TypeScript
bump, a @types/react bump and a React bump are one upgrade wearing three PRs, and
merging them out of order is how a green CI run still leaves the build broken.
The approach
Upgrade Intelligence tracks components (npm packages, GitHub repos, GitLab projects) a team depends on, and runs each new release through a pipeline: fetch the changelog, send it to an LLM for structured analysis, score urgency from real signals rather than a version-diff guess, scan the connected codebase for actual usage of anything the changelog flags as breaking, and — when the component is part of a known ecosystem — bundle it with the other packages that have to move together.
Heavy work happens off the request path. API handlers enqueue a job to Redis-backed BullMQ and return immediately; seven workers process changelog analysis, periodic checks, dependency-graph builds, CI correlation, impact scanning, bundle execution and transitive dependency audits.
Ecosystem-aware bundling
A single-package view of "what needs upgrading" misses the fact that React,
@types/react and TypeScript are one upgrade. The bundle recommender recognises
15+ hardcoded ecosystem groups — React, Vue, Angular, Prisma, Next.js,
TypeScript-and-its-types, testing libraries, ESLint, Babel, Webpack, Vite,
GraphQL, Tailwind, Storybook, NestJS — and topologically orders the bundle with
Kahn's algorithm, so a type-level dependency upgrades before the package whose
types it supplies.
Bundle execution runs sequentially, not in parallel, by explicit design: worker concurrency for the bundle-execution queue is capped at 1 to avoid one job mutating a repository another job is still working in.
Checking the codebase, not just the changelog
An LLM reading a changelog can tell you an API was removed. It cannot tell you, on its own, whether your code calls that API. The impact scanner closes that gap: once a changelog analysis flags a breaking change, a separate pass fetches the connected repository's source and pattern/AST-matches the deprecated surface against real usage, so a team only gets urgency escalated for changes that would actually touch their code.
Confidence, not just a pass/fail gate
When an upgrade PR's CI run finishes, the platform doesn't just report red or green. It builds a 0–100 confidence score from four independent signals: test pass rate, code coverage, a similarity correlation between which tests failed and which breaking changes were predicted, and the historical success rate of upgrading that component before. The point is to catch the case a binary gate misses in both directions — tests that pass by accident, and tests that fail on something unrelated to the upgrade.
Design decisions worth noting
- Two Redis instances, not one. Queue coordination (BullMQ) and LLM output/changelog caching are deliberately split onto separate Redis instances with independent configuration, so cache pressure and queue throughput never compete with each other.
- Multi-provider LLM adapter with a circuit breaker. Analysis can run
against OpenAI, OpenRouter, Ollama or a local LM Studio endpoint, with a
configurable fallback order and a circuit breaker so one provider's outage
degrades the pipeline instead of stalling it. Local reasoning models proved
the harder case in practice — several return their answer in a separate
reasoning_contentfield with an emptymessage.content, which was silently tripping the circuit breaker until the adapter learned to normalise both response shapes, strip<think>blocks, and retry once with a larger token budget when a reasoning model runs out of budget mid-thought. - Version age comes from the registry, not a guess. Urgency scoring originally estimated how "old" a version was from the size of the version jump alone (major ≈ 180 days, minor ≈ 90, patch ≈ 30). It now fetches the component's real release date and only falls back to the heuristic when that lookup fails.
- A stub LLM provider exists for CI. The full integration suite runs in GitLab CI against a deterministic stub, so the pipeline is tested without external API keys ever touching CI configuration.
Case study
See this in production: Queue-based infrastructure for scoring, bundling and verifying dependency upgrades.
Availability
Private. Built and operated in-house; not distributed under an open licence.