Skip to content

Vertex

A statically typed language for Salesforce. Compiles to Apex, runs locally too.

Here is a taste:

fn validate_age(age: Int): Result<Int, String> {
if age < 0 {
Error("age cannot be negative")
} else if age > 150 {
Error("age is unrealistically large")
} else {
Ok(age)
}
}
fn label(age: Int): String {
if age < 18 { "minor" } else { "adult" }
}
debug match validate_age(25) {
Ok(a) => "${label(a)}, age ${a}",
Error(e) => "invalid: ${e}",
}
// adult, age 25

Sum types, exhaustive pattern matching, string interpolation, and a Result the compiler makes you deal with. Paste it into a file and run it with vertex run, no org required.

If it compiles, it runs

Static types, exhaustive match, Option<T> in place of null, and Result<T, E> in place of exceptions. The compiler catches a lot of what ships as a production bug in Apex.

Features from this decade

Sum types, pattern matching, Option, Result, a pipe operator, type inference, immutability by default. The things Apex developers have wanted for a while.

One way to do things

No class inheritance, no static/instance split, no implicit null. Fewer ways for a program to surprise you.

Local dev loop

Write, run, and test without touching a Salesforce org. The JIT runs your code locally in milliseconds. When you’re ready, vertex build emits Apex classes you deploy the usual way.

Errors that actually help

Every diagnostic has a title, notes, a help: line, and spans on the relevant places. Readable by a human and structured enough for an LLM pair.

No more null pointers. Absence is Option<T> and you have to handle both cases to get at the value:

type User { User(name: String) }
fn greet(user: Option<User>): String {
user
.map(fn(u: User): String { "Hello, ${u.name}!" })
.unwrapOr("Hello, stranger!")
}
debug greet(Some(User(name: "Alice"))) // Hello, Alice!
debug greet(None) // Hello, stranger!

No more untyped throw. Fallible functions return Result<T, E>, and ? propagates the error when you want the happy path to read cleanly:

fn check_positive(n: Int): Result<Int, String> {
if n < 0 { Error("negative: ${n}") } else { Ok(n) }
}
fn process(raw: Int): Result<String, String> {
let n = check_positive(raw)?
Ok(if n < 18 { "minor" } else { "adult" })
}
debug process(25) // Ok(value: adult)
debug process(-1) // Error(error: negative: -1)

And no more waiting on a deploy every time you want to try something:

$ vertex run hello.vtx
Hello, Alice!

Vertex is a statically typed language that compiles to Salesforce Apex. It borrows from Gleam (Result, Option, the pipe), Kotlin (named parameters, records), Rust (?, explicit mutability), F# (pipeline thinking), and TypeScript (type inference feel), over a syntactic baseline that should look familiar if you write Apex.

It targets the Salesforce platform; it is not a general-purpose language. Existing Apex is meant to stay as Apex. Vertex is for new code and incremental adoption via a strong FFI.

Head to Getting Started to install and run your first program, or dig into the Language Tour. If you prefer reading a real codebase, there is a sample library management app at vertex-run/vertex-library-management.