Usually, it is a trade-off between readability, dialyzer/xref precision, and generality.
The price of using many dynamic modules is that you can't use something like grep(1) anymore to find out who are callers. Likewise, tools usch as xref fails to see the call graph. This argues the notion should be used with care, although there are many good reasons for dynamically invoking a module as well.
What to do depends on the problem. Sometimes code can just be rewritten to something simpler. Other times, you can create a callback system and use that. Or you may be able to let a target process vary. And of course there are situations where the best approach is to just use dynamic modules in the code base.
A somewhat sound advice is to make sure there are few dynamically injected modules and that they are not subject to change as the system runs. Otherwise, the code may be hard to follow for a newcomer.