`just` Is My Go-To Command Runner Now

Why I switched from make to just as my daily command runner — covering discoverability, documentation, parameters, environment variables, platform-specific recipes, and chaining commands.

just Is My Go-To Command Now

I used to rely heavily on make for running common project commands—things like pnpm dev, bun run build, or cargo run. Over time, it became more friction than help. Remembering recipe names was annoying, listing commands wasn’t straightforward, environment variables felt hacky, and cross-platform support was basically a no-go. make was designed for generating files, not really for being a daily command runner, which is how I was using it.

That’s when I switched to just, and honestly, it fits this use case far better.

just is a command runner, not a build system pretending to be one. It’s open source, easy to read, and very intentional about developer ergonomics. You write a justfile, define your commands (recipes), and that’s it. No magic, no weird edge cases.

Discoverability Matters

One of the biggest wins is discoverability.

just --list
just --choose

You immediately see what commands exist, grouped and documented. There’s even a built-in interactive selector. This alone removes the “what was that command again?” problem.

Grouping and Documentation

Commands can be grouped and documented directly in the file:

# Build the project
[group('dev')]
build:
    echo "Building the project..."
    bun run build

# Run tests
[group('dev')]
test:
    echo "Running tests..."
    bun run test

# Deploy the project
[group('deploy')]
deploy:
    echo "Deploying the project..."
    bun run deploy

Which shows up nicely when listed:

Available recipes:
    [deploy]
    deploy # Deploy the project

    [dev]
    build  # Build the project
    test   # Run tests

Readable, obvious, and self-documenting.

Parameters Without Pain

Passing arguments is simple and explicit:

dev bin="api":
    npm run -- --bin {{bin}}
just dev api

No shell gymnastics, no confusing syntax.

Environment Variables Done Right

Environment handling is built in, not bolted on.

You can define them directly:

export NODE_ENV = "development"
export PORT = 3000

Or load them automatically from a file:

set dotenv-load
set dotenv-filename := ".env.local"

This works consistently across platforms, which is a big deal when targeting Web, Windows, Android, iOS, and Linux.

Platform-Specific Recipes

This is one of those features you don’t realize you need until you have it:

[windows]
open:
    start http://localhost:3000

[linux]
open:
    xdg-open http://localhost:3000

[macos]
open:
    open http://localhost:3000

Same command, different implementation, zero conditionals.

Private Recipes and Dependencies

You can hide internal helpers by prefixing them with _, and define dependencies cleanly:

lint:
    echo "linting"
    pnpm format

test: lint
    pnpm test

No surprises, execution order is clear.

Small but Useful Extras

  • Built-in functions like {{ os() }}

  • Custom shell configuration

  • Sequential execution:

    just lint test build

Chaining Commands

Another feature I use more often than I expected is chaining commands. You can run multiple recipes sequentially with a single invocation:

just lint test build

This keeps workflows simple and readable without creating artificial “meta” recipes. Each command stays focused, but you still get a smooth end-to-end flow when you need it.

Final Thoughts

Overall, just feels like a tool that understands how developers actually work day to day—and once it’s in place, it quietly disappears and just gets out of the way. If you haven’t tried it yet, it’s worth a look. You might not go back.

Comment down below what’s your favorite feature or any suggestion for others or how you are using just

💬 Want to learn, build, and grow with a community of developers? Join the King Technologies Discord — where code meets community! 🚀