The Gate

11 goroutines up 1m 00s

Experiment · a process of its own

The build is the audit.

Go is strict enough that a broken program does not start, and small enough that a model can still write the next draft. JavaScript will run the draft and wait. Rust may refuse the draft and the repair. This page is that argument, running.

Four places a generated program can land

Runs anythingRefuses the unsafe

Scroll. The doors open.

JavaScript runs the file.

A wrong type becomes a quiet coercion, or a crash on a later line, when a person finally reaches it. The model can ship that. You find out in production, or in a test you thought to write.

Bun shortens the news.

Bun is a JavaScript runtime written in Zig, on JavaScriptCore. It starts quickly and runs TypeScript without a separate build step. The language still accepts the programs Node would accept. You hear about the failure sooner. You do not hear about a different class of failure.

Go fails the build, and the message is short.

Unused names, unknown imports, and type mismatches stop the build. The compiler names a line. You pass that line back to the model and continue. Errors are ordinary return values, so a missed check is visible in the source. The build does not refuse a program merely because an error value was ignored. For a person prompting an application into existence, this is the useful end of the spectrum: the compiler audits the draft.

Rust refuses more, including the repair.

The borrow checker blocks whole families of memory bugs before a program can exist, and Option and Result make the empty and the failed cases part of the type. Models imitate the syntax and miss the ownership. The error you paste back often creates a different error. That is a poor fit for staying in flow, and a good fit when the bug is unaffordable.

01

Where the languages diverge

The difference is not taste. It is what happens to a draft that is almost a program.

01 Types

Go

A value has a type, and the compiler checks it. Specimen 01 is a one-line disagreement. The build stops, and nothing is served.

JavaScript

A value has a type at runtime. The same call can coerce or throw, depending on what arrives. TypeScript can describe the intent. A model can still step around it.

Rust

The types are stricter, and a match has to be complete. A case the model forgot does not compile away into a surprise.

02 Failures

Go

A fallible function returns an error value. You can see it in the signature. You can also ignore it. The language makes the failure visible. It does not force the check.

JavaScript

Failures throw. If nothing catches them, they surface later, often far from the line that caused them.

Rust

Failure is a Result and absence is an Option. Using the inner value means dealing with both. An unused Result warns. A warning is not, by itself, a failed build.

03 Dependencies

Go

HTTP, JSON, HTML, crypto, and logging live in the standard library. A made-up import fails the build, the same way specimen 02 fails on a made-up name.

JavaScript

The package index is huge, and Bun installs it quickly. A package that does not exist fails at install or at import. The project is already in motion by then.

Rust

Cargo resolves real crates and real versions. A model can still pick a real crate and use it wrongly. Resolution is not understanding.

04 Shape

Go

The syntax is small, and gofmt makes programs look related. There is less room to mix three eras of a framework, because there is one usual way to write the thing.

JavaScript

There are many frameworks, and several eras of each. Generated code will combine them. Bun makes that cycle fast. It does not choose an idiom.

Rust

The language is opinionated, and it is much larger. Ownership and lifetimes are where a generated draft tends to fall over, even when the idea was sound.

02

How hard the gate shut

Five specimens, three languages. Bar height is how hard the draft was stopped. The language selected above stays bright. Click a bar to reach that specimen.

Hover a bar. Height is how hard the gate shut. Color is the result.

  • Refused
  • Warned
  • Crashed
  • Ran

03

What the compilers on this machine said

Five fixed programs. Each one was compiled or executed here, with go version go1.27.1 linux/amd64, rustc 1.98.1 (48a229cea 2026-09-01), and bun 1.3.14. A visitor cannot send source to this process. The transcripts are embedded.

Specimen 01

A number that was a string

The function asks for two numbers. The call site passes a string. Bun multiplies anyway and prints 57, which looks like a successful total. Go and Rust do not produce a binary.

JavaScript · Bun

Ran

Source

function total(price, qty) {
  return price * qty;
}

console.log(total("19", 3));

Transcript

57

bun specimen.js · 20 ms · exit 0

bun 1.3.14

Go

Refused

Source

package main

import "fmt"

func total(price int, qty int) int {
	return price * qty
}

func main() {
	fmt.Println(total("19", 3))
}

Transcript

# specimen
./main.go:10:20: cannot use "19" (untyped string constant) as int value in argument to total

go build -o app . · 89 ms · exit 1

go version go1.27.1 linux/amd64

Rust

Refused

Source

fn total(price: i32, qty: i32) -> i32 {
    price * qty
}

fn main() {
    println!("{}", total("19", 3));
}

Transcript

error[E0308]: mismatched types
 --> specimen.rs:6:26
  |
6 |     println!("{}", total("19", 3));
  |                    ----- ^^^^ expected `i32`, found `&str`
  |                    |
  |                    arguments to this function are incorrect
  |
note: function defined here
 --> specimen.rs:1:4
  |
