Skip to content

Start here

Your first project

brine new creates a project with a program, a test, and an AGENTS.md for coding agents. From then on the loop is: edit, brine check, brine run, brine test. This page walks through each command with the output it prints. You need Brine installed first.

  1. Create the project:

    Terminal window
    brine new hello
  2. Go into it and run it:

    Terminal window
    cd hello
    brine run
    Built hello v0.1.0 [dev] in 2.04s
    Running target/dev/hello.exe
    Hello, brine!
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    The first build also compiles Brine’s runtime, so it takes a couple of seconds. Later builds reuse it. (On Linux and macOS the binary is target/dev/hello, with no .exe.)

  3. Run it again without changing anything:

    Terminal window
    brine run
    Fresh hello v0.1.0 [dev] in 0.02s
    Running target/dev/hello.exe
    Hello, brine!
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    Fresh means nothing changed, so Brine ran the binary it already had.

The Built, Fresh and Running status lines go to stderr; your program’s output goes to stdout. Add -q to hide the status lines.

hello/
├── .gitignore
├── AGENTS.md
├── README.md
├── brine.toml
└── src/
└── main.br

brine new also runs git init in the folder, unless it’s already inside a git repository. To turn an existing folder into a project, run brine init in it; it creates the same files and skips any that already exist.

brine.toml
[package]
name = "hello"
version = "0.1.0"

The file marks the project root. brine commands look for it in the current folder and its parents, so you can run them from anywhere inside the project. The program starts at src/main.br. See Projects for the other settings, such as [check] typed = true.

src/main.br
# Welcome to brine! Run this program with `brine run`.
fn greet(name: str) -> str:
return f"Hello, {name}!"
fn main():
print(greet("brine"))
# parallel by default: this runs across all your cores
let squares = par [i * i for i in 0..10]
print(squares)
test "greeting":
assert greet("you") == "Hello, you!"

fn main() is where the program starts. par [...] builds the list of squares on every core. The test block at the end is a test, which brine run ignores and brine test runs.

Instructions for coding agents working in the project: the commands to run and the rules that most often trip up someone who knows Python. Many coding agents read AGENTS.md on their own when they start work in a folder. See AGENTS.md and common mistakes for its full text and an explanation of each rule.

.gitignore holds one line, /target: builds and the build cache go in target/ (target/dev/, target/release/, target/cache/). README.md is a short stub for you to fill in.

Command What it does
brine check type-check and prove, without building; the fastest feedback
brine run build src/main.br and run it
brine test build and run every test "...": block, in parallel
brine fmt rewrite every source file in the standard format
brine build build a binary into target/dev/ (or target/release/ with --release)
Terminal window
brine check
Checked hello in 0.00s — 0 runtime checks proven unnecessary, 1 remain (see --report)

check parses, type-checks, and runs the prover. It doesn’t build anything, so it’s the command to run after every edit. The second half of the line counts the safety checks (array bounds, overflow, division by zero) that the prover removed and those that stay in the program as runtime checks. --report lists the ones that remain:

Terminal window
brine check --report
src/main.br:10:24: overflow check `i * i`
Checked hello in 0.00s — 0 runtime checks proven unnecessary, 1 remain

The prover handles linear arithmetic, so it can’t show that i * i stays in range; the program checks it at runtime instead. A remaining check is not an error. See Runtime checks.

Terminal window
brine test
Built tests [dev] in 0.10s
test greeting ... ok
test result: ok. 1 passed; 0 failed; finished in 0.02s

Each test runs in its own process. brine test shout runs only the tests whose names contain shout. See Testing.

Terminal window
brine fmt
1 file checked, 0 reformatted

There’s one standard format (4-space indentation, spaces around operators), so code always looks the same. brine fmt --check changes nothing and exits with status 1 if a file needs formatting, for CI. See Formatting.

Here is one round of changes: add a function and a test, make a mistake, and fix it.

  1. Add a shout function and a test for it to src/main.br. Write it carelessly, and pass it a number by mistake:

    src/main.br
    # Welcome to brine! Run this program with `brine run`.
    fn greet(name: str) -> str:
    return f"Hello, {name}!"
    fn shout(name:str)->str:
    return greet(name).upper()
    fn main():
    print(greet("brine"))
    print(shout(42))
    # parallel by default: this runs across all your cores
    let squares = par [i * i for i in 0..10]
    print(squares)
    test "greeting":
    assert greet("you") == "Hello, you!"
    test "shouting":
    assert shout("you") == "HELLO, YOU"
  2. Check it:

    Terminal window
    brine check
    error[E0301]: mismatched types
    --> src/main.br:11:17
    |
    11 | print(shout(42))
    | ^^ expected `str`, found `int`
    |
    = help: convert with `str(...)` or use an f-string like f"{x}"
    = run `brine explain E0301` for more about this error
    error: could not compile due to 1 error

    Every error has a code, the exact place, and a hint. brine explain E0301 explains the code at length. Change the call to shout("brine").

  3. Format the file:

    Terminal window
    brine fmt
    Formatted src/main.br
    1 file checked, 1 reformatted

    fn shout(name:str)->str: is now fn shout(name: str) -> str:, and its body is indented by 4 spaces.

  4. Run the tests:

    Terminal window
    brine test
    Built tests [dev] in 0.09s
    test greeting ... ok
    test shouting ... FAILED
    ---- shouting ----
    panic: assertion failed: shout("you") == "HELLO, YOU"
    --> src/main.br:21:5
    test result: FAILED. 1 passed; 1 failed; finished in 0.03s

    The expected string is missing its !. Fix the test to compare with "HELLO, YOU!" and run brine test again:

    Built tests [dev] in 0.09s
    test greeting ... ok
    test shouting ... ok
    test result: ok. 2 passed; 0 failed; finished in 0.02s
  5. Run the program:

    Terminal window
    brine run
    Built hello v0.1.0 [dev] in 0.09s
    Running target/dev/hello.exe
    Hello, brine!
    HELLO, BRINE!
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

You don’t need a project to try something. Every command also takes a file:

Terminal window
brine run scratch.br
brine check scratch.br
brine test scratch.br
brine run scratch.br -- first second # arguments after `--` go to the program

brine run --release and brine build --release optimize through the C compiler. They take longer to build and run faster:

Terminal window
brine build --release
Built hello v0.1.0 [release] in 2.59s
Output target/release/hello.exe