CLI tool
vlang/v avatar
vlang/v

V from vlang/v: a compiled language with a C backend, built from source in one command

Simple, fast, safe, compiled language for developing maintainable software. Compiles itself in <1s with zero library dependencies. Supports automatic C => V translation.

37,919 stars2,290 forksVMIT

At a glance

What is it?
V is a small, statically typed compiled language whose main backend emits readable C. This review covers how the vlang/v repository builds, how the compiler bootstraps itself, and where the language's pre-1.0 status shows through.
Who is it for?
Adopt V if you want a small compiled language with a readable C backend, fast self-compilation, and a build that starts from one git clone plus make. Do not adopt it if you need a frozen language specification or long-term API guarantees, because the README states that changes will happen before 1.0.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly V, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What V solves, and who the vlang/v repository is actually for

V targets a specific complaint: compiled languages that are fast at runtime but slow to build and large to install. The README lists the goals directly. Compilation runs at roughly 110k lines per second with a Clang backend and roughly 500k lines per second with the native and tcc backends, measured on an Intel i5-7500 with an SSD and no optimization. The project also states that V compiles itself in less than a second.

The second goal is dependency weight. V's main backend compiles to human-readable C, and the README describes zero library dependencies. That combination is what makes the cross-compilation story work: if the output is plain C, a C compiler you already have is most of the toolchain.

The intended audience is engineers writing low-level software. The README points at Vinix OS as an example, and the repository ships a built-in graphics library, a cross-platform UI library, a web framework in vlib/veb, and an ORM. The language is not positioned as a scripting replacement. It is positioned as a C alternative with a smaller surface area.

One claim in that list deserves suspicion. Safety is listed as "no null, no globals, no undefined behavior (wip)". The wip marker is the project's own, and it means the third item is a goal rather than a guarantee. Treat the safety bullet as a direction of travel, not a property you can rely on when auditing code.

How the V compiler bootstraps: vc/v.c, the Makefile, and the C backend

The build does not start from a V binary. It starts from C. The Makefile's first target, download_vc, clones https://github.com/vlang/vc into vc/ using a blobless filter, or pulls it if vc/v.c already exists. That directory holds the C source of a previous V compiler. The v target then compiles it with the system C compiler, and the resulting executable is the V compiler you use.

This is a self-hosting loop with a checked-in escape hatch. Because the bootstrap compiler is C, you can build V on a machine that has never had V installed, as long as it has git and a C compiler. The Makefile reads uname -s and uname -m to adjust link flags, adding -latomic on Linux ARM and -lexecinfo on FreeBSD, NetBSD and OpenBSD. That is the entire platform-detection layer.

Once the compiler exists, the main backend emits C and hands it to a C compiler. That is why the README can describe V as "as fast as C" while also describing the output as human-readable C. The native backend and the tcc backend skip some of that work, which is where the roughly 500k loc/s figure comes from. The JavaScript backend exists as a separate target.

Memory management is a compiler flag, not a language-level garbage collector you are stuck with. The README gives four modes: GC by default, manual via v -gc none, arena allocation via v -prealloc, and autofree via v -autofree. Choosing between them is a build decision you make per binary.

Installing V from source on Linux, macOS or Windows

The README calls installing from source the preferred method. On a Linux or macOS machine with git, the sequence is a clone, a change of directory, and make. On Ubuntu or Debian the README notes you may need sudo apt install git build-essential make first.

bash
git clone --depth=1 https://github.com/vlang/v
cd v
make

When make finishes, the V executable is at the root of the cloned repository, so [path to V repo]/v. The README's first smoke test is to run the bundled hello world example.

bash
./v run examples/hello_world.v

On Windows the README says to run makev.bat in CMD, or ./makev.bat in PowerShell, instead of make. On FreeBSD, OpenBSD, NetBSD, DragonFly and Solaris, install GNU make and invoke it as gmake. FreeBSD additionally needs boehm-gc-threaded and gmake packages; OpenBSD 7.8 needs boehm-gc and openssl-3.5. Termux on Android needs clang, libexecinfo, libgc, libgc-static, tcc, make and git, and after make the README suggests ./v symlink to put v on your path (without sudo on Termux).