1 | fn total(price: i32, qty: i32) -> i32 {
  |    ^^^^^ ----------

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0308`.

rustc --edition 2021 -o app specimen.rs · 68 ms · exit 1

rustc 1.98.1 (48a229cea 2026-09-01)

Specimen 02

A helper that was never defined

The model calls a function that does not exist, after one honest line. Bun prints that line, then throws. The Go and Rust programs never start.

JavaScript · Bun

Crashed

Source

console.log("server up");
console.log(hallucinatedHelper());

Transcript

server up
1 | console.log("server up");
2 | console.log(hallucinatedHelper());
                ^
ReferenceError: hallucinatedHelper is not defined
      at <anonymous> (specimen.js:2:13)

Bun v1.3.14 (Linux x64)

bun specimen.js · 20 ms · exit 1

bun 1.3.14

Go

Refused

Source

package main

import "fmt"

func main() {
	fmt.Println("server up")
	fmt.Println(hallucinatedHelper())
}

Transcript

# specimen
./main.go:7:14: undefined: hallucinatedHelper

go build -o app . · 81 ms · exit 1

go version go1.27.1 linux/amd64

Rust

Refused

Source

fn main() {
    println!("server up");
    println!("{}", hallucinated_helper());
}

Transcript

error[E0425]: cannot find function `hallucinated_helper` in this scope
 --> specimen.rs:3:20
  |
3 |     println!("{}", hallucinated_helper());
  |                    ^^^^^^^^^^^^^^^^^^^ not found in this scope

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0425`.

rustc --edition 2021 -o app specimen.rs · 68 ms · exit 1

rustc 1.98.1 (48a229cea 2026-09-01)

Specimen 03

Two ways to change one value

Two live references to the same mutable value. JavaScript and Go both update it and print 3. Rust stops at the second borrow, before a program exists.

JavaScript · Bun

Ran

Source

const box = { value: 1 };
const left = box;
const right = box;
left.value += 1;
right.value += 1;
console.log(box.value);

Transcript

3

bun specimen.js · 19 ms · exit 0

bun 1.3.14

Go

Ran

Source

package main

import "fmt"

func main() {
	n := 1
	left := &n
	right := &n
	*left += 1
	*right += 1
	fmt.Println(n)
}

Transcript

3

go build -o app . && ./app · 241 ms · exit 0

go version go1.27.1 linux/amd64

Rust

Refused

Source

fn main() {
    let mut n = 1;
    let left = &mut n;
    let right = &mut n;
    *left += 1;
    *right += 1;
    println!("{n}");
}

Transcript

error[E0499]: cannot borrow `n` as mutable more than once at a time
 --> specimen.rs:4:17
  |
3 |     let left = &mut n;
  |                ------ first mutable borrow occurs here
4 |     let right = &mut n;
  |                 ^^^^^^ second mutable borrow occurs here
5 |     *left += 1;
  |     ---------- first borrow later used here

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0499`.

rustc --edition 2021 -o app specimen.rs · 58 ms · exit 1

rustc 1.98.1 (48a229cea 2026-09-01)

Specimen 04

A name left behind

An unused binding, the kind of leftover a model leaves in a file. JavaScript prints "published" and ignores the spare name. Go refuses to build. Rust warns, and still builds.

JavaScript · Bun

Ran

Source

const draftTitle = "The Gate";
console.log("published");

Transcript

published

bun specimen.js · 21 ms · exit 0

bun 1.3.14

Go

Refused

Source

package main

import "fmt"

func main() {
	draftTitle := "The Gate"
	fmt.Println("published")
}

Transcript

# specimen
./main.go:6:2: declared and not used: draftTitle

go build -o app . · 96 ms · exit 1

go version go1.27.1 linux/amd64

Rust

Warned

Source

fn main() {
    let draft_title = "The Gate";
    println!("published");
}

Transcript

warning: unused variable: `draft_title`
 --> specimen.rs:2:9
  |
2 |     let draft_title = "The Gate";
  |         ^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_draft_title`
  |
  = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default

warning: 1 warning emitted

--- program ---
published

rustc --edition 2021 -o app specimen.rs && ./app · 119 ms · exit 0

rustc 1.98.1 (48a229cea 2026-09-01)

Specimen 05

An average that is wrong

The average of 10, 20, and 30 is 20. All three programs build, run, and print 15. The extra divisor is ordinary bad arithmetic. The compilers accept it because the program is well-formed.

JavaScript · Bun

Ran

Source

function average(values) {
  let sum = 0;
  for (const value of values) {
    sum += value;
  }
  return Math.floor(sum / (values.length + 1));
}

console.log(average([10, 20, 30]));

Transcript

15

bun specimen.js · 21 ms · exit 0

bun 1.3.14

Go

Ran

Source

package main

import "fmt"

func average(values []int) int {
	sum := 0
	for _, value := range values {
		sum += value
	}
	return sum / (len(values) + 1)
}

func main() {
	fmt.Println(average([]int{10, 20, 30}))
}

Transcript

15

go build -o app . && ./app · 250 ms · exit 0

go version go1.27.1 linux/amd64

Rust

Ran

Source

fn average(values: &[i32]) -> i32 {
    let mut sum = 0;
    for value in values {
        sum += value;
    }
    sum / (values.len() as i32 + 1)
}

fn main() {
    println!("{}", average(&[10, 20, 30]));
}

Transcript

15

rustc --edition 2021 -o app specimen.rs && ./app · 112 ms · exit 0

rustc 1.98.1 (48a229cea 2026-09-01)

04

What still gets through

  1. Logic

    Specimen 05 prints 15 where the average is 20. Go, Rust, and Bun agree with each other, and they are wrong. The gate checks form. It does not check intent.

  2. Bulk

    Go’s explicit errors can turn into a page of repeated checks. The file compiles. It is still a chore to read. A rigid language can be verbosely empty.

  3. Structure

    Packages, transactions, and goroutines can be arranged badly in code that builds. Go will not stop a data race. The race detector is a separate run. Rust’s safe code will not compile a data race. That is the wall again, and the reason Go is the practical middle.

05

Running on this machine

The page you are reading is the stack. One Go process renders the HTML. htmx asks for a fragment and swaps it in. SQLite is a file. A goroutine ticks once a second. There is no Node process in front of this site.

  • net/http
  • html/template
  • embed
  • log/slog
  • context
  • database/sql
  • modernc.org/sqlite
  • htmx
Binary
19.4 MB, templates and fonts inside it
Router
The standard library ServeMux, with the pattern syntax from Go 1.22
Database
SQLite through a pure-Go driver, so the binary does not need a C compiler
This build
go1.27.1
Goroutines
11
Heap
2.7 MB
Uptime
1m 00s
Responses
57
Page views
42
Heartbeat
300ms ago

slog · UTC

  1. 08:04:58 INFO request method=GET path=/robots.txt status=404 ms=0
  2. 08:04:40 INFO request method=GET path=/ status=200 ms=11
  3. 08:04:11 INFO request method=GET path=/ status=200 ms=5
  4. 08:04:03 INFO request method=GET path=/ status=200 ms=10
  5. 08:03:58 INFO listen addr=127.0.0.1:8791

hx-get="/partials/vitals" hx-trigger="every 3s" hx-swap="innerHTML"

The bright line is goroutines. The pale line is the heap, in megabytes. A point is added each time the vitals refresh.

Context deadline

Waiting

Nothing is running. The work itself takes about 250ms. The budget is the only thing that changes.

Circuit breaker

Closed

No calls yet. Three failures open the breaker for 12 seconds.

Failures
0 / 3
Left the process
no

The breaker is the pattern. The library used in production is github.com/sony/gobreaker. The one here is small, the states are real, and yours is separate from everyone else’s. Closed, then open after three failures, then a single trial.

06

When a site has to grow up

Go stays small on purpose. A larger site adds a tool for each operational problem, instead of adopting a framework that owns the process. These are the ones worth knowing. This exhibit already uses slog, context, and the breaker. The rest are for when the file stops being the whole program.

01 sqlc

sqlc.dev

You write the SQL. It generates the Go. A wrong column fails before the program runs, which is the same kind of gate as the rest of the language.

02 migrate

github.com/golang-migrate/migrate

Numbered SQL files move the schema. Local, staging, and production stay on the same version when the binary ships.

03 bcrypt

golang.org/x/crypto/bcrypt

Password hashing with the library next to the standard set. Less room for a model to invent a hasher.

04 go-oidc

github.com/coreos/go-oidc

OpenID Connect, for signing in through an identity provider. Tokens get checked.

05 scs

github.com/alexedwards/scs

Server-side sessions. The cookie holds an id. The session data stays on the server.

06 slog

log/slog

Structured logs in the standard library. This process writes JSON to its stdout, and the dark panel above is the same stream, shortened.

07 Prometheus

github.com/prometheus/client_golang

Counters and histograms for latency, memory, and in-flight work. The numbers above are that idea, drawn directly instead of scraped.

08 OpenTelemetry

go.opentelemetry.io/otel

A trace that follows one request through every service it touches, so a slow page has a location.

09 context

context

A deadline and a cancel signal passed into the work. The buttons above give a stand-in query 250ms, then abandon it when the budget is 40ms.

10 wire

github.com/google/wire

Dependency injection at compile time. The graph is checked when you build, and it adds nothing while the process runs.

11 A single binary

go build

The server, the templates, and the static files are one executable. The host does not need a language runtime installed for this site. Copy the file, point a vhost at it, and it is the same program that ran in development.

07

Where should the correction come from?

Anonymous, stored as four integers in SQLite. The bars are the file, read back on this request.

  1. JavaScript 1 5%
  2. Bun 4 22%
  3. Go 5 27%
  4. Rust 8 44%

18 recorded in the SQLite file.