Hey there, is it normal that whenever I modify a controller, it recompiles my router, all my controllers and my views?
I tried already ran
mix xref but I don’t really know how to investigate further.
Here is my
mix xref output for one of my controllers: https://gist.github.com/cblavier/217681e36c546fe7d44d2f0d245860b5
Hmm, one big thing of note is that it looks like the Views/Templates use Router helpers, but directly instead of namespaced. Do you use things like
some_path(...) instead of
Routes.some_path(...)? That would create a hard dependency and force recompilation. Phoenix like 1.3 or so changed from direct accessing to namespaced accessing for that reason, though it does mean you lose compile-time checking on them.
Thank you, it helps! I was able to decrease the count of recompiled files from 208 to 72.
Now, I can decrease it to about 10 files if I disable all custom module plugs I’m using in my controllers. Any idea why?
I tried playing with
config :phoenix, :plug_init_mode, :runtime but it doesn’t change anything? Does it also apply to controller plugs or only router plugs?
I suggest watching this video. It’ll give you an idea of what causes recompilation of modules.
:plug_init_mode is just a way to lessen recompilations for plug, but you might have other causes of compile time dependencies as well.
Already watched it! I think I tracked down all my “compile” dependencies.
But I can’t understand why, when using a same plug from a few controllers, when I touch one of the controllers, all the other controllers get recompiles (even if my plug code is empty!)
I finally solved my problem
Touching a view now recompiles only 1 file, and touching a controller recompiles 2 files (the controller and the endpoint) instead of 208 … (went from 30sec to 2sec)
What did I do?
- watched this video twice
- ran a gazillion times mix
xref graph --source lib/../my_controller.ex and
xref graph --sink lib/../router.ex
- also ran
fswatch _build -r | cat | grep .beam to actually see what files are being recompiled
RouteHelpers instead of importing them
- removed any hard reference to
Router in any of my plugs. I had to workaround this problem by using
Module.concat["MyApp.Router.Helpers"] in my navigation plugs
- had a weird dependency issue related to Coherence library when
coherence_routes were inlined in my app main scope instead of its own dedicated scope
- the last one still seems weird to me, but I also had to rewrite any
defdelegate calls from views to other views into a classic function call
Hope it will help someone, and if you have any comment about my post, feel free!
defdelegate does ensure the function actually exists on the other end, which makes it create a compile time dependency.
Still trying to understand why I didn’t noticed this mess earlier, I figured out that I had no such problem on this project a month ago (ie. touching a controller was only re-compiling a single file)
I finally figured out that I upgraded my phoenix version. And this specific commit is the one that made the issue apparent.
Is it intended? Should I open a ticket on phoenix?
That commit is actually removing the compile time dependencies of plugs if
:init_mode is configured to
:runtime (in dev). It’s there to prevent the issues of cascading recompiles. If
:compile it should act exactly as before the change.
I actually had no
plug_init_mode option at this time.
But I just tried to set it to both
runtime, it doesn’t change anything
(I even hard changed the
init_mode value directly in
Endpoint code to discard any configuration mistake)
That’s to be expected as it was only recently added to plug exactly because many people had the same problems as you had. When set to runtime it makes the
init/1 function of plugs be executed at runtime instead of compile time, so any compile time dependencies based on those init calls should be gone.