Two of your service's dependencies demand incompatible versions of one shared library — how do you resolve it?
answer
- Check whether the constraint is even real
- Cheapest and most reversible first
- Caps are often stale and defensive
- Every rung moves ownership onto you
- Separate environments beat forcing an install
basics
~20 sWork from cheapest and most reversible outwards: check whether the capping requirement is stale and a newer release of that dependency widens it, loosen your own pin, then consider dropping the dependency, vendoring it, or splitting the two consumers into separate processes.
solid answer
~50 sTreat it as a ladder of options ordered by reversibility and by how much you end up owning. First establish which constraint is real: an upper cap in a third party's metadata is often defensive and stale, so check whether a newer release of the capping distribution widened it — upgrading it is the cheapest fix. If your own pin is the cap, loosen it and let your tests decide. If upstream has not moved, verify in a scratch environment whether the cap is actually true for your usage, then push a fix upstream and carry a local override in the meantime. Beyond that the options get expensive: drop the dependency if it is used for one function, vendor it and accept that you now own its bugs and security fixes, or separate the two consumers into different processes so they never share one environment. Forcing an inconsistent install and shipping it is not on the ladder.
code
console · 1 linepython -m pip checkgo deeper
Know that the install failure is a genuine contradiction, not a tooling glitch, and that the first move is to read which requirement caps the version and whether a newer release of that dependency lifts the cap.
Explain the concrete options and their mechanics: upgrading the capping distribution, loosening your own pin, dropping a thin dependency, and why two versions cannot simply coexist in one environment.
Show the judgement: verifying whether a cap is real before working around it, testing the combination under realistic load, pushing a fix upstream while carrying a temporary override, and refusing to ship an environment its own metadata says is inconsistent.
Own the strategy. Decide when the org accepts an ownership transfer like vendoring or a fork, when a conflict justifies splitting a deployable unit, how upper-bound and dependency-count policy prevents the situation recurring, and how temporary workarounds get an expiry and an owner.
## The situation A warehouse pick-list builder handles a peak of about **1,200 requests per minute**, and under that peak it hits a race on shared state inside a library it depends on transitively. The race is fixed in that library's newer release — but another of the service's dependencies declares an upper cap that excludes it, so the install resolves to the buggy version or fails outright. Staying put is not free: the defect is real and it bites at peak. This is the standard shape of the senior version of this problem, and the interesting part is not the resolver, it is the decision. ## Step 0: establish the facts before touching anything Read the actual metadata rather than the folklore. The conflict report names the capping requester and its version; check the requester's release history for a version that widened the range. Confirm which constraint is *yours* — a surprising share of conflicts are caused by an exact pin someone added to your own requirements two years ago for a reason nobody remembers. ## The ladder, cheapest first **1. Upgrade the capping dependency.** Upper caps are frequently defensive: a maintainer wrote `<2` before the shared library's 2.0 existed and widened it two releases later. If a newer release removes the cap, you are done, and it is fully reversible. **2. Loosen your own pin.** If the cap is yours, the fix is to widen or remove it and let your test suite decide whether the newer version is safe. Cheap, reversible, and it removes a constraint the resolver was never obliged to respect for a good reason. **3. Test whether the cap is true, then fix it upstream.** Build a scratch environment with the cap bypassed, install the newer shared library, and run your suite plus a load test at your peak. If the capping distribution genuinely works against the newer version, send the maintainer that evidence and a change widening the range, and carry a local override in your own environment definition until it lands. You have converted a permanent problem into a temporary one. **4. Drop the dependency.** Look at what the capping distribution is actually used for. If it is one helper function or a thin wrapper, deleting it and inlining the behaviour is often the cheapest permanent fix available, and it reduces the dependency count that produced the conflict in the first place. **5. Vendor it.** Copy the capping distribution's source into your own tree under your own namespace so it is no longer resolved at all. This works, and it is sometimes the right answer for small, stable, effectively unmaintained code. Be clear-eyed about the price: you now own its security fixes, its bug reports and its future compatibility, and your scanners no longer recognise it as a known dependency with known advisories. Vendoring is an ownership transfer, not a trick. **6. Split the consumers.** The single-version rule is per environment, so putting the capping consumer behind a process boundary — a separate worker, service or isolated tool environment — removes the conflict entirely. It costs a real interface, a deployment unit and the operational burden that comes with it. Justified when the two consumers are genuinely separate concerns; overkill when they are not. **What is not on the ladder:** installing with dependency checking bypassed and shipping the result. That converts a loud build-time failure into a quiet runtime one — an `ImportError` or an `AttributeError` raised at peak, in production, in a code path you did not exercise. If you do bypass checks to *experiment*, do it in a scratch environment and never in the artefact you deploy. ## How to choose Two axes decide it. **Reversibility:** an upgrade or a loosened pin can be undone in a commit; vendoring and process-splitting cannot, cheaply. **Ownership:** every rung past the third moves maintenance from someone else onto your team, permanently. Prefer the highest rung on the ladder you can reach, and when you have to descend, write down why, what would let you climb back, and who checks. “Revisit when the capping distribution's next release ships” is a real, checkable trigger; “temporarily vendored” with no trigger is how a fork becomes forever. And in the specific case above, the deadline argument cuts both ways: the race at peak is a production defect, so “we will wait for upstream” needs a mitigation attached — rate limiting the affected path, or a temporary override in the deployed environment — rather than being a decision to live with the bug indefinitely.
- How do you decide between vendoring a dependency and running a fork of it?By whether you intend to give it back. A fork keeps the project's identity, history and upstream remote, so you can rebase your change and eventually drop it — right when the fix is small and the maintainer is likely to merge. Vendoring copies the code into your namespace and cuts the tie, which is only sane for small, stable, effectively unmaintained code. Vendoring also removes the dependency from advisory scanning, so its security burden becomes yours to track by hand.
- The capping dependency is unmaintained and the cap is genuinely wrong. What now?Confirm the cap is wrong by running your suite against the newer shared library with the cap bypassed in a scratch environment. Then choose between carrying a local override with a pinned, tested combination and a note explaining it, or removing the dependency altogether — an unmaintained dependency that already blocks an upgrade will block the next one too, so the removal case is stronger than it looks.
- Why is forcing the install through with dependency checks bypassed such a bad answer here?It does not resolve the conflict, it hides it. The environment ends up in a state its own metadata says is invalid, and the failure moves from a build that stops to a runtime `ImportError` or `AttributeError` in whichever code path first touches the removed or changed API — plausibly under peak load, which is exactly when you least want to discover it.
saying these in an interview costs you the question
- Forces the install past dependency checks and ships it
- Reaches for vendoring before checking for a newer release
- Assumes every declared upper cap is accurate
- Ignores that vendoring transfers security ownership to the team
- Splits into separate services as the first move rather than the last
- Leaves a temporary override with no trigger to revisit it