To “run the gauntlet” is to be forced to run between two rows of soldiers armed with pikes who are allowed to strike out and attack you.
As a developer, you run the gauntlet every day.
At the risk of a little hyperbole, every time you write a call to access a feature of an operating system, external application or service, a new pike-wielding soldier appears on your gauntlet.
If that call is to a system whose stability and unwavering interface is proven, its soldier on your gauntlet is essentially disarmed. But when that call is to a new system, or an application with a periodically adjusted, ill-defined or poorly understood interface, their soldiers become armed and your gauntlet becomes perilous.
Such are the deadly costs of run-time abstractions.
Once upon a time, when we told the hardware directly what to do there was no operating system to call upon to grant access to higher level features. In this simpler time, our gauntlet was completely devoid of soldiers (and certainly devoid of pikes!). We had a clear path.
Then, out of necessity, we introduced the first run-time abstraction; the operating system, an independent ‘application’ that acts as gatekeeper of hardware and software resources. At this point, instead of directly instructing the hardware, we asked the OS gatekeeper to do things on our behalf. It was at this point that our gauntlet became… a gauntlet. Over time, as the gatekeepers and their interfaces became more reliable, its soldiers were disarmed. They’re relatively harmless now.
Today, developers who express their program requirements in languages with no run-time environment such as C can mosey on down a completely disarmed gauntlet. That is, until the time comes that they need to expose their code to higher level languages.
Take one quote from a developer:
“Almost any systems code I write these days is something I’ll eventually want to expose to a high-level language. […] Having lots of rich concepts and semantics in your systems language just gets in the way, because you have to find out how to map between the two.”
But this mapping (through language binding, FFI’s or ABI’s etc.) often then introduces run-time dependencies in addition to the OS, which only means one thing; more soldiers and more pikes. (Not to mention the unnecessary ‘glue code’ required to map between the two languages, code that doesn’t contribute any functionality toward meeting the application requirements!)
And then what about the more ‘complex’ languages, ones with extensive run-times? Still more soldiers. And whether or not they’re armed depends on how well the language is maintained, and how stable its runtime environment is. Garbage collection, for instance, certainly comes with its own set of soldiers (usually armed).
What about frameworks? Same thing. “Don’t pike us, we’ll pike you!”
And micro-service architectures? Same thing, but now you’re at risk of being piked by “friendlies”!
Take the following Reddit exchange that took place mere days ago in a thread titled “Everything should be [a] microservice”:
Dev 1: “We just had a discussion in the team, and we decided, that we need “add-one” microservice that would get a number and return the number increased by one. A nice separation of concerns in modern distributed web application :)”
Dev 2: “That’s an awesome idea. Finally someone spent some time thinking about the massive overhead of computing this locally compared to simply creating an http request, waiting for the answer, processing the answer and parsing the value. This as a microservice will be so much more efficient than adding 1 locally.”
(The worrying part about the above exchange is that some redditors couldn’t quite tell if these comments were genuine or not.)
We seem to be abstracting further and further into run-time territory.
But what if, instead of pushing our abstractions into run-time, we could pull them right back into ‘design’-time?
That is just what the World Compiler achieves.
It allows developers to contribute to and create immensely complex applications with just one run-time dependency; the OS itself.
To join and compete within this World Compiler, a developer builds an Agent, an autonomous software program that captures and maps (n+1)-level requirements to n-level requirements, giving these requirements to a completely new set of Agents within the network who will carry out their own compile-time mapping.
Whenever a new build is kicked off, hundreds upon thousands of Agents within the World Compiler come together to collectively program, compile and return an application, like one great big decentralised compiler spanning all layers of abstraction, from the gathering of application-specific requirements right down to opcode generation.
With such a system, it now becomes practical to pull garbage collection out of run-time and design it as part of the application itself… to pull frameworks out of run-time and design them as part of the application itself.
We’ve been boxed into in an industry where a single compiler, fragmented languages and run-time abstractions are used to achieve an end-goal of automation. Out of necessity (and at the expense of run-time performance), we have had to push a lot of overhead right into run-time.
But we’ve had it the wrong way around!
We should be pushing the overhead into compile-time, and using it to our advantage.
Rather than ‘talking’ at run-time to perform an automated service, let’s ‘talk’ at compile-time to produce an automated product.
Let’s create a new industry where the languages and compilers and tools and paradigms are all decentralised yet compatible and where every single niche is opened up to competition.
Let’s say goodbye to the gauntlet.
Take the World Compiler for a spin here. Request an invite to become part of it here.
To learn more about Code Valley, please visit https://codevalley.com, or follow the project on twitter.