Using mix aliases to improve your workflow
When developers onboard new projects, we are uniquely positioned to help improve the onboarding experience for future developers. So, when I join a new project, I relish the opportunity to clarify and simplify setup instructions.
Recently, I joined a new project and took a look at the setup instructions:
## Dev setup
To set up the project, run:
mix deps.get
mix event_store.create
mix event_store.init
mix ecto.create -r R1
mix ecto.create -r R2
mix ecto.migrate -r R1
So, after getting the project’s dependencies, we create an event store and initialize it. Then, we create two ecto repos (R1 and R2) and migrate R1. R2 is a read-only repo, so it doesn’t need to be migrated.
That’s six steps. And they’re all things we could script. So, I asked myself,
could we consolidate those steps into a single mix setup
step?
I frequently use mix aliases to simplify my workflow, so I checked the
project’s mix.exs
file to see what was there. To my surprise, the project
didn’t have any aliases defined. And that was an opportunity for improvement.
Improving setup
I added the aliases
entry in project
and defined a setup
task:
def project do
[
...
aliases: aliases()
]
end
defp aliases do
[
setup: ["deps.get", "event_store.setup", "ecto.setup"],
"event_store.setup": ["event_store.create", "event_store.init"],
"ecto.setup": ["ecto.create -r R1", "ecto.create -r R2", "ecto.migrate -r R1"]
]
end
Taking inspiration from other Phoenix apps, I defined three aliases: setup
,
event_store.setup
, and ecto.setup
. We can run mix event_store.setup
and
mix ecto.setup
separately if we ever need to, but running mix setup
performs
all the desired actions.
Having done that, I could then change the setup to be a single command:
## Dev setup
To set up the project, run:
-
- mix deps.get
- mix event_store.create
- mix event_store.init
- mix ecto.create -r R1
- mix ecto.migrate -r R1
- mix ecto.create -r R2
+ mix setup
Beautiful! 🤩
Improving tests
As I read further down the README, I noticed we had a similar setup with tests:
## Test setup
MIX_ENV=test mix ecto.create -r R1
MIX_ENV=test mix ecto.create -r R2
MIX_ENV=test mix ecto.migrate -r R1
I could have created a test.setup
alias, but instead, I took inspiration from
Phoenix apps to create an improved mix test
alias.
The alias quietly creates and migrates test databases without having a separate test-setup step:
def aliases do
[
...
test: [
"ecto.create -r R1 --quiet",
"ecto.create -r R2 --quiet",
"ecto.migrate -r R1 --quiet",
"test"
]
]
end
So, when we run mix test
, we do the following:
- create the databases if they don’t exist,
- migrate R1’s data, and
- run the tests
Perfect! We no longer need a separate test-setup step. We can just run our tests
with mix test
, and the alias will take care of creating databases and
migrating data. ✅
Even more aliases
There’s a lot more you can do with mix aliases. For example, in other projects, I’ve defined an alias to watch assets and prepare them for deployment:
defp aliases do
[
...
"assets.deploy": [
"tailwind default --minify",
"esbuild default --minify",
"phx.digest"
],
"assets.watch": ["esbuild default --sourcemap=inline --watch"]
]
end
The more I can consolidate within mix
, the better I like it.
So give mix aliases a try. And let me know what useful aliases you create. I’d love to incorporate those into my own projects! 😁