← All articles
TERMINAL Task Runners: Make, Just, Task, and npm Scripts 2026-02-09 · 4 min read · make · just · task-runner

Task Runners: Make, Just, Task, and npm Scripts

Terminal 2026-02-09 · 4 min read make just task-runner automation workflow

Task Runners: Make, Just, Task, and npm Scripts

Every project has commands that developers run repeatedly: start the dev server, run tests, build for production, lint the code, deploy. The question is how you document and run them.

README files with "run these 5 commands in order" inevitably become outdated. npm scripts work for JavaScript projects but feel wrong for non-JS tasks. Makefiles are universal but have arcane syntax. Modern alternatives like just and task solve the common complaints.

npm Scripts

Every JavaScript/TypeScript project already has package.json, so npm scripts are the zero-setup option.

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint . --fix",
    "test": "vitest",
    "test:ci": "vitest run --coverage",
    "db:migrate": "drizzle-kit migrate",
    "db:studio": "drizzle-kit studio",
    "typecheck": "tsc --noEmit"
  }
}
npm run dev
bun run dev    # Faster with Bun
bun dev        # Even shorter — Bun allows dropping "run"

Strengths:

Weaknesses:

Make

Make has been available on every Unix system since 1976. It's the lowest common denominator.

# Makefile
.PHONY: dev build test lint deploy db-migrate

dev:
	bun run dev

build:
	bun run build

test:
	bun test

lint:
	bun run lint

# Commands with dependencies — db-migrate runs before seed
db-seed: db-migrate
	bun run db/seed.ts

db-migrate:
	bunx drizzle-kit migrate

# Parameterized commands
deploy:
	@echo "Deploying to $(ENV)..."
	./scripts/deploy.sh $(ENV)

# Multi-step with error handling
ci: lint typecheck test build
	@echo "All checks passed"

# Commands with environment variables
docker-build:
	docker build -t myapp:$(shell git rev-parse --short HEAD) .
make dev
make ci
make deploy ENV=staging

Strengths:

Weaknesses:

just

just is a command runner that looks like Make but is designed specifically for running commands, not building files. It fixes Make's biggest pain points.

# justfile

# List available commands
default:
    @just --list

# Development server
dev:
    bun run dev

# Run all checks (CI simulation)
ci: lint typecheck test build
    @echo "All checks passed"

# Run tests with optional filter
test filter="":
    bun test {{filter}}

# Database commands
db-migrate:
    bunx drizzle-kit migrate

db-seed: db-migrate
    bun run db/seed.ts

db-reset:
    bunx drizzle-kit drop
    bunx drizzle-kit migrate
    bun run db/seed.ts

# Deploy with required environment parameter
deploy env:
    #!/usr/bin/env bash
    set -euo pipefail
    echo "Deploying to {{env}}..."
    ./scripts/deploy.sh {{env}}

# Docker build with git SHA tag
docker-build:
    docker build -t myapp:$(git rev-parse --short HEAD) .

# Load environment from .env file
set dotenv-load

# Choose shell (bash with strict mode)
set shell := ["bash", "-euo", "pipefail", "-c"]
brew install just

just          # Shows available commands
just dev
just test     # Runs all tests
just test auth # Runs tests matching "auth"
just deploy staging

Strengths:

Weaknesses:

Task (go-task)

Task uses YAML configuration and has built-in features for watching files, running tasks conditionally, and parallel execution.

# Taskfile.yml
version: '3'

dotenv: ['.env']

tasks:
  default:
    desc: Show available tasks
    cmds:
      - task --list

  dev:
    desc: Start development server
    cmds:
      - bun run dev

  test:
    desc: Run tests
    cmds:
      - bun test {{.CLI_ARGS}}

  lint:
    desc: Lint and format code
    cmds:
      - bun run lint

  ci:
    desc: Run all CI checks
    deps: [lint, typecheck]  # Run lint and typecheck in parallel
    cmds:
      - task: test
      - task: build

  db:migrate:
    desc: Run database migrations
    cmds:
      - bunx drizzle-kit migrate

  db:seed:
    desc: Seed database
    deps: [db:migrate]
    cmds:
      - bun run db/seed.ts

  build:
    desc: Build for production
    cmds:
      - bun run build
    sources:
      - src/**/*.ts
      - package.json
    generates:
      - dist/**/*

  watch:test:
    desc: Run tests on file change
    watch: true
    sources:
      - src/**/*.ts
      - test/**/*.ts
    cmds:
      - bun test
brew install go-task

task          # Shows available tasks
task dev
task ci
task test -- --verbose  # Pass args through
task watch:test         # Watch mode

Strengths:

Weaknesses:

Comparison

Feature npm scripts Make just Task
Installation Built-in (JS) Built-in (Unix) Required Required
Config format JSON Makefile justfile YAML
Parameters Awkward Yes Yes (typed) Yes
Dependencies Pre/post hooks Yes Yes Yes (parallel)
Watch mode No (need nodemon) No No Yes
Comments No (JSON) Yes Yes Yes
List commands No No --list --list
.env loading No No Built-in Built-in

Recommendations

JavaScript/TypeScript projects: Start with npm scripts for standard commands (dev, build, test, lint). Add just or task when you need database commands, Docker operations, or deployment scripts that don't fit naturally in package.json.

Multi-language projects: just or Task. Both handle polyglot projects well. just is simpler; Task is more powerful (watch mode, parallel deps, source tracking).

Open source projects: Make. It's everywhere and contributors don't need to install anything. Accept the quirks — the universality is worth it.

Personal preference: If you hate tabs-vs-spaces issues, use just. If you want YAML and watch mode, use Task. If you want zero dependencies, use Make.

The best task runner is the one your team will actually use. Pick one, document your commands, and move on.