skip to content

Dependency Resolution & Versioning

How a build or package manager takes your declared dependencies and computes one coherent set of versions, then installs it reproducibly. It is the source of a surprising share of real-world breakage, which is why interviewers ask about it.

part ofSoftware design & architectureoverview, primer and where to startread it →
on this pageshow

questions

page 2 of 2

A team hits a diamond conflict where two transitive dependencies require incompatible major versions of the same library, and decides to 'force' the whole build onto a single chosen version regardless of what any individual dependency originally declared. What are the risks of this approach, and when would forcing a single version NOT be the right call?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Forcing one version everywhere can silently break whichever library wasn't actually tested against that version, since the build tool doesn't check compatibility for you. It's risky when the two required versions are truly incompatible (a real major breaking change), not just a routine version mismatch.

open as a page

A CI pipeline switches from `npm install` to `npm ci` (or from `yarn install` to `yarn install --frozen-lockfile`) specifically for build reproducibility. What does this strict install mode actually do differently under the hood, and what class of bug does it catch that the regular install command lets through silently?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Strict install modes wipe out existing node_modules and reinstall purely from the lockfile, and they refuse to run at all if the lockfile doesn't exactly match the manifest -- catching cases where someone forgot to update the lockfile after changing dependencies, instead of silently patching the mismatch like a normal install would.

open as a page

SemVer defines a special rule for the 0.y.z range that doesn't apply once a package reaches 1.0.0. What is that rule, and why do many popular libraries intentionally stay below 1.0.0 for years despite being widely used in production?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Versions starting with 0 (like 0.4.2) are 'anything can change, anytime' under SemVer - even a MINOR bump can break things. Maintainers use this to keep freedom to redesign the API while people are still testing it out, even if lots of people already use it.

open as a page

You've inherited a service whose transitive dependency graph has grown to over a thousand packages, slowing installs and inflating the audit surface. What concrete techniques can you use to reduce or manage that graph's size, and what does each one trade away?

level: seniorimportance: should knowfreq 55%

basics

~20 s

You can swap heavy libraries for lighter ones, remove dependencies you don't actually use, let the package manager dedupe overlapping versions, or pin versions so the graph stops silently growing - but each of these costs either engineering time, some feature, or some flexibility.

open as a page

How does 'tree-shaking' in a JavaScript bundler like webpack or Rollup actually decide what code to eliminate from a production bundle, and what has to be true about a dependency for tree-shaking to work on it?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Tree-shaking is a bundler feature that deletes code you imported but never actually use, so your final downloaded JavaScript file is smaller — but it only works if the library is written in a way the bundler can statically analyze.

open as a page

A service's build succeeds and all unit tests pass, but production throws NoSuchMethodError inside a third-party library's code that your team never calls directly. Walk through how you'd diagnose this as a transitive version conflict, and how pinning would fix it.

level: seniorimportance: should knowfreq 55%

basics

~20 s

The build tool likely picked the wrong version of a shared library that two dependencies both need. You'd inspect the dependency tree for duplicate versions of the same library, find which two consumers disagree, and pin the graph to a version that satisfies both.

open as a page

You're responsible for a widely-consumed internal library published to hundreds of services inside a large engineering org. Design a policy that lets SemVer's MAJOR/MINOR/PATCH numbers actually stay trustworthy over years of contributions from dozens of different teams, given that no single person reviews every change. What mechanisms would you put in place, and what's the failure mode of skipping each one?

level: principalimportance: should knowfreq 35%

basics

~20 s

You need more than good intentions: a clear written definition of what's 'public API,' automated checks that catch accidental breaking changes before merge, a required human sign-off for anything flagged as breaking, and a changelog that's actually generated from real diffs. Skip any of these and the version number slowly stops meaning what it claims.

open as a page

Heuristic mediation strategies like nearest-wins and highest-version-wins always produce some answer, even when it's wrong. What does it mean for a dependency resolver to use a SAT/constraint-solving approach instead, and what problem does that solve that pure heuristics can't?

level: principalimportance: should knowfreq 35%

basics

~20 s

A SAT-style resolver treats version picking as a constraint problem across the whole graph and searches for a combination that satisfies everyone's stated requirements, backtracking if needed - and it can honestly report 'no solution exists' instead of silently picking something broken.

open as a page

Some JavaScript teams commit their entire node_modules folder to git instead of relying on npm install at build time. What does this buy them, and why has it fallen out of favor as a default practice?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

Committing node_modules means the exact installed dependency files travel with the repo, so a fresh checkout runs immediately without needing to reinstall anything — but it makes the repo huge and hides security problems from normal tooling.

open as a page

In a large plugin-based JVM application — say, an IDE or an application server hosting many independently developed plugins — different plugins each need a different, incompatible version of the same library, and forcing one aligned version across the whole application isn't realistic. What techniques let two incompatible versions of the same library coexist at runtime in a single JVM process, and what do they cost?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

You can trick the JVM into treating the two copies as different libraries entirely — either by loading each plugin's classes with its own separate loader so they don't share names, or by physically renaming one copy's internal package names so they no longer look like the same library. Both add real complexity and memory/build overhead in exchange for avoiding a forced single-version pick.

open as a page

A team's lockfile was generated by a CI runner on Linux x64. After adding a package that ships native/platform-specific optional dependencies (like a bundler such as esbuild or an image library such as sharp), engineers on macOS ARM report install failures or a missing native binary, even though everyone is using the same committed lockfile. What's causing this, and how do modern lockfile formats address it?

level: principalimportance: nice to knowfreq 40%

basics

~20 s

Some packages install a different native binary sub-package per operating system/CPU combination, listed as optional dependencies; if the lockfile format only records what was resolved on the platform that generated it, other platforms' entries can be missing or the wrong ones can get reused during install.

open as a page

Your organization is debating whether to require SLSA build-provenance attestations, cryptographically verified via sigstore/cosign, for every third-party dependency before it's allowed into any CI pipeline. What does verifying provenance actually protect against that an SBOM alone doesn't, and what's a legitimate reason to not make it mandatory everywhere immediately?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Provenance is proof of exactly how and where a piece of software was built — which source commit, which build system, run by whom — cryptographically signed so it can't be faked. It catches attacks where the published artifact doesn't actually match its claimed source, which a simple inventory list can't detect. Requiring it everywhere at once can be impractical because most packages don't provide it yet.

open as a page

At the scale of an entire engineering organization with hundreds of services, each with its own deep transitive dependency graph, what architectural and policy choices most affect how manageable those graphs are over time, and what trade-off does each choice involve?

level: principalimportance: nice to knowfreq 35%

basics

~20 s

Decisions like whether every service resolves its own dependency versions independently or shares one company-wide set, how strict the rules are for adding a new dependency, and whether you track the whole graph centrally all change how bad the mess gets - and each choice trades flexibility for consistency, or speed for safety.

open as a page

Designing a dependency-versioning policy for an organization with hundreds of services, how would you balance staying current on security patches against the upgrade risk that wide version ranges introduce?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Don't pick one extreme for everything. Let low-risk dependencies float and update automatically through reviewed pull requests; pin or tightly restrict the few dependencies where a break would be very costly; and measure how long it takes services to actually adopt a security patch so you can catch the laggards.

open as a page

showing 31–44 of 44