varyk GitHub

Why Varyk?

Varyk is a small language for backend services: APIs, workers, and microservices. You write Go-like application code and ship a native binary with Rust's safety and speed, no garbage collector, and the Rust ecosystem behind it. Varyk exists so that ordinary backend services can be written simply and shipped as safe Rust, for teams that want what Rust delivers but cannot afford what Rust costs to learn or to hire for. It keeps every guarantee Rust makes, and it keeps the Rust ecosystem. What it removes is the part of Rust you have to hold in your head.

Why I built it

I came to Varyk through the languages I built services in before it. JavaScript, then Python, then TypeScript: productive languages, but working with data without real types was harder than it needed to be, and TypeScript's types are only as strong as the JavaScript underneath them. I also needed performance those languages could not give me. So I went looking for a language that was fast and had a type system I could lean on.

Go is simple, and I like that. But it has a garbage collector, with the runtime and memory overhead that come with it, and a syntax I never took to. Swift is a good language whose server ecosystem is too small for the microservices I build. Rust has the ideology I believe in: ownership instead of a garbage collector, safety enforced by the compiler, native performance, and an ecosystem that already has everything a service needs. I love it. Day to day, though, it is difficult to work with, and much of the difficulty comes from quirks that have nothing to do with the service being written.

What I wanted did not exist: Rust's ecosystem, guarantees, and performance, with application code as simple as Go. Varyk is that language.

— Vlad Mickevic

What you get

Rust's guarantees, unchanged. A Varyk program becomes a Rust program and is checked by rustc, with the full borrow checker. Memory safety and freedom from data races are not approximated or re-implemented; they are Rust's, because the code is Rust. Varyk never uses unsafe to get around the checker.

Go's simplicity. You write what the program does, and the compiler does the memory bookkeeping that Rust makes you spell out. One way to do each thing, and a language reference short enough to fit in a prompt. A developer arriving from Go, TypeScript, or Python reads and writes Varyk on the first day, not the first month.

The whole Rust ecosystem, with nothing to bootstrap. .rs files sit next to .vr files and the two build together, and since milestone 3 a Varyk package is a Cargo package. You add a crate with cargo add and call it from a .rs file in the package, a facade that wraps what your program needs in plain functions, structs, and enums, which Varyk imports like its own. There is nothing to generate and no second toolchain, because your program is Rust by the time the crate sees it. The goal is that Varyk uses a crate as easily as Rust does; the facade gets most of the way there, and the last step, using a Rust crate straight from Varyk code, is an unscheduled experiment. A Varyk package needs no facade at all: since milestone 5b2, Varyk code names one by its key in Cargo.toml, as in units::length::add(a, b), and varyk builds it from its .vr sources. Every other simpler-than-Rust language starts its library ecosystem from zero. Varyk starts with Rust's.

Zero overhead. No garbage collector, no reference counting, no runtime beyond what Rust already has. Passing a value to a Varyk function never allocates. The compiler inserts exactly two kinds of allocation, a string literal stored into a value you own and, since milestone 5b3, a string passed as a trailing value to a Rust facade function, copied as it is handed over, and --emit-rust shows you where. Nothing is cloned behind your back.

No lock-in. The generated Rust is readable, and it is yours: --emit-rust shows all of it. varyk publish ships a Varyk package as a plain Rust crate, with the generated .rs files and the .vr sources beside them, so consumers need only cargo. If Varyk stops being the right choice, you keep the Rust.

Built for teams that write code with agents. Coding agents already write good code; the slow part is reviewing it, and Varyk is designed to keep reviews short. The language is small, with one way to do each thing, so agent output looks like the code around it. Code is concrete, with few abstractions to see through. rustc checks memory safety and data races in everything the agent writes, so a review can focus on what the service does. The language reference fits in a prompt, and diagnostics come as JSON with codes and fix-its, so the agent fixes its own mistakes before a person looks. One official stack for HTTP, JSON, and databases, JSON built in since milestone 5a and databases and HTTP as the official packages varyk-sql, since milestone 5b3, and varyk-http, since milestone 5b4, and MongoDB as varyk-mongo, after milestone 5, means every Varyk service is written the same way, and what an agent or a reviewer learned in one applies in the next.

Gradual adoption, in both directions. Start in Varyk and drop to a .rs file for the parts that need Rust's full expressiveness, or use Varyk from an existing Rust project: a Varyk library published with varyk publish is a plain Rust crate, which the project adds to [dependencies] like any other and builds with plain cargo. Using a Varyk package from source in a plain Rust project, not only in the form varyk publish assembles, is not supported yet. Rust experts and newcomers work in one codebase, one build, one registry.

The cost of Rust

Rust earns its guarantees by making you write down a decision on almost every line:

That discipline is what makes Rust programs fast and safe. It is also why the first weeks are hard, why the code stays dense long after, why hiring for Rust is hard, and why teams that would benefit from Rust choose Go and accept the garbage collector.

How Varyk removes it

Most of that surface is the spelling of decisions the compiler can make on its own. Varyk keeps Rust's ownership model inside the compiler and takes the spelling out of the language:

Compared with others

Declaring a parameter's passing convention in the signature and inferring the rest is not a new idea. The closest relatives, and the one-line difference from each:

What it is not

Where it stands

Varyk is experimental and pre-1.0: until 1.0, a new feature or a breaking change bumps the minor version. Milestones 1 to 4, 5a, 5b1, 5b2, 5b3, 5b4, and 5c are complete and prove the approach on the example programs with the language surface they need; the roadmap lists what comes after, without dates. The design page has the principles in priority order, and the open questions are recorded in the specification; milestone 5b1 answered two of them, the async runtime shape and ownership transfer, and milestone 5b2 a third: a Varyk library is used from Varyk code by its key in Cargo.toml, with no facade. If you think the bet is wrong, the issues are the place to say so.