There is a Docker path too. The repository ships a Dockerfile that installs gcc, clang, make, git and binutils, clones the repository into /opt/vlang, runs make, and symlinks the binary to /usr/local/bin/v, with the container command set to v.

bash
git clone --depth=1 https://github.com/vlang/v
cd v
docker build -t vlang .
docker run --rm -it vlang:latest

For a first real use beyond hello world, the README's static-binary recipe is the most informative one. It builds a self-contained Linux executable with no runtime dependencies, using the Alpine image and a set of compiler flags.

bash
with_alpine v -skip-unused -prod -cc gcc -cflags -static -compress examples/http_server.v

The README shows the expected output as a statically linked ELF executable, with examples/hello_world at roughly 16 KB and examples/http_server at roughly 335 KB. Those sizes are the project's own published figures, not measurements taken here.

Updating V and the pre-1.0 stability trade-off

Updating is a single command, and it does more than pull source.

bash
v up

The README states that v up refreshes the bundled TCC binaries used for fast C compilation in addition to updating V itself. That matters because the tcc backend is one of the two fast paths in the compilation-speed claim, and a stale TCC would quietly degrade it.

The stability section is where the project is most honest and most exposed. The README says V is "relatively stable, and doesn't change often", then immediately says there will be changes before 1.0. Syntax changes are handled through vfmt automatically. Core APIs, primarily the os module, will also shift until 1.0, after which the project promises growth without breaking existing code.

That is a real cost, and it lands unevenly. If you are writing a small tool that you rebuild from source, vfmt absorbing syntax churn is cheap. If you are publishing a library that other people compile against, every pre-1.0 API change in the os module is a breaking release you have to manage. The README's post-1.0 promise (feature freeze, bug fixes and performance improvements only, and no V 2.0 within a decade) is a clear statement of intent, but it is a promise about a version that does not exist yet.

The frequent-release cadence reinforces the point. The repository shows 0.5.2 released on 2026-07-12, 0.5.1 on 2026-03-09, and a weekly.2026.08 build on 2026-02-17. The last push to master was on 2026-07-12. That is not an abandoned project, but it is also not a frozen one, and the version numbers sit below 1.0.

Where V is the wrong choice, and how it differs from Zig, Rust, Go, Nim and Odin

The clearest limitation is the C-to-V translation feature. The README lists "C to V translation" as a key feature and links a demo video of translating DOOM. Translation is a starting point, not a maintenance strategy. The README does not document a round-trip guarantee, a supported subset of C, or what happens to translated code when V's syntax changes before 1.0. If your plan is to translate a large C codebase and keep it translated, the README gives you no basis for that plan.

The second limitation is ecosystem gravity. V's standard library lives in vlib/ and the README points at vlang.io and the docs file for everything else. There is no package-count claim to evaluate, and the project's own framing is that it is small and self-contained. If your work depends on a mature third-party library graph, that is a mismatch, not a bug.

The third is the safety wording. "No undefined behavior (wip)" means you cannot treat V as a memory-safe language in the way you would treat a language whose specification guarantees it. The memory model is selectable, and v -gc none or v -prealloc put allocation decisions back in your hands.

Against the alternatives people search for, the difference is architectural. Rust enforces memory safety through the borrow checker at compile time and has no C backend; V instead compiles to C and lets you choose GC, manual, arena or autofree. Go has a garbage collector and a large standard library, while V's README emphasizes zero library dependencies and a sub-second self-compile. Zig also targets C interop and readable output, but Zig's approach centers on comptime and explicit allocator passing rather than V's flag-selected memory modes. Nim compiles to C as well, with a different macro system and a longer history. Odin is a systems language in the same size class, without V's C-to-V translation feature. The honest framing is that V competes on build speed, output readability and small surface area, not on guarantees.

