Is there a way to list “superfluous” dependencies found in your ‘mix.exs’ file?

Is there a way to list “superfluous” dependencies found in your ‘mix.exs’ file?

Not really. Your best bet is to find all your dependencies’ root module namespaces (Ecto, Phoenix, Absinthe etc.) and look for them inside your project one by one.

Let’s say you have timex in your mix.exs, but you aren’t sure if it’s actually used in the project (likely after inheriting it from a teammate). The module namespace of that dependency – as it’s clearly visible on its docs page – is Timex. So, if you are using ripgrep (which you should! it’s an amazing tool), then do this at the root of your project’s directory:

rg 'Timex' -g '*.ex' -g '*.eex' -g '*.exs'

If this returns no results then timex is not used in the project (you could remove all -g flags and just be left with rg 'Timex' if you have doubts that the dependency might be used in files with other extensions). Rinse and repeat for all deps you have doubts about. You could also script this by parsing mix.exs's deps section by yourself (or using Elixir’s Mix code to parse it), but that might not be as easy as it sounds, and I haven’t attempted it – it might be an interesting project to work on, actually.

Finally, after you’ve eliminated all superfluous dependencies from your mix.exs file deps section, run this:

mix deps.clean --unlock --unused

This will also remove the dependencies’ files from the deps directory as well so it and the mix.exs file will now be in sync.

1 Like

Thank you, Dimitar. Scanning the source code is okay, I guess, but the problem is that there is no bell reminding you to do so. Ideally Elixir should send a warning saying that such and such dependencies are not referenced anywhere in the source code. Just like (I believe) it does when, for instance, you have a leftover alias that is not used anymore. And this is what is happening to me. I once added a dependency which I needed a the time but which, after code changes, is no longer required.

When you have just one project to check, it’s not too bad but I have many to consolidate.

Thanks again!

The problem with this is that you may want to add a library that is used from iex, but is never called from application code. I have benchee as a dependency in a few projects despite never having any committed code using it, and to get a warning every time would be quite annoying. I could see it being useful as a mix command, but it would be opt in and then basically the same as the grep command.

1 Like

That’s not possible since Elixir is a dynamic language. Imagine that, with the above example, you actually have code that accepts a string, converts it to atom and uses it as a module name and then calls a function in the module, like so:

def EvilModule
  def dynamic_call(mod_name, func_name, args)
      when is_binary(mod_ name) and is_binary(func_name) and is_list(args) do
    mod = String.to_atom(mod_name)
    func = String.to_atom(func_name)
    apply(mod, func, args)

…and then you call this from an iex session like so:

EvilModule.dynamic_call("Timex", "century", [])

This cannot be detected by searching through the code because you called it from your REPL session (similar to what @John-Goff gave as an example). You might also have tests that are generated by macros and they can accept parameters from the command line.

There are several scenarios where reliable detection is impossible.

What I showed you above is a best-effort but far from guaranteed success. In the end, use your best judgement and combine it with proper tooling to help you make a decision.

1 Like

Well, if you are really serious about this you could run this at the root of each of your Elixir projects:

mix run -e ":code.all_available() |>, 0)) |> Enum.filter(&List.starts_with?(&1, 'Elixir.')) |>, 7, 5000)) |> Enum.each(&IO.puts/1)"

This will print all available Elixir modules in the project (the Enum.filter part excludes the Erlang ones) and you can then pipe this list of modules to a script utilizing xargs and do shell trickery to your heart’s content. :slight_smile:

I can see how something like this, if properly done and finished (and tested) can be useful but IMO the better path is for the projects to have good tests. Then you can remove dependencies one by one, run tests and re-add the dependencies that turned out to be necessary.

1 Like