Hi, I’m working towards reducing the recompilation dependencies on a project with hundreds of modules.
Digging through this, I’ve stumbled into something that I didn’t expect:
When a function returns modules as values, that creates a runtime dependency to these modules.
Taking the example below:
```elixir
defmodule ImplSelector do
def resolve(impl_type) do
case impl_type do
:foo ->
ImplA
:bar ->
ImplB
:omg ->
:"Elixir.ImplC"
:wut ->
NonExistingMod
end
end
end
```
```shell
➜ mix xref graph --source lib/impl_selector.ex
lib/impl_selector.ex
├── lib/impl_a.ex
│ └── lib/impl_behaviour.ex (compile)
└── lib/impl_b.ex
└── lib/impl_behaviour.ex (compile)
```
It creates runtime deps from ImplSelector to Impls A and B. C is left because I’ve used :”Elixir.ImplC”, and as a non existing module doesn’t raise any warnings (just an atom after all right), I wonder if we could remove these dependencies too, or if it’s really desired.
We rely a lot on behaviours, and figuring these from instance configurations. I think that would have a good impact on the dependency graph overall, if it’s something that could go.
Thanks!
----
A bit off-topic, but wanted to share that when I started this saga this week, Elixir 1.11 only reduced ~10% the modules on recompilation, but now, as I’ve fixed a few important dependencies (still a lot to go), I can see already ~50% less modules recompiling in 1.11 as compared with same code in 1.10.4! 🌈