The reason we’ve started to seriously consider migrating off umbrella app, is a particular limitation we’ve noticed when using mix xref graph
tool. It was a bit of a journey, so let me explain.
Recently several developers in the company reported that our application code compilation times have increased to the point they’ve become quite uncomfortable for day-to-day work.
We’ve started closely looking into what’s going on, and were able to identify and fix many low-hanging fruits using mix xref graph
tool, which is a static analysis tool that reveals module dependencies. Someone suggested a gist with a tutorial for using mix xref
on a real project. That gist was instrumental in helping me build a mental model around what kind of work needs to be done to reduce app re-compilation times.
After reaping the low-hanging fruit came a moment where we hit a limitation: mix xref graph
will not show cross-application compile dependencies. It’s best illustrated based on example.
One of our apps is called api
another one is called domain
. mix xref graph --label compile-connected
successfully shows dependencies between modules within a single app, but not between api
and domain
. While obviously there are many instances, where for example a controller from api
app depends on a module from domain
app.
Removing umbrella app helped us solve this, so running mix xref graph --label compile-connected
now reveals the “full picture” and helped us see more opportunities for re-compilation optimization.
However, after years of running an umbrella app we’ve accumulated more issues with it:
- the way we handled sharing code between apps - by extracting common code into yet another app - proved problematic,
- the need to have a dedicated files like
test_helper.ex
,factory.ex
etc. in each individual app, - a somewhat non-obvious connection between root project
mix.exs
respective file in each individual app, - calls to
Path.join("../../..")
across tests when in need to access something shared between the apps, - overall speed of test suite (suites for each individual app needs to run sequentially one after another),
-
mix test test/app/path/to/file_test.exs
were never actual real path to files, but rather “path pattern” to look for a test file within the individual app, - some packages don’t work well with umbrellas (example)
- etc.
So, small DX stuff like that. Not to mention we’ve developed a habit to attribute sporadic elixir-lsp
misbehavings to our app being umbrella.
In the end of the day I tend to think no one on the team really understood or valued the benefits of umbrella app structure well enough to make a case for keeping it. Our app is pretty straightforward, REST web server application, and most of us craved for less obstacles in a day-to-day work.