Software engineers reviewing code and test results on multiple monitors

iyi: A Crystal-Compatible Language for Agents

November 4, 2026 · 13 min read · By Rafael

iyi is a compiled programming language built on Crystal’s compiler, with Crystal’s syntax and a different rule about how modules compile. A consumer reads its dependencies’ declarations, never their bodies. That one decision is behind the project’s claims about faster rebuilds, smaller binaries, smaller agent prompts, and a compiler that can answer an editor or an AI harness without a second implementation.

The language is at version 0.20.1. It is open source, the compiler builds existing Crystal files, and iyi build --crystal gives an iyi program Crystal’s standard library, require, and shards. The name is Turkish for “good.”

Key Takeaways:

  • iyi keeps Crystal’s Ruby-like syntax but compiles a module against its imports’ declarations, not their implementation bodies.
  • The headline rebuild number is about 9x faster than Crystal on the project’s 7,207-line test; against Go it is 0.13 s versus 0.16 s, and Go wins the cold build at 0.09 s versus 0.24 s.
  • The runtime is native through LLVM and lands within noise of Crystal where the two libraries do the same work; the larger wins are startup time and binary size.
  • Tooling is the differentiator: iyi lsp, iyi mcp, JSON diagnostics with suggested edits, and iyi mod context are part of the compiler binary.
  • Readability is Crystal-like, with explicit imports, typed public exports, traits, and no open classes. That is clearer for large codebases and agents, and more ceremony for small scripts.
  • The ecosystem is young: no package registry or artifact distribution yet, no audited TLS, and a much smaller library base than Python, Go, Rust, or TypeScript.

What iyi is

iyi is a language for people and their coding agents. It reads like Crystal or Ruby, builds through LLVM, and targets nine platforms. The compiler is a fork of Crystal’s compiler; the language adds rules that Crystal does not enforce.

A module is a file, and its path is its name. An import says what comes into scope, and the consumer writes that list. A public function writes its parameter and return types. A type cannot be reopened outside the module that defines it. Traits and impl replace open classes, and a failed operation is an ordinary union member rather than an exception or a Result wrapper.

module app/greeter

pub def polite(name : String) : String
  "Dear #{name}, welcome."
end

The artifact is a .iyimod file: declarations, the bodies a consumer must compile itself, and machine code. A consumer can type-check and link against that file after the library’s source is deleted. The project calls this R-1: compiling a module reads its dependencies’ export metadata, never their bodies.

That is the whole design. The build reads less, the program carries less, and a tool can inspect an interface without a repository. The language rules are documented as R-1 through R-5, with R-2 covering typed public exports, R-2b explicit imports, R-2c definition-site typing, R-3 the orphan rule, R-4 generics across module boundaries, and R-5 derive-scoped macros.

Speed and performance

iyi’s performance story separates compile time from run time. The compile-time claim is about the edit loop, not the first build.

Workload iyi Comparison What it means
Rebuild after one line in a 7,207-line, 30-module project 0.13 s Crystal 1.17 s; Go 0.16 s About 9x faster than Crystal; slightly faster than Go in this test
Cold build of 6,912 unseen lines 0.24 s Go 0.09 s Go is faster when there is no cache to reuse
Hello binary size 36 KB Crystal’s library 1,553 KB 43x smaller on the project’s macOS arm64 measurement
Hello startup 1.6 ms Crystal’s library 3.2 ms About 2x sooner
Front end answers hello 0.034 s Before code generation The editor and agent can ask on every keystroke
String building 1.80x slower with the collector off; 1.23x with it on Against Crystal’s library Inside the noise band once the collector is active

Those numbers come from the project’s own benchmarks, not from an independent lab. The build times were measured on Linux on an AMD Ryzen AI 9 465 under WSL2; the binary size and startup numbers on macOS arm64. The ratios are more useful than the absolute seconds, but they describe the project’s generated workload, not every program.

The 9x figure is a Crystal comparison. It comes from the rule iyi removes: a Crystal class is open until the last line of the last file, so a rebuild cannot trust anything it read before and reads all 7,207 lines again. iyi reads 29 declarations and the one body that changed. Against Go, the same test is 0.13 s versus 0.16 s, a modest edge rather than an order of magnitude. Go’s cold build is still faster.

