You Don't Need a Justfile

I’ve written twice now about Makefiles: once about why they’re useful and once about advanced patterns. So you can probably guess where I stand on this. But I keep getting the same response from people: “Have you tried just? Have you looked at Pixi? Why not use a Taskfile?”

I have. And I keep going back to Make.

The New Hotness

Let me give credit where it’s due. There are a lot of new task runners out there, and they’re not bad tools. The big ones I keep hearing about:

just is probably the most popular Make alternative right now. It’s written in Rust, has a cleaner syntax than Make, and explicitly positions itself as a “command runner” rather than a build system. No more tab-vs-spaces headaches. No more .PHONY everywhere.

Pixi is a package manager from the Prefix.dev folks (the conda ecosystem people) that also includes a task runner. You define tasks in your pixi.toml and run them with pixi run test. It handles environments and dependencies alongside your tasks.

Task (aka Taskfile) is a Go-based runner that uses YAML. It’s like if someone looked at a Makefile and said “what if this was a YAML file instead?” Which, depending on your relationship with YAML, is either appealing or horrifying.

Mise (formerly rtx) is a polyglot tool manager that also includes a task runner. It manages your tool versions (Python, Node, whatever) and lets you define tasks too. Think of it as asdf + Make in one tool.

And there are more. Invoke, doit, pypyr, Nox – the Python ecosystem alone has produced about a dozen of these things.

What They Get Right

I’ll be fair. These tools address real complaints about Make:

  • Tabs vs. spaces: Make’s insistence on literal tab characters for indentation is genuinely annoying. just uses regular indentation. Taskfile uses YAML. Everyone’s happy.
  • Syntax: Make’s syntax is arcane. $@, $<, $$, the difference between = and := – it’s not exactly welcoming. just’s syntax is closer to a shell script, which is what most people actually want.
  • Error messages: When Make fails, the error messages range from unhelpful to actively misleading. “Missing separator” when you used spaces instead of tabs is a classic.
  • Cross-platform: Make on Windows is a pain. just and Task work everywhere out of the box.

These are legitimate gripes. I’ve hit all of them. I’ve spent 20 minutes debugging a Makefile only to realize I had a space where a tab should be. It’s maddening.

What They Get Wrong

Here’s the thing though. Every one of these tools asks you to adopt something new. A new binary to install. A new syntax to learn. A new file format to maintain. And what do you get for that cost? A slightly nicer way to run shell commands.

Let me walk through my actual complaints.

You Already Have Make

Make is on every macOS and Linux machine already. It’s in every CI image. It’s in every Docker base image that has build tools. There’s nothing to install. No setup step for your CI pipeline. No explaining to a new teammate how to get the task runner working before they can run the tests.

With just, you need cargo install just or brew install just. With Pixi, you need to install Pixi. With Task, you need to install Task. These are small things, but they add up. Every tool in your stack is a thing someone has to learn, install, update, and debug when it breaks.

The Problem Is Overstated

The tabs thing? Yeah, it’s annoying the first time you hit it. Then you configure your editor to show whitespace characters and you never think about it again. The syntax? You only need about 10% of Make to use it as a task runner. Define a target, write some shell commands under it, done. The fancy stuff I wrote about in my advanced patterns post is there if you want it, but you can ignore most of it and still get a lot of value.

You Lose Real Features

Make has a dependency graph built in. It knows that if target B depends on target A, and target A hasn’t changed, it can skip re-running A. This is what Make was designed for. The timestamp-based rebuild system I covered in the advanced patterns post genuinely saves time on large projects.

just explicitly does not do this. It’s a command runner, not a build system. That’s fine if all you want is shortcuts for shell commands, but you’re leaving real power on the table. When your test suite takes 30 seconds and your linter takes 15 seconds, being able to skip the lint step when no source files have changed matters.

The Configuration File Arms Race

I’ve watched this pattern play out across the software industry for years. Someone decides the existing tool is too complicated, so they build a simpler one. The simpler one gets popular. People start wanting features. The simpler one gets more complicated. Eventually it’s just as complex as the original tool, but now you have two tools to know about.

just’s justfile syntax has grown to include things like conditional expressions, functions, string manipulation, path handling, and regular expressions. Taskfile has gotten YAML anchors, includes, matrix strategies, and dynamic variables. At some point, you have to ask: if the replacement is going to end up just as complex, why not stick with the one that’s been battle-tested for 50 years?

Fragmentation Is a Tax

Here’s what actually happens on a team: one person sets up the project with a Taskfile because they like YAML. Another person joins from a team that used just. A third person’s previous company used Pixi. Now nobody knows anybody else’s task runner, and you’ve recreated the same problem that Make’s ubiquity was solving.

I’ve worked on projects with Makefiles that were written 10 years ago, and they still work. I have zero confidence that today’s Taskfile will be runnable in 2036 without some fiddling.

When I’d Actually Consider Alternatives

I said I’d be honest, so here’s where I’d bend:

Windows-heavy teams: If most of your team is on Windows and you can’t rely on WSL, Make is genuinely painful. just or Task are reasonable choices here.

You need the environment management: If you’re already using Pixi for conda environment management and you’re just adding tasks to the same config file, that’s fine. The value there is consolidation, not the task runner itself.

The team has already adopted something else: I’m not going to die on this hill. If your team already uses just and everyone’s happy, that’s great. Don’t switch to Make just because some guy with a blog told you to. The best tool is the one your team actually uses consistently.

My Actual Take

Look, I get the appeal. just has a cleaner syntax. Pixi bundles environment management. Taskfile is more readable. These are real advantages, and if one of these tools genuinely makes your team more productive, use it.

But most of the time? Most of the time you’re trading a universal, zero-install, battle-tested tool for a slightly prettier version that does the same thing. You’re adding a dependency to avoid learning a handful of Make conventions. You’re solving a problem that goes away after a week of using Make regularly.

The software industry has a chronic case of thinking that old means bad. Make is old. Make is also really good at what it does. When I type make test in a project I haven’t touched in two years and it just works, that’s the value of choosing boring technology.

I’ll take “ugly but works everywhere” over “pretty but you need to install it” every single time.