Drift

Dependency updates, checked against your code

Your dependency updated. Did it break your code?

Drift checks the update against the code that uses it. You get the changed API, exact call sites, and a fix you can review.

83.9% precision against 16,168 negative controls16 package ecosystemsEvery number, and every one refused

Why this exists

A green update PR can still break your build.

Version rules describe what maintainers intended. Drift checks what they shipped, then looks for that change in your repository.

88.42 of every 100 updates, unaffected11.58 broke a client, per the same study

Not an update bot

Three tools, three different questions.

Dependabot · Renovate

“A newer version exists.”

Opens the PR. Says nothing about whether it breaks you.

OpenRewrite · Moderne

“I will rewrite your code for this migration.”

Only where somebody wrote a recipe. Needs a build to parse.

Drift

“Here is what changed, where it lands, and what I could not check.”

Any version pair, computed from the published artifacts.

OpenRewrite is the better tool once you have decided to migrate — it rewrites the code, and Drift does not. Drift answers the question before that one: which of these upgrades can I take, and what will the rest cost me?

For coding agents

Your agent recalls what a package changed. Drift makes it check.

Ask an agent to upgrade a repository and it answers from memory — about a version pair it never looked at. Drift gives it the computed diff instead, and a verdict it is told not to soften.

  • check_upgrades — every pending upgrade, by verdict
  • explain_upgrade — what changed, and the exact lines
  • check_installed — names imported that the installed version does not export
  • Runs locally over stdio. No Drift service or account; evidence comes directly from upstream registries.

Real output

A real repository. A real dependency change.

Recorded run · linked commit

supabase/supabase-js — The official Supabase client for JavaScript and TypeScript.

