Just: One Place to Discover
The discovery surface that turns project incantations into a known place humans and agents both look first · ~14 min read ~– min read · Suggested by Bob engineerpmoperations
Every repo has its own incantations — the build commands, test commands, deploy scripts no one documented. The justfile turns that surface into a known place. Both humans and agents look there first instead of guessing, and that single property compounds across the whole team.
The discovery problem
A senior engineer joining a new codebase used to spend their first day reading scripts — package.json, Makefile, docs, a Slack thread — building a mental model of “which incantation does which thing” that lasted three months until the next refactor. The original author paid pennies; every later contributor paid the same toll. An agent pays the toll every fresh session and never learns: a session that reads config files to infer commands burns 2,000–5,000 tokens; one that opens with just --list burns ~200.
The justfile is for humans first and agents second. The agent benefit is real; the human benefit is what convinces the people who do not work with agents to keep the surface current.
just for humans
Onboarding gets faster. A new contributor runs just --list and is producing useful output by the end of their first hour. The dev and ops control plane lives in one file: just up, just dev, just check, just doctor. The justfile is also real-time documentation — comments above a recipe become its description in just --list, so the docs do not drift because the docs are the recipe definition.
just for agents
An agent reads the same justfile and gets the same benefit, scaled. just --list is the first thing a Claude Code session in a Strike repo runs after orientation; the output goes into context as the canonical action menu. From there the agent does not guess how to run the tests — it runs just api check. Token efficiency is the second-order win: a recipe tuned to print pass/fail summaries by default uses the context budget more efficiently than a raw npm run that floods fifty lines of webpack noise back in.
The one-off rule
Here is the discipline. Every time you (or an agent) runs a one-off command for a control-plane activity — a deploy, a codegen, an env reset — ask whether it should be a recipe. If it will be run more than once, the answer is yes. The opposite failure is also real: a justfile cluttered with unused recipes is harder to read than a tight one. Treat recipes like code — prune the dead ones.
Hierarchies and modules
Strike’s justfile uses just’s mod directive to compose per-package justfiles into one discovery surface (mod api "packages/api", mod web "packages/web", etc.). Recipes are invoked by name — just api check, just web fix, just services up — while the root file holds cross-package orchestration like just check, which runs api, web, mcp, and mock checks in parallel. The split keeps files manageable; nothing has to know about anything outside its scope.
Extract non-trivial scripts
A justfile recipe is a great wrapper and a poor scripting language. Once a recipe grows past ~10 lines, or has real branching or shell quirks, extract it to scripts/foo.sh and make the recipe a one-liner that calls it. just doctor in Strike is two lines pointing at a hundred-line script. Bash and Python both handle complexity better than just does; just’s job is to be the discovery surface.
Strike’s day-1 recipes
Master a short list before going looking for more: just --list, just doctor, just setup, just up / just down, just dev, just check and just check-fast, just fix, and per-package versions like just api check for narrower scope. The setup recipes you usually type yourself; the rest you describe by intent and the agent runs.
just check is the umbrella, not a substitute for thinking about which package you actually changed. If you only touched packages/api, just api check is faster and just as informative.
Sandbox-safe versus sandbox-bypass
A teaser. Most just recipes run cleanly in Claude Code’s default sandbox — just check, just fix, just stats all work without elevated permissions. A handful need bypass: anything that writes to the Go module cache, commits or pushes through git, or installs globally. The full table and trust-model framing live in Your Box and Your Trust Model; the justfile itself stays neutral.
- How We Build Here — The trail's opening cairn. The "deterministic constraints over taste" framing covers why the build's discoverable surface matters as much as the build itself.
- The Workshop — The trail's tool map. Just is one of the required three; this cairn is the deep read on the third of them.
- just — The official upstream documentation. Recipe syntax, command reference, and integration patterns.
- just modules — Documentation for the
moddirective used to compose Strike's hierarchical justfile.
Open a repo you have never seen before. You want to run the test suite. Where do you look? README maybe; package.json scripts; a Makefile; a bin/ directory; a half-finished docs page; the CI config; the slack thread someone sent. Each of those is right somewhere; none of them is right everywhere; and a contributor who guesses wrong burns minutes that compound across the team.
The justfile is a single discovery surface. Run just --list from the repo root and you see every recipe the project considers blessed: build, test, lint, format, deploy, services up, services down, the lot. There is no “how do I” question that the justfile cannot answer for the recipes it covers, because the answer is the recipe name. We use it for our build system. We also use it as the team’s runbook.
This cairn is short on philosophy and long on practice. Most of what makes a justfile valuable is the discipline around it.
The discovery problem
A senior engineer joining a new codebase used to spend their first day reading scripts. They opened the package.json, found a dev script, found the start script, found the build script, then went looking for the test runner because someone had wired it through make test instead. By the end of the day they had a mental model of “which incantation does which thing” — a model that would last about three months before the next refactor invalidated half of it.
That cost is non-trivial. It is also asymmetric: the original author paid pennies; every later contributor pays the same toll the original author dodged. Multiply by the number of contributors and the number of refactors, and the project is renting out a small chronic friction in exchange for not having a discovery surface.
Worse, an agent joining the same codebase pays the same toll, but does not learn. Every fresh session with no skill anchored to your project rediscovers the toolchain from scratch — which scripts run the build, which directory holds the tests, which environment variables matter. That cost shows up as wasted tokens and wasted turns, every time.An honest measurement: a Claude Code session that has to figure out the build commands itself spends 2,000–5,000 tokens just reading config files and inferring what to run. A session that opens with just --list spends ~200. Multiply by the number of sessions a contributor runs in a week.
The justfile is for humans first and agents second. The agent benefit is real and meaningful; the human benefit is what convinces the people who do not work with agents that the justfile is worth keeping current.
just for humans
Onboarding gets faster. A new contributor runs just --list, sees the recipes available, and is producing useful output by the end of their first hour. They do not have to read three READMEs and a Slack thread to figure out what npm run dev:strict-with-coverage was renamed to last quarter.
The dev and ops control plane lives in the same file. just up brings up the local services. just dev launches the tmux dev session. just api dev runs the Go API with hot reload. just web dev runs the Next.js frontend. just services down shuts the stack. just doctor verifies your environment is sane. None of these are particularly clever; the value is that they all live where the contributor knows to look.
The justfile is also a real-time documentation channel. When someone adds a recipe, they document it inline as a comment line above the recipe definition. Run just --list again and the comment shows as the recipe’s description. The docs do not drift, because the docs are the recipe definition. The day a recipe stops working is the day someone runs the recipe and finds out — not three months later when they go to write the docs.
just for agents
An agent reads the same justfile and gets the same benefit, scaled. just --list is the first thing a Claude Code session in a Strike repo runs after orientation; the output goes into the agent’s context as the canonical action menu. From that point on, the agent does not guess “how do I run the tests” — it runs just api check. It does not guess “how do I auto-fix lint issues” — it runs just api fix. The cost of getting the right command right is zero, and the cost of getting it wrong is bounded.
Token efficiency is the second-order win. Every command an agent invokes returns output that flows back into context. A failed npm run invocation that prints fifty lines of webpack noise is a fifty-line tax on the rest of the session. A just check invocation that has been built to print pass/fail summaries first and full output only on demand uses the same context budget more efficiently. The justfile is where you decide what the agent (and the human) sees. Treat that as a feature.
Recipes that an agent runs frequently — check, fix, build — should be tuned to print useful summaries by default and verbose output only when explicitly asked. The agent’s context is finite; do not flood it.
The one-off rule
Here is the discipline that keeps the justfile useful: every time you (or an agent) runs a one-off command for a control-plane activity — a deploy, a codegen, an env reset, a fix-up — ask whether it should be a recipe. If it will be run more than once, the answer is yes.
The principle is not “more recipes is better.” The principle is that the justfile is the place a contributor (or agent) looks first, and a recipe that exists there will be found, while a one-off command lost in a stale Slack thread will not. If you find yourself re-typing a command from memory or copying it out of a chat history, that is a signal: the command wants to be a recipe.
The opposite failure is also real. A justfile cluttered with recipes nobody runs is harder to read than a tight one. Recipes that have not been used in six months should either be promoted (their utility was real but underexposed; document them better) or deleted. Treat recipes the way you treat code: prune the dead ones; the team will thank you.
Hierarchies and modules
Strike’s justfile uses just’s mod directive to compose multiple smaller justfiles into one discovery surface. The root justfile declares modules:
mod api "packages/api"
mod web "packages/web"
mod mcp "packages/mcp"
mod services "just/services.just"
mod conduit "just/conduit.just"
A recipe in a module is invoked by name: just api check, just web fix, just services up. The module’s own justfile holds the recipe definitions; the root justfile holds the orchestration recipes that span modules (just check runs api, web, mcp, and mock checks in parallel).
The split keeps individual files manageable. The api package’s justfile holds Go-specific recipes (fix, check, coverage, dev, build); the web package’s justfile holds Next.js-specific recipes; the root justfile holds the cross-package umbrellas (check, fix, clean). Nothing has to know about anything outside its scope.The mod directive’s other useful property is that you can develop a package’s recipes in isolation — cd packages/api && just check works the same as just api check from the root. Useful when iterating on a single package without the rest of the stack in your way.
Extract non-trivial scripts
A justfile recipe is a great wrapper and a poor scripting language. Once a recipe grows past about ten lines, or once it has real branching, error handling, or shell quirks, extract it: write scripts/foo.sh (or scripts/foo.py), and make the recipe a one-liner that calls it.
# Verify dev environment is ready (exits non-zero if critical issues found)
doctor:
{{justfile_directory()}}/scripts/doctor.sh
That is what just doctor looks like in Strike. The recipe is two lines; the script behind it is a hundred lines of careful environment checks. Both files are easier to read than a single 100-line justfile recipe would be.
The reason is mechanical: justfile recipes run inside a particular shell context with particular escaping rules and particular error-propagation behavior, and the rules become subtle when you have multi-line commands, conditionals, and variable interpolation. Bash and Python both handle complexity better than just does. Just’s job is to be the discovery surface; let bash or python do the actual computation.
Strike’s day-1 recipes
If you are joining the project this week, these are the recipes that show up most often — both in your direct setup work and in the agent’s daily loop. The setup recipes (just setup, just doctor, just hooks) are usually the only ones you type yourself. The rest, you describe by intent (“check this,” “start the dev stack,” “run the gates”) and the agent runs them on your behalf.
| Recipe | What it does |
|---|---|
just --list |
Show every recipe with descriptions. The first command of every session. |
just doctor |
Verify your environment — Go, Node, mise, direnv, Docker, hooks, tools. Run after onboarding and after you change your machine. |
just setup |
Install per-package toolchains (Go modules, npm packages) and wire core.hooksPath. Idempotent. |
just hooks |
Set core.hooksPath = .githooks (one-time per clone, run by setup). |
just up / just down |
Start / stop the local Docker services (Postgres, Keycloak, etc.). |
just dev |
Launch the full tmux dev session — services up, API hot-reloading, web hot-reloading, mock running. |
just check |
Run quality gates across api, web, mcp, mock in parallel. Pre-commit blocker. |
just check-fast |
The fast subset, used by the stop hook and iterative dev. No web build. |
just fix |
Auto-fix lint issues across packages. Always try this before manually fixing. |
just api fix / just api check |
Per-package versions. Same idea, narrower scope. |
just stats |
Code statistics — file counts, lines, by package. Useful when scoping a refactor. |
just check is the umbrella, not a substitute for thinking about which package you actually changed. If you only touched packages/api, just api check is faster and just as informative. just check is for cross-package work and pre-merge confidence.
The full list is longer — there are recipes for AWS SSO login, EKS tunnel management, Conduit publishing, OpenTofu plans, beads sync. Run just --list yourself, or ask the agent “what recipes does this repo expose?” — either way, that surface is the documentation.
Sandbox-safe versus sandbox-bypass
A teaser for the next cairn. Most just recipes run cleanly in Claude Code’s default sandbox — just check, just fix, just stats all work without elevated permissions, because they read files and write to package-local build directories. A handful of recipes need bypass: anything that writes to the Go module cache (just api setup invokes go mod tidy underneath), commits or pushes through git, or installs packages globally.
The full table of sandbox-safe versus sandbox-bypass operations lives in Your Box and Your Trust Model, along with the broader trust-model framing: how much rope you give the agent, where the boundary is, and how to think about it. For now: most justs run by default; a few need permission. The justfile is not the place that decides which is which — your harness’s permissions list is.
Summary
- One discovery surface, two audiences. Humans and agents both run
just --listfirst. The justfile is the team's runbook. - Add a recipe whenever a one-off command will be run more than once. The discipline is what keeps the surface valuable; recipes nobody runs should be pruned.
- Hierarchies via
mod. Per-package justfiles for narrow concerns; the root file for orchestration.just api checkandjust checkare both first-class. - Extract scripts when recipes get complex. Once a recipe has real branching or grows past ~10 lines, move it to
scripts/foo.sh. The justfile is for discovery, not for scripting. - Strike's day-1 set is short.
just --list,just doctor,just setup,just up,just check,just fix. Master those before you go looking for more. - Most recipes run sandbox-safe. A handful need bypass. Your Box and Your Trust Model covers the trust model; the justfile itself stays neutral on which recipes need what.
- If you have used a project that did not have a justfile (or equivalent discovery surface), what did the team use instead, and how did newcomers find their way? Could that surface be lifted into a justfile, or were there reasons it had to stay scattered?
- The "every one-off becomes a recipe if you run it more than once" rule has an obvious failure mode — recipe sprawl. How would you tell a colleague when their proposed new recipe should instead be a comment in the README or a one-line shell alias?
- The justfile is documentation that runs. What other places in our workflow are documentation that should similarly be the same artifact as the thing being documented?
- How We Build Here — The trail's opening cairn. The "deterministic constraints over taste" framing covers why the build's discoverable surface matters as much as the build itself.
- The Workshop — The trail's tool map. Just is one of the required three; this cairn is the deep read on the third of them.
- Beads, the Backbone — The companion cairn on the agent's persistent memory. Like the justfile, beads creates a known surface the agent reaches for first.
- just — The official upstream documentation. Recipe syntax, command reference, and integration patterns. The canonical source for anything this cairn does not cover.
- just modules — Documentation for the
moddirective used to compose Strike's hierarchical justfile. Useful when adding a new module or refactoring an existing one. - Recipe documentation conventions — How comment lines above a recipe become the description shown by
just --list. Adopt this so the docs and the recipes never drift.
Generated by Cairns · Agent-powered with Claude