---
title: Conventions
description: The rules the zap.ts codebase follows, the commands that check them, and the reason behind each one.
---

zap.ts follows a small set of rules. They are written down in `AGENTS.md` at
the root of the repository, so people and AI tools both read the same file.

## The checks

Run these before you open a pull request. Run them from the root folder.

| Command                  | What it checks                     |
| ------------------------ | ---------------------------------- |
| `pnpm run typecheck`     | TypeScript types, in every package |
| `pnpm run lint`          | Mistakes in your code              |
| `pnpm run lint:fix`      | The same, and fixes what it can    |
| `pnpm run format:check`  | Code style                         |
| `pnpm run format`        | The same, and writes the changes   |
| `pnpm run test`          | All tests                          |
| `pnpm run test:coverage` | The same, with a coverage report   |
| `pnpm run test:e2e`      | The app in a real browser          |
| `pnpm run build`         | That every app still builds        |
| `pnpm run fallow`        | Files and libraries nobody uses    |
| `pnpm run react-doctor`  | Common React mistakes              |

To check one package only:

```bash
pnpm --filter @zap-ts/billing run typecheck
```

Some of this runs for you. A hook formats and lints your changed files before
each commit.

## How to write code here

**Write the test first.** Watch it fail. Then make it pass. A test that never
failed has never proved anything.

**Only build what the task asks for.** Do not add a setting nobody has asked
for. Do not add a layer of abstraction that has one user. You can always add
it later, and by then you will know what shape it should be.

**Wait for the third copy.** When you see the same code twice, leave it. When
you see it a third time, move it somewhere shared. Two things that look alike
often stop looking alike a week later.

**Take the smallest change that works.** Try the standard library first. Then
a helper that already exists in zap.ts. Add a new library last.

**Comments say why, not what.** The code already says what it does. A comment
should explain the reason you cannot read from the code, such as why an odd
choice was made.

## Rules that the tools enforce

These are not suggestions. The linter or the build will stop you.

**No version numbers in a `package.json`.** Use `catalog:` for outside
libraries and `workspace:*` for packages inside zap.ts. See
[Dependencies](/guides/dependencies).

**No module mocking in tests.** Pass the dependency in, or write a small fake
of the real interface. See [Testing](/guides/testing).

**Test files sit next to the code they test**, named `*.node.test.ts` or
`*.browser.test.ts`. The name decides where the test runs.

## Why the rules are short

Every rule here has a cost. Someone has to read it, remember it, and follow it
at two in the morning.

So the list stays small on purpose. If a rule does not prevent a real problem
that has actually happened, it does not belong.

## Related

- [Testing](/guides/testing) — how tests are organised
- [Dependencies](/guides/dependencies) — the catalog rule in detail
- [Working with AI agents](/guides/agents) — how `AGENTS.md` is used
- [Effect](/concepts/effect) — reading the code that uses it
