Detect dependencies that are declared but never referenced#2114
Closed
Flo0807 wants to merge 1 commit into
Closed
Conversation
mix deps.unlock --check-unused only compares mix.lock against mix.exs, so a dependency that is declared but never called looks perfectly used to it. That blind spot let :number sit unused from 71757de (2024-08-26) until it started blocking decimal 3.x, and with it ecto_sql, about two years later. Nothing off the shelf covers this: ex_check's :unused_deps wraps the same lock check, and mix xref works on modules and files without a notion of which dependency they belong to. Add scripts/check_unused_deps.exs, which resolves each declared dependency to its modules via the application spec and matches them against the module references in Backpex's own BEAM files, using both the imports table and literal atoms so that behaviours and structs count as usage. Wire it into the lint alias and CI as "mix deps.unused". Running it surfaced two more instances of the same class of problem: * jason was declared but appears nowhere outside mix.exs. Removed. * ecto_sql was declared, but only Ecto.Query/Changeset/Repo are used, which live in :ecto. Narrowed to {:ecto, "~> 3.6"}; host applications bring their own SQL adapter. phoenix_ecto is allowlisted with a reason: it provides protocol implementations that are resolved at runtime and never referenced by name. The allowlist is printed on every run, since an exemption nobody sees is how an unused dependency hides in the first place. Also add scripts/*.exs to the formatter inputs so the check stays formatted.
Collaborator
Author
|
Closing for now — the two findings this check surfaced are being handled as dedicated PRs first (#2115 removes jason, #2116 switches ecto_sql to ecto). The check itself will come back as its own PR once those have landed, at which point it can be scoped purely to the tooling and will be green against develop. The branch |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The
numberincident (#2113) was not caused by a hard problem — it was caused by an invisible one.numberstopped being used on 2024-08-26 (71757de), stayed declared inmix.exs, and kept being resolved for about two years until itsdecimal ~> 1.5 or ~> 2.0pin blockeddecimal3.x andecto_sql3.14.CI never had a chance to notice. The step was even called "Deps Unused":
But
mix deps.unlock --check-unusedonly comparesmix.lockagainstmix.exs. A dependency that is declared but never called looks perfectly used to it.Nothing off the shelf closes this gap:
ex_check's:unused_depswraps the same lock check, andmix xrefoperates on modules and files with no notion of which dependency they belong to.What
scripts/check_unused_deps.exs, wired into thelintalias and CI asmix deps.unused:Dev/test-only tooling is skipped: it is never part of what a Backpex user resolves.
What it found immediately
Two more instances of the same class of problem:
jasonmix.exs:54. Never used inlib/, not in git history either.ecto_sqlEcto.Query/Ecto.Changeset/Ecto.Repoare used — those live in:ecto. The onlyuse Ecto.Migrationhits are inside@moduledocexamples.{:ecto, "~> 3.6"}phoenix_ectois allowlisted with a reason (protocol implementations, resolved at runtime, never referenced by name). The allowlist is printed on every run — an exemption nobody sees is how an unused dependency hides in the first place.Note on
ecto_sqlThis is the one user-facing change here: Backpex no longer pulls in
ecto_sql, onlyecto. Host applications bring their own SQL adapter (the demo declaresecto_sqlitself), so in practice every Phoenix + Ecto app already has it. Flagging it explicitly in case you would rather keepecto_sqldeclared and allowlist it instead — happy to switch.Verification
mix deps.unusedis green on this branch (10 dependencies checked, 1 allowlisted).numberitself can no longer serve as the fixture — it is now uninstallable against the lockeddecimal3.1.1, which is precisely the conflict Remove unused number dependency to unblock decimal 3.x #2113 removed.castorewas used instead.)mix lintpasses end to end (compile --warning-as-errors,deps.unlock --unused,deps.unused,format,credo, 100 doctests + 174 tests, 0 failures).mix credo --strict,mix format --check-formattedandmix docs --warnings-as-errorspass; the demo still compiles against the narrowed dependency.Scope
Backpex only. The demo has plenty of legitimately unreferenced runtime dependencies (
bandit,phoenix_live_reload, …), so the check would be noisy there without value — the published package is where a stale dependency actually hurts users.