Mise: powerful dev tool

I imagine many people who work in software development - or do it as a hobby - are familiar with the situation where they have to install, configure, and maintain a variety of tools. At work, for instance, our services are written in Go, but integration tests are written in Java. As a result, developers and test engineers can sometimes struggle to run each other's code. On top of that, there are numerous tools like database and api code generators, build systems with custom configurations, different versions of the languages themselves, a random nodejs in some of the microservices, and so on. None of this is an insurmountable obstacle, but these things can waste our time, be a source of frustration, or even stop us from getting things done.

There is another way. Recently, we've decided to try mise - a "tool to manage languages, env vars, and tasks per project." For a Go service, we can create a mise.toml file in the repository with the following tools:

[tools]
go = "1.26.2"
sqlc = "1.31.1"
golangci-lint = "2.11.4"

Then run mise install and all the necessary tools will be at our fingertips within seconds - confined to this project. Mise integrates nicely with both your terminal and your IDE, so no extra work is required to start using them. Do we also need Java and Maven to run tests? Let's add them too:

[tools]
go = "1.26.2"
sqlc = "1.31.1"
golangci-lint = "2.11.4"
java = "25"
maven = "3"

Is something missing from the registry? If it releases on GitHub, mise can handle it:

[tools]
"github:exercism/cli" = "3.5.8"

Tools are installed in the ~/.local/mise directory, so they are independent from your system's package manager with no conflicts between the two. This separation also means we can get anything we want regardless of whether we're on stable Debian, bleeding-edge Arch, or macOS.

Another useful feature is tasks. Similar to Makefiles, mise can be used to store and execute repeatable commands.

[tasks."start"]
run = "go run ."
description = "Launch the service"

[tasks."test"]
run = "go test ./..."
description = "Execute unit tests"

[tasks."codegen"]
run = """
rm -rf internal/codegen/
sqlc generate -f spec/codegen/sqlc.yaml
"""
description = "Generate code from database and api specs"

Tasks are quite flexible and configurable - they can have dependencies, variables, their own tools, outputs, and so on.

If the service and other tasks require env vars to function, mise can read them from an .env file:

[env]
_.file = ".env"

[tasks."start"]
run = "go run ."
description = "Launch the service"

Isn't this lovely? I really enjoy working with mise as it simplifies the work environment for everyone involved - including CI/CD pipelines. Even in pet projects I find it helpful.

Trivia: how do you pronounce mise? The answer might shock you!