Licence, maintenance status and what upgrades cost

V is MIT licensed, and the LICENSE file sits at the repository root. MIT is permissive: it allows use, modification and redistribution with the licence text preserved, and it carries no copyleft obligation on your own code. The repository also ships a SECURITY.md and a CODE_OF_CONDUCT.md. This is a description of the licence, not legal advice; if you are shipping a product, have your own counsel read the LICENSE file rather than this paragraph.

On maintenance, the facts are narrow. The repository is not archived. The last push to master was on 2026-07-12, and the most recent release, 0.5.2, carries the same date. Since then there has been no push, so the accurate statement is that the last push was on 2026-07-12, not that the project is under active development.

The upgrade cost is unusually low for a compiler and unusually high for a language. Low because v up is one command and refreshes the bundled TCC binaries at the same time, and because vfmt is the project's stated mechanism for absorbing syntax changes. High because anything you build against the os module or other core APIs can move before 1.0, and because a language change can invalidate code that vfmt cannot mechanically rewrite. The Makefile's bootstrap step adds a second consideration: vc/v.c is pulled from a separate repository, so a build depends on both repositories being reachable, or on vc/v.c already being present locally. The Makefile handles the offline case by falling back to the existing file, but a fresh clone on a machine without git fails at that step with the message that git is required to download vc/.

Cross-compiling and static binaries with the Alpine image

The Alpine Docker path is the most concrete deployment story in the README, and it is worth reading as a design statement. The README defines a shell alias that runs the Alpine image as a non-root user with the current directory mounted at /src and set as the working directory.

bash
git clone --depth=1 https://github.com/vlang/v
cd v
docker build -t vlang_alpine - < Dockerfile.alpine
alias with_alpine='docker run -u 1000:1000 --rm -it -v .:/src -w /src vlang_alpine:latest'

The point of building inside musl-based Alpine is portability of the artifact, not of the build environment. The README's stated goal is static executables you can copy to a server running a different Linux distribution without dependencies. Two example invocations are given, one for an HTTP server and one for hello world with garbage collection disabled.

bash
with_alpine v -skip-unused -prod -cc gcc -cflags -static -compress examples/http_server.v
with_alpine v -skip-unused -prod -cc gcc -cflags -static -compress -gc none examples/hello_world.v

Each flag has a job. -skip-unused drops code the program does not reach, which is what keeps a hello world binary near 16 KB. -prod turns on production optimization. -cc gcc selects the C compiler. -cflags -static passes static linking through to it. -compress shrinks the binary. -gc none removes the garbage collector for the second example, which is only safe if the program does not need it. This is the clearest illustration of V's model: the language gives you the shape of the program, and the build flags decide what actually ships.

Editorial conclusion

Adopt V if you want a small compiled language with a readable C backend, fast self-compilation, and a build that starts from one git clone plus make. Do not adopt it if you need a frozen language specification or long-term API guarantees, because the README states that changes will happen before 1.0. Before committing, verify three things: that make succeeds on your OS with the packages the README names for it, that v up and vfmt handle any syntax change you hit, and that the memory model you pick (GC by default, or v -gc none, v -prealloc, v -autofree) fits the workload.

Frequently asked questions

How do I install V from the vlang/v repository?

The README calls installing from source the preferred method: clone https://github.com/vlang/v with --depth=1, change into the v directory, and run make (makev.bat on Windows, gmake on the BSDs and Solaris). The resulting executable is at the repository root as ./v. A Dockerfile is also provided for a container build.

How do I update V to the latest version?

Run v up. The README states that this updates V and also refreshes the bundled TCC binaries used for fast C compilation.

What memory management options does V support?

The README lists four modes: garbage collection by default, manual via v -gc none, arena allocation via v -prealloc, and autofree via v -autofree. The choice is made at build time with a compiler flag.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
For maintainers

Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vlang-v.svg)](https://hysenlabs.com/projects/vlang-v)
Community notes

Community notes