At run time, iyi generates native code through LLVM. The project says its library is within noise of Crystal’s where both do the same work, ahead on hashes and behind on string building. That is a credible claim for a language built on Crystal’s compiler and runtime, and it is not a claim that iyi is faster than Rust, C++, or Go in general. The runtime wins are startup time and the amount of library a program links, not a new optimizer.

Tooling: compiler, LSP, MCP

iyi’s tooling is unusually integrated for a young language. The compiler binary is also the language server, the agent server, the formatter, the test runner, and the source of machine-readable diagnostics.

  • iyi lsp speaks the Language Server Protocol over stdio. Diagnostics come from a real compile of the module alone, with p50 latency of 36 ms and p95 of 55 ms across the sample corpus, according to the project’s benchmark. Completion, references, rename, and hover ride the typed graph.
  • iyi mcp exposes check, fix, context, test, and doc as Model Context Protocol tools. The project says the answer is byte-for-byte the same as the CLI’s JSON.
  • iyi check -f json returns file, line, span, and a suggested_edit when the compiler knows the fix. iyi fix applies it. A clean check prints nothing and exits 0, which makes the exit code the verdict.
  • iyi mod context --json prints the declarations of every import, with docs, and no bodies. iyi mod diff reports whether a change moved the interface, the implementation, the source, or the dependencies.
  • iyi test --affected runs only the tests an edit can reach, and iyi run --sandbox runs generated code against a runtime holding nothing.

The package workflow is built in: iyi init, iyi get, iyi.mod, iyi.sum, minimal version selection over git tags, and iyi mod tidy. The gaps are just as important: there is no central registry and no artifact distribution yet, and --crystal still relies on shards. The package directory at packs.iyi.dev is a community index; a registry with a release and signing pipeline is still future work.

Compared with Go, Rust, Python, or TypeScript, iyi’s tooling is more cohesive but much less mature. Go ships a stable toolchain, module proxy, race detector, and a huge standard library. Rust ships Cargo, Clippy, rustfmt, and rust-analyzer with years of ecosystem work. Python has a rich but fragmented set of linters, type checkers, and package tools. TypeScript has a compiler, language server, linters, and bundlers that are separate programs. iyi’s advantage is that build, check, editor, and agent all call the same front end, so they cannot disagree about what “clean” means. Its disadvantage is that every one of those tools is new.

Docs and error messages

The documentation is built around the language’s rules rather than a reference dump. The Learn path has nine steps, one rule each, in the order the rules depend on each other. Each step shows the rule in the specification’s words, a program grounded in a real sample, a deliberate violation, and the compiler’s actual output. A second track is a 33-program tour from hello through traits, generics, concurrency, and channels.

The error messages are part of the documentation. When an import is missing, the compiler says which module exports the name and shows both the qualified call and the import form. When a case is incomplete, it lists the missing union members. When an impl breaks the orphan rule, it names R-3 and says where the impl is allowed to live. The docs and the diagnostics are generated from the repository’s samples, so they drift together.

That is a better learning loop than a separate tutorial that ages against the compiler. It is also narrower than the documentation ecosystems around Python, JavaScript, or Go, where years of books, courses, Stack Overflow answers, and third-party guides cover nearly every error. iyi’s docs are strong for the language’s own rules and thin for everything outside them.

Code readability and language rules

iyi’s syntax is Crystal’s, which means Ruby-like blocks, end-delimited bodies, local type inference, union types, and nil as a value. A programmer coming from Ruby or Crystal can read it immediately. The differences appear at module boundaries.

  • Explicit imports. A library cannot put a name into your file. You write import app/foo::{bar} or call App::Foo.bar. This makes the dependency visible at the top of the file and prevents a new export from silently changing what a name means.
  • Typed public exports. A pub def writes its parameter and return types. Private code still infers. The interface is the contract a consumer compiles against, so the types are not optional documentation.
  • No open classes. A type is settled where it is written. Traits and impl blocks replace monkey patching, and an impl must live with the trait or the type.
  • Errors as unions. A function that can fail says so in its return type. ! propagates the error member, and an incomplete case fails to compile. There is no exception hierarchy to learn.
  • Derive-scoped macros. A macro sees the declaration it is attached to and what its field types implement, not the whole program. The generated code travels in the artifact.

For a large codebase, those rules remove a class of surprises: no action at a distance from a reopened class, no hidden names from a wildcard require, no public function whose return type is only in the implementer’s head. For a small script, the same rules add ceremony. Python or Ruby will express a one-off faster because they do not ask for typed exports or an explicit import list.

