Varyk
Build backend services simply. Ship Rust binaries.
Varyk is a small language for APIs, workers, and microservices, created by Vlad Mickevic. It removes Rust's ownership ceremony and keeps Rust's safety, speed, ecosystem, and deployment model: you write Go-like application code, the compiler turns it into readable Rust, rustc checks it, and you ship one native binary. Rust's safety, Go's simplicity.
- No garbage collector: memory is managed the way Rust manages it.
- No lifetime annotations, no
&or&mutto choose at a call site, and one string type. - One native binary to deploy, with no language runtime to install.
- Cargo and crates.io underneath: a Varyk package is a Cargo package.
- Readable generated Rust, yours to keep.
- Drop into Rust whenever you need it, in a
.rsfile beside your Varyk, in the same build.
JSON, configuration from the environment, logging, tests, async functions
and tasks, and Varyk packages that use Varyk packages work today, and so do
SQL databases through the varyk-sql
package (varyk add sql), and HTTP services through the
varyk-http package
(varyk add http), with every route checked against its handler before
anything runs. What works today is under Status.
Use Varyk until you need Rust
A Varyk package is a Cargo package: a Cargo.toml and a src/main.vr or
src/lib.vr. varyk init writes one, varyk run builds it, and varyk publish ships it to crates.io as a plain Rust crate that needs no Varyk
to use. There is nothing to bootstrap: every crate on crates.io is available
from the first day, and every Varyk package joins them.
| Layer | File | |
|---|---|---|
| Your service | src/main.vr |
Varyk. It never names a crate, and it never writes a reference. |
| Your facade | src/text.rs |
Rust. It wraps what the program needs in plain functions, structs, and enums, which Varyk imports like a module of its own. |
| Any crate | crates.io | Added with cargo add. Need Stripe, AWS, or Kafka? Use the Rust crate. |
examples/packages/matcher wraps regex-lite this way:
// src/text.rs
// src/main.vr
mod text;
fn main() {
match text::Matcher::new("[0-9]+") {
Ok(digits) => println!("{}", digits.count("route 66 or 101")),
Err(e) => println!("not a pattern: {}", e),
}
}
Need something Varyk doesn't provide? Use the Rust crate. Need Rust itself,
for the one hot loop or the borrow-heavy library? Write it in the .rs
file and call it from Varyk. Varyk is for the application: kernels,
database engines, custom allocators, and borrow-heavy libraries stay in
Rust, in the same build.
How it works
Varyk compiles to Rust and runs on the Rust ecosystem, the way TypeScript
compiles to JavaScript, though Varyk is not a superset of Rust. Most of
Rust's surface is the spelling of decisions the compiler can make on its
own: which & to write, which string type, which lifetime. Varyk keeps
Rust's ownership model inside the compiler and takes the spelling out of
the language. The Rust compiler checks everything Varyk generates, so the
guarantees are Rust's own.
examples/borrowing.vr:
struct User {
name: string,
}
fn rename(mut user: User) {
user.name = "Bob";
}
fn print_user(user: User) {
println!("{}", user.name);
}
fn main() {
let mut user = User {
name: "Alice",
};
print_user(user);
rename(user);
print_user(user);
}
It prints Alice, then Bob. This is the generated src/main.rs for it,
as the compiler writes it; varyk build --emit-rust examples/borrowing.vr
prints the same file after the generated Cargo.toml, passed through
rustfmt when rustfmt is installed:
varyk build writes that Cargo project under target/varyk/ and hands it
to cargo; rustc and the full borrow checker check it like any other Rust
code, and Varyk never uses unsafe to get around them. The project is
readable, and it is yours.
Who it is for
Varyk exists so that ordinary backend services can be written simply and shipped as safe Rust. First, developers building services in Go, TypeScript, or Python, who want Go's simplicity and one-binary deployment without the garbage collector, with rustc catching the bugs those languages compile. Then developers coming from Rust, who want the same safety model with less ceremony for application code. And teams that write code with coding agents: agents already write good code, and the slow part is reviewing it. A small, concrete language with one way to do each thing keeps agent output reviewable, rustc checks its memory safety and data races, and diagnostics with codes and fix-its come in machine-readable form, so the agent fixes its own mistakes before a person looks.
Try it
You need a stable Rust toolchain, 1.85 or newer, installed through
rustup. Varyk calls cargo to build your program.
Install the compiler from crates.io, write a new package, and run it:
Or build the compiler from this repository and run the first example:
The main commands:
varyk check [file.vr] check the program for errors
varyk build [file.vr] generate and build the Rust; prints the executable path
varyk run [file.vr] build, then run the program, forwarding its exit code
varyk test [file.vr] build the program's #[test] functions and run them
varyk init [dir] write a new package; --lib writes a library
varyk add [args] run cargo add in the package; varyk add sql adds the database package
varyk publish publish the package to crates.io as a plain Rust crate
Without a file, a command works on the package that contains the current
directory (it searches upward for Cargo.toml).
build and run accept --release, and run passes the arguments after
-- to the program. build also accepts --emit-rust, which prints the
generated Cargo.toml and Rust. Every command accepts
--message-format=json, which prints errors as JSON, one object per line.
When running from this repository, put cargo run -p varyk -- in front, as
in the example above.
Status
Varyk is experimental and pre-1.0: anything may change, including any
syntax, error code, or command-line flag, and a new feature or a breaking
change bumps the minor version. This is milestone 5b4: the compiler's
side of HTTP, on top of milestone 5b3's facades for packages, milestone
5b2's Varyk packages that use Varyk packages, milestone 5b1's async
functions and tasks, milestone 5a's data, configuration, logging, and
tests, and milestone 4's closures, iterators, and patterns. Structs,
enums, and match, for loops, methods, Option, Result, Vec, ?,
and format! work, and so do packages with dependencies, modules at any
depth, use, private fields, and Rust structs and enums imported from
.rs files; the compiler builds and runs every program in examples/,
and it reports every error it knows about with a code, a plain-word
message, and, where it can, a suggested fix. Dates and times, and much
more, are not there yet; see
docs/language.md for exactly what works.
Milestone 5b4 makes the compiler ready for
varyk-http, the HTTP
package, which lives in its own repository and is published on
crates.io. With it in a build,
app.get("/users/{id}", get_user) adds a route, and varyk check
checks the path against get_user's parameters; a handler's parameters
are filled from the path, the query string, the JSON body, and
the app's shared state, and what it returns is the answer, None a 404.
before, before_on, and after hooks run around each request, and
app.request sends one through the app in a test. An Error can carry an
HTTP status (Error::with_status(404, "...") in the language,
http::bad_request("...") in the package); one without a status is a 500
whose message is logged and never sent to the client. A .rs facade can
also take any value JSON can write (&T: Serialize), and varyk add
takes several official packages at once (varyk add http sql). See
"HTTP" in
docs/language.md.
Milestone 5b3 lets a package's .rs facade take a type parameter filled
from where the result goes, any number of plain values after the other
arguments, and a parameter that takes only text written in the program,
and adds pub use and varyk add sql. Together they are the compiler side
of the database package varyk-sql,
which lives in its own repository and is published on crates.io: SQLite,
Postgres, and MySQL through sqlx, with a query's values always passed beside
its text. See "Calling Rust" in docs/language.md.
Milestone 5b2 lets a Varyk package use another one by its Cargo.toml key
(units::length::add(a, b)), to any depth; varyk compiles each from its
.vr files and is the only builder of a Varyk package. See
examples/packages/trip.
Milestone 5b1 adds async fn and .await; a call without .await starts
a task, and Task::all, Task::all_settled, .detach(), Shared<T>, and
time::sleep wait for, share with, and time tasks, on a built-in
multi-threaded runtime. Task, Shared, and time are now reserved names,
a breaking change. See examples/tasks.vr, fanout.vr, and shared.vr.
Milestone 5a adds a built-in Error, so parse returns a Result;
attributes #[rename], #[default], #[skip], and #[test]; json::parse
and json::stringify; env::parse, which fills a struct from the
environment and .env; log::info and its three siblings; and
varyk test and varyk add. The varyk-std crate behind them is a
dependency of every package. parse returning a Result is a breaking
change. The users package in examples/packages/ shows them together.
Milestone 4 adds closures, as the argument of a call like filter or
map; chains such as names.iter().filter(|n| n.len() > 3).count();
HashMap; and getters that return part of what they are given without
copying it. Nothing is written for that: Varyk sees that every return of
display_name is part of self, and the generated Rust returns a
reference into it, where milestone 3 needed a .clone():
fn display_name(self) -> string {
if self.nickname.is_empty() { self.name } else { self.nickname }
}
Documents
- varyk.com: the project site.
- docs/language.md: the language reference, one page.
- docs/design.md: the principles and the decisions behind them.
- docs/open-questions.md: questions not yet answered.
- docs/roadmap.md: what comes next.
- docs/specs/2026-09-23-varyk-design.md: the full design.
- docs/specs/2026-09-25-milestone-2-design.md: milestone 2's additions.
- docs/specs/2026-09-26-milestone-3-design.md: milestone 3's additions.
- docs/specs/2026-09-29-milestone-4-design.md: milestone 4's additions.
- docs/specs/2026-09-30-milestone-5a-design.md: milestone 5a's additions.
- docs/specs/2026-10-01-milestone-5b1-design.md: milestone 5b1's additions.
- docs/specs/2026-10-02-milestone-5b2-design.md: milestone 5b2's additions.
- docs/specs/2026-10-03-milestone-5b3-design.md: milestone 5b3's additions.
- docs/specs/2026-10-05-milestone-5b4-design.md: milestone 5b4's additions.
- CONTRIBUTING.md and AGENTS.md: how to work on the compiler.
License
Licensed under either of MIT or Apache-2.0, at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Trademark
The code is open source; the name and the logo are not. "Varyk" and the Varyk logo (the two-gear mark) are trademarks of Vladislav Mickevic, and the licenses above do not grant rights to them. You may use the name to refer to the project, but a modified version or a fork needs a different name and its own mark. See TRADEMARKS.md.