drift outdated — supabase/supabase-js
    54c225dac·real run took 8.9s, replayed at 1×

    What the recordings found

    Safe Here329
    Affects You7
    Review Required68
    Runtime Unknown0
    Evidence Missing8

    270 exact call sites, linked to files and lines. Missing evidence is shown as a gap, not softened into a pass.

    How Drift performs on public data

    100.0%

    recall on 267 hand-labelled Java API changes, with 99.0% precision.

    Read the method and every miss →

    Run it yourself

    Try Drift in your browser, on your ecosystem.

    Each demo is a small project pinned to an old version of a real dependency, using an API the newer version breaks. Opening it upgrades the manifest and leaves the code alone, so Drift analyses an ordinary uncommitted change — and has no idea it is a demo.

    How a Finding Gets Made

    1. Dependency movedmanifest + lockfile
    2. API changedrelease notes + API diff
    3. Your code uses itimports + exact call sites
    4. Verifybuild / test
    5. Fixcodemod → fix plan → agent

    Real example: w3lib 2.1.1 → 2.4.1 in Scrapy, from the recording above. Each stage below opens to the artefact it actually produced.

    1. 1
      Read What MovedManifests and lockfiles, not guesses.

      Drift parses the manifest, then checks the lockfile for the version actually installed — a range only says what's permitted. Workspace members are read as separate packages, since a bump in one is a fact about that one.

      package.jsongo.modGemfile.lockpyproject.tomlCargo.tomlpom.xmlDirectory.Packages.propsconanfile.txtvcpkg.jsonplatformio.ini
      pyproject.toml1 changed dependency
      @@ pyproject.toml — [project.dependencies] @@
      25 "tldextract",
      26- "w3lib>=2.1.1",
      26+ "w3lib>=2.4.1",
      27 "zope.interface>=5.1.0",
    2. 2
      Gather EvidenceThree independent sources, each with a link you can open.

      Every record carries a URL or a local locator — the difference between Drift and asking a model what it remembers about a library.

      Registry metadata

      The package's own index entry

      Deprecation notices, yanked releases, publish dates, the maintainer's own 'latest' tag, and known advisories from OSV for both versions.

      GET pypi.org/pypi/w3lib/json

      GET api.osv.dev/v1/query ← both versions

      Release notes & changelog

      What the maintainers said changed

      Every GitHub release between the two versions, the CHANGELOG, and any migration guide it points to — matched by explicit rules, not a model's recollection.

      GET api.github.com/repos/scrapy/w3lib/releases

      GET raw…/w3lib/master/CHANGELOG.rst

      Computed API surface

      What actually changed, whatever they said

      Both versions are fetched and their public surfaces compared symbol by symbol — nothing is installed, no build script runs. Drift's strongest signal, and the only one that catches a break nobody wrote down.

      .d.ts · Go AST · rustdoc JSON

      japicmp · ECMA-335 · C headers · stubs

      Sources are independent on purpose. A missing changelog entry is caught by the surface diff; a behavior change with no signature change is caught by the changelog. Agreement raises confidence, and disagreement is reported rather than resolved by picking a favorite.

    3. 3
      Decide What BreaksA finding without a citation is not a finding.

      Computed diffs already know which symbol changed and how, so Drift reads structure rather than prose. Changelog lines go through rules that recognize a removal or a rename. Every breaking change points back at the evidence that justified it, and anything it couldn't establish is recorded as a gap.

      drift / analyzew3lib · 6 breaking changes
      behaviour-changeconfidence: 60/100 — Fairly confidentcites: GitHub release notes

      canonicalize_url no longer applies lowercase to the userinfo URL component.

      Review call sites for assumptions that no longer hold. Make any dependency on lowercased userinfo explicit instead of silently relying on the previous canonicalization behavior.

      @@ w3lib v2.2.1 — release notes @@
      canonicalize_url() no longer applies lowercase
      to the userinfo URL component.
    4. 4
      Find It in Your CodeOnly the files that import it are searched.

      A file that never imports w3lib can't be broken by a w3lib change, however often the word url appears in it. Within files that do, Drift matches the symbols the evidence named — skipping comments and strings, so a word in a docstring is never mistaken for a call. Each site gets its own confidence, highest when the file provably bound that symbol from that import.

      import graph#include graphAST-aligned indexper-site confidence
      symbol: canonicalize_url4 results · 3 files

      scrapy/linkextractors/lxmlhtml.py

      57 return canonicalize_url(link.url, keep_fragments=True)

      high confidence · 1 of 4 sites

      This file directly binds canonicalize_url from an import of w3lib, so the name here is provably that function — not a local one that shares its spelling. These are the four places that call the API whose behavior changed; whether a particular caller depends on lowercased userinfo is what the review step must determine.

      scrapy/linkextractors/lxmlhtml.py

      399 link.url = canonicalize_url(link.url)

      scrapy/utils/request.py

      100 url = canonicalize_url(request.url, keep_fragments=keep_fragments)

      tests/test_linkextractors.py

      794 return canonicalize_url(url, *args, **kwargs)
    5. 5
      Know What To ReviewThe finding includes its remediation.

      Drift keeps the recommended next step attached to the finding, so the evidence, affected code, and remediation stay together.

      Structured context like this measurably helps automated repair — the strongest tested model in a peer-reviewed study fully repaired 27% of breaking builds when given the erroneous line and the API diff.

      remediationfrom Drift

      Behaviour changed: canonicalize_url no longer applies lowercase to the userinfo URL component. Review call sites for assumptions that no longer hold. Prefer making the assumption explicit over silently adapting to the new behaviour.

    Verification rules & confidence

    When a Source Can’t Be Reached

    It’s recorded as a gap and the package is reported as not verified — never as clean. Zero findings from a complete check and zero findings from a check that never ran are different facts, and you need to know which one you have.

    not verified

    Drift found nothing it could check this version against.

    What Drift Refuses to Report

    Six lines that match a changed symbol and get no comment — each rule exists because a real run got it wrong without it.

    scrapy/core/downloader/tls.pyno comment

    from twisted.internet.ssl import Certificate

    A symbol this file got from somewhere else. Certificate here is Twisted's, whatever cryptography did to a class of the same name. A name has one binding in a scope, and the import says whose it is.

    components/ui/button.tsxno comment

    const Comp = asChild ? Slot : "button";

    A mention, when the change is a change of signature. A changed argument list is a fact about calls. Storing the name, re-exporting it, or asking for its type passes no arguments and has nothing to update.

    scrapy/utils/conf.pyno comment

    """To define format set a colon at the end of the o…"""

    A word inside a comment, a docstring, or a string. An English sentence containing the word define is not a call to define. Comments are stripped and string contents blanked before any identifier is matched.

    w3lib/url.pyno comment

    + def add_or_replace_parameters(url, new_parameters)

    Anything additive. A new export, a widened parameter, a new optional field. It cannot break a caller, so it appears in the reasons to upgrade and never in the risks.

    openapi.yamlno comment

    + responses.200.content.schema.properties.region

    Changes that break the server, not you. In an API diff only the consumer-breaking direction is reported. A response gaining a field is not your problem, and a report you have to filter is a report nobody reads twice.

    zlib.hno comment

    #define ZLIB_VERSION "1.3.1"

    A version constant that moves every release. ZLIB_VERSION changes on every single tag. A guaranteed finding at the top of every upgrade teaches people to skim past the ones that matter.

    What the Confidence on a Finding Means

    Two separate questions underneath. Did this happen upstream? is answered by the evidence — a computed diff starts high, prose starts medium, and agreement between sources promotes it. Does it land here? is answered per line. Drift keeps them apart internally — a certain upstream diff is not a reason to call a repository affected on its own — but rolls them into one score on the report, so the question “how sure is Drift, overall?” has a plain answer without hiding the breakdown that produced it.

    High

    The file bound that exact symbol from an import of this dependency. There is an edge from the declaration to this line.

    Medium

    The file imports the dependency and the symbol appears in it. Also the ceiling for C and C++, where #include binds no names and only a compiler could say more.

    Low

    The symbol appears but no import link could be established — a dynamic import, a re-export barrel, or a package whose module name differs from its own.

    AI-pass findings are capped at medium by construction, so they can never alone clear the bar to open a pull request. They assist recall — they don’t get a vote.

    And when Drift isn’t sure a change lands in your code, it says so in the verdict itself — “may affect your code”, not “affects your code”, for anything short of a directly imported match. Being wrong with the same confidence as being right is the failure mode this whole model exists to avoid.

    Ecosystem coverage

    16

    ecosystems — evidence · static analysis · verify · fix

    npmpypigocargomavenrubygemsnugetpackagisthexpubswiftcocoapodsopamconanvcpkgarduino
    Full support matrix →

    On every dependency change

    AnalyzeAskPR

    Approval by defaultNever mergesGuardrails fail closedUnverified ≠ safe
    See the full run
    Analyze Dependency Changes01 / 06

    main.github/workflows/drift.yml·push · pyproject.toml

    1. Set Up Job

      2s

      Triggers only on manifest and lockfile changes, not every push.

      1on: push

      2paths: **/package.json, **/go.mod, **/vcpkg.json, …

      3permissions: contents: write · issues: write · pull-requests: write · checks: write

    2. Run trydrift/drift@v0

      —
    3. Post the Check Run

      —
    4. Ask Before Acting

      —
    5. Verify the Approval

      —
    6. Open the Pull Request

      —

    What should Drift build next?

    Public roadmap

    Drift is early. Tell us what is missing, vote on ideas, or follow what has shipped.

    Try it on your next update

    Stop reading every changelog just to find the one change that matters.