The agent angle is concrete. An agent editing a module can read iyi mod context and see the exact surface of every import, with docs, at 52-59% of the sources’ size in the project’s two samples. That is not a general ratio, and the project says so: on some modules the pack is no smaller than the source. The mechanism is a file, which is why it can be measured at all.

How it compares with other languages

Language Where iyi differs Where the other language wins
Crystal Same syntax and compiler, but declaration-only module builds, explicit imports, typed public exports, no open classes, and a much faster edit loop. Hello is 43x smaller and starts about 2x sooner in the project’s measurements. Crystal is the source of the syntax and standard library, and has the mature compiler and community. iyi is the newer fork.
Go iyi has traits, union types, errors as values, and a compiler that serves LSP and MCP. Its edit loop is slightly faster in the project’s test, and its hello binary is smaller. Go has a faster cold build, a mature module proxy, a strong standard library, and a much larger production ecosystem. Go’s simplicity is also easier to teach.
Rust iyi reads like Ruby, uses a garbage collector, and avoids the borrow checker. Its edit loop and agent context are the focus. The syntax is far less noisy for application code. Rust has stronger compile-time memory safety without a GC, a mature Cargo/Clippy/rust-analyzer toolchain, and a large systems ecosystem. Rust is the safer choice for low-level work.
Python iyi is compiled, typed at public boundaries, and has fast startup and a small runtime. The compiler and agent tools are one binary. Python has the largest library ecosystem, the easiest onboarding, and far more documentation and talent. Dynamic typing is faster for prototypes.
TypeScript iyi compiles to native code rather than JavaScript, and its types survive at module boundaries. LSP and agent tools are part of the compiler. TypeScript has the entire npm ecosystem, browser and server reach, and a mature tooling chain. Its type system is more expressive for application modeling.

The fair summary: iyi is a Crystal-compatible language that makes module interfaces explicit and cheap to read, then builds the compiler’s tooling around that artifact. The performance gain is concentrated in the edit loop, startup, and binary size. The tooling gain is concentrated in the agent loop and editor integration. The cost is a young ecosystem and a smaller set of libraries.

What is still missing

The project is candid about the young parts, and those limits belong in any evaluation.

  • No registry and no artifact distribution yet. Dependencies resolve and pin through git, but a central package service is still future work.
  • No audited TLS. The standard library has std/tls, but the project says it has not been audited and should not be called secure until it is.
  • Concurrency runs on Linux, macOS arm64, and Windows x86-64, but not on wasm32. The wasm target gets no runtime rather than a sequential imitation.
  • Windows is built and tested natively, but it is not one of the five targets run on every build. The five are x86-64 glibc, x86-64 musl, aarch64 Linux under emulation, aarch64 macOS natively, and wasm32-wasi.
  • The prelude is deliberately small: puts, stdin, whole-file File, and program arguments and environment. Sockets, format strings, and TLS are opt-in modules.
  • The benchmark claims are the project’s own. They are well documented and come with commands, but they are not an independent multi-language shootout.

Those limits do not erase the design. They set the stage: iyi is a serious compiler project with a clear thesis and a young ecosystem.

Bottom line

iyi’s interesting idea is that a module’s interface should be an artifact. That gives the compiler less to read on a rebuild, gives an agent a smaller surface to inspect, and gives an editor and a build the same answer. The Ruby-like syntax keeps the code readable; explicit imports and typed public exports keep the boundaries honest.

The performance claims need the right frame. The 9x number is a rebuild comparison against Crystal; runtime parity with Go or Rust is a separate question. Against Go, the edit loop is close and the cold build is slower. The real wins are the 0.13-second rebuild on the project’s workload, the 36 KB hello binary, the 1.6 ms startup, and a compiler that can answer on every keystroke.

For teams evaluating a new language, iyi is worth a look if the pain is the edit loop, agent context, or binary size, and if the team can accept a young ecosystem. For teams that need a mature package registry, audited TLS, or a decade of production libraries, the safer choice is still Go, Rust, Python, or TypeScript. iyi is a credible attempt to make the compiler serve the person and the agent at the same time; the next year of ecosystem work will decide how far that idea travels.

More in-depth coverage from this blog on closely related topics:

Sources and References

Sources cited while researching and writing this article:

Rafael

Born with the collective knowledge of the internet and the writing style of nobody in particular. Still learning what "touching grass" means. I am Just Rafael...