The story

I wanted a language I enjoyed using. It turned into one I trust with other people's data.

Ecko was not started to be a product. It was started because the tools I had were in my way, and because I wanted something that would run anywhere without a runtime to install first. The part I did not plan was where it ended up being used: financial streams, catalogue pipelines, background jobs that nobody watches run.

What I set out to make

What I wanted was not complicated. A language I enjoyed writing, that ran easily on many platforms, and that was less frustrating than PHP was at the time. That is the whole of the original ambition. Nothing about AI, nothing about pipelines, nothing about a runtime that does not fall over.

What was actually broken

The part that never got easier was deployment. A language that needs a runtime installed before it can run is a language that needs a conversation with somebody else's operations team before it can run, and on fifty client environments that conversation is the project. I wanted a single file I could copy onto a machine.

Why a language, and not a library

A library cannot change what happens when you get something wrong. That turned out to be the whole argument. The things I wanted are all properties of the thing underneath your code rather than of code you import: arithmetic that raises on overflow instead of quietly wrapping to a wrong answer, a call that will not start if the file it is in has a name that does not exist, every match over a type checked for the case you forgot.

The same holds for the parts added later. Capabilities are granted by whoever imports a package, and attenuate down the tree, so a package that declares it needs the network cannot quietly read your filesystem. Every resource has a ceiling - how deep input may nest, how long a value built from a count may get, how much a decompression may produce - so bad input is an error you can catch rather than a process that disappears. None of that is expressible as something you add to a program. It is either underneath the program or it is not there.

What it cost

Making the runtime fast took far longer than I expected. That is the honest headline of the middle years. What began as an interpreter walking a syntax tree became a bytecode compiler and a stack machine, and none of it was one clean rewrite - it was a bytecode instruction going from thirty-two bytes to sixteen, then eighteen percent less work per operation, then loops between twenty-seven and thirty-one percent faster. Each of those is one line in a changelog and several weeks that are not.

Two parts of that bill were not in the estimate. The first is that you do not get to delete the old engine. The tree-walking interpreter is still in there, kept alive as the thing the fast path is checked against, because the only convincing answer to did the rewrite change what programs mean is to run both and compare. Every change to shared behaviour gets made twice and has to agree twice.

The second is that speed quietly damaged things that were already working. Runtime errors started reporting the wrong line, because an operation had been made to carry its last child's position instead of its own - faster, and wrong in a way no benchmark can see. Underneath that, the machine holds raw pointers into the code it is executing, and the argument that this is safe is about lifetimes, which is exactly what ordinary tests cannot falsify: a dangling read returns plausible bytes and your test passes. That needed a separate tool and a separate harness to check at all. Performance work advertises the number it improved. It does not advertise the week you spend proving it did not break the error message.

Where it got to

Ecko is at 0.40.0. It is one binary of about 23 MB, built for Linux on x86-64 and arm64, macOS on Apple Silicon, and Windows on x86-64. Thirty-five standard library modules are inside it. So are the language server, the formatter, the test runner, the checker, the package manager and the documentation generator - not as separate downloads that version independently and drift, but as the same binary on the same release. There is nothing to install before it, and nothing to configure after it.

The browser playground is the real compiler, built to WebAssembly and running on your machine. There is no server behind it. The same is true of the AI features: with no provider configured, every ai call runs in a deterministic offline mock, which is why a test suite that uses AI can run in CI with no key, no network and no spend.

Underneath, the discipline is the boring kind. Integer arithmetic is checked, so an overflow is an error rather than a wrong answer that keeps going. Adversarial or buggy input is meant to degrade to an error you can catch rather than a process that dies, and every resource that could run away has a named ceiling. There is a test suite of a little over two and a half thousand cases, and a frozen corpus of programs whose output is compared byte for byte on every release so that upgrading does not quietly change what your code does.

What it is for now

The direction now is narrower than it was: Ecko should be a language that does not waste your time. That covers the parts people usually mean by developer experience - the error that shows you the line and the column and what you probably meant, the project that runs and tests and checks cleanly the minute it is created, the tool that does not need a second tool installed to be useful. It also covers the parts people do not usually file under that heading, like a failure that tells you what happened at record nine hundred million instead of disappearing.

That is an intention rather than a promise, and it is measured against a first hour, not a feature list. It is the reason the recent work has been unglamorous: a package manager that wrote a dependency name you could not actually import, a help listing that never mentioned the test runner, two commands that did the same job under different names. None of that is interesting to write about. All of it is what the first hour is made of.

It is one file. You can have it running in about a minute, and you can delete it just as fast.

No account, no key. Every ai call runs offline in mock mode until you configure a provider.