Today, when compared to any other point in programming history, we find ourselves spoiled for choice when it comes to programming languages. Advancements in language design, toolkits like LLVM, the democratization of programming via the internet, and a load of other factors have led to a Golden Area of new languages. Each week we see the popularization of a new language like Zig, Deno, Nim, Rust, Flutter, Kotlin, Go, Crystal, Carbon, and many, many others. And yet, when surveying the landscape of what languages are actually used, the list is almost always populated with the Usual Suspects: JavaScript, TypeScript, Python, Java, Ruby, PHP, etc. The reason for this, I think, is that while new languages are always popping up, some with novel design decisions or built-in features, very few of them are "serious" in the sense that they're designed for serious, production-level workloads and architectures. I don't want to call them Toy Languages, but I also don't think we'll be seeing any serious production services running on Deno or Crystal any time soon.

"But what's wrong with the Usual Suspects?" you might ask.

And that would be a very good question. Honestly, I don't think the argument is about what is wrong with them less than it is more about how they fit into today's architectures and ecosystems and the kinds of work they're being asked to take on.

JavaScript, TypeScript, NodeJS, CoffeeScript, etc.

JavaScript is the Three Raccoons in a Trenchcoat Pretending to be Human of programming languages. It was never designed to do nearly most of what its doing today and everything that's been added to it since its inception in 1995 feels like an afterthought. There's usually three to eight different ways to do anything in JS and all of them have unique side effects and footguns that you don't know unless you're a dyed-in-the-wool JS developer. This kabbalah of ancient Deep Magic makes writing effective and performant JavaScript incredibly hard for those not baptized in the waters of the V8 engine. While the idea of using the same language for frontend and backend work sounds like a good idea, in practice the only thing front and back-end have in common is basic syntax and a love for unnecessary 3rd-party NPM packages.

NodeJS, TypeScript, CoffeeScript, and any other JS-derived language or variation falls under this umbrella, too, as they're just adding another raccoon.

And, if you don't believe me about JavaScript being a hot mess, just go watch this brilliant lightning talk: WAT

Python

I really like Python. Its easy to get in to, the syntax is clear and concise, and its flexible enough to do most things reasonably well. With the addition of type annotations and match/case in recent versions, Python has made some really great quality-of-life improvements as of late. Unfortunately, these features can't really save Python from its main downside: its slow. I mean really slow when compared to other languages. Being interpreted doesn't really help in this regard, but the core of the problem lies in the GIL, or Global Interpreter Lock. The GIL ensures that Python code only runs on a single core of a CPU for the purposes of thread safety but at the detriment of speed and flexibility in parallel computing and concurrency. What's the use in having a 128-core, 512GB MacBook Pro M9001 when the code you're writing only takes advantage of a very small sliver of that computing power? Limiting execution to a single core also means that any sort of parallelization short of multiprocessing (async, threading) is also bound to a single core.

In practice, in production, Python APIs are rarely deployed by themselves (or at least shouldn't be deployed by themselves), as they usually need some sort of WSGI wrapper around them. Flask, Sanic, FastAPI, etc. are all bound by the same constraints that Python apps are, so to reliably and somewhat performantly serve web traffic they need to be run inside some sort of parallel-processing framework. This only complicates deployments and debugging more.

We also need to talk about distributing Python apps as well: it's not great. pip, Python's package manager, defaults to installing dependencies globally, meaning you cannot have two versions of the same library installed at the same time. This obviously causes issues when trying to use multiple Python apps on the same machine. And, when it comes to distribution, things get even more messy. Sure, there's tools like virtualenv and poetry that make dependency isolation easier, but relying on those for distributing your Python-based tools to end-users is a fool's errand.

Syntax-wise, Python is clean and easy to read โ€” most of the time. Sure, significant whitespace is horrible and should never be a thing, but mostly everything else makes sense.

Ruby

In my opinion Ruby is awful. It has its very dedicated and vocal fanbase, sure, but the language is simply bad and needs to be taken out to the woodchipper.

Its not fast.

Its not easy to read.

And most of the time its used alongside a framework so horrible and terrible it should be thrown into the abyss, never to be seen or spoken of again: Rails.

Ruby on Rails applications are toys. They're not meant for serious workloads, serious applications, or serious anything. There's a reason all the big Ruby shops have since transitioned into other languages, Shopify being the big one.

Additionally, the Ruby ecosystem and culture around it encourage bad behavior and poor quality code. The number of times I've seen applications that needed someone to drop into the Ruby console to modify an application's state in real time just so an app can be deployed or a database can be migrated is way, way too many, and anyone who encourages that behavior should be shunned.

In short, Ruby is ๐Ÿ’ฉ.

PHP

PHP, for all the things that it does right, is just a giant mess of security holes and performance bottlenecks. The cool thing about PHP is that it can run exclusively in the context of an HTTP request, and that's about where the cool things end.

PHP's syntax is funky to say the least. String concatenation with a . ? Gross.

2005 called, it wants it's web apps back.

Rust

Rust is probably the "most serious" language on this list, in that its considered a low-level systems language (comparatively, we're not talking Assembler low-level here, more like C low-level). And unlike the other languages on this list, it has some serious traction to become instrumental to a number of key systems, like the Linux kernel and future Windows versions.

Rust also has a very devoted group of disciples, and with good reason. Its fast, its memory safe, its easier to write than C or C++ but keeps all the performance and flexibility of those languages. The Cargo package manager has a thriving ecosystem. All good things.

But there are some things that Rust just doesn't do very well, and the big one is concurrency. Writing applications with concurrency in Rust is an exercise in masochism (from what I understand) and concurrency as a feature was really more of an afterthought than anything. Maybe in the future it will get better, but right now, in current state, its bad.

In addition to concurrency, there seems to be a debate in the Rust community right now about how macros should be used, if at all. Until the dust settles on the Great Macro Debate I would stay away.

Also, nitpicking, but Rust syntax uses two of my least favorite conventions: double-colons :: and semi-colons ;.

Go

Go is the answer to the question "what happens if I design a language but ignore the last 20 years of software development?"

I've written about Go before, especially how its package management was overlooked and half-baked on launch, and has only somewhat recently been more-or-less ironed out. They absolutely missed big by releasing Go 1.0 without proper package management features.

While Go really nailed some features โ€” concurrency in Go is easy and user-friendly with goroutines โ€” others seem to be not well thought out, lagging behind the industry, or simply weirdly designed:

  • Struct tags are basically just magic comments that change how the program acts.
  • Error handling is repetitive and annoying.
  • Go chooses convention over clarity: method and parameter exposure is controlled by capitalization instead of keyword. Methods or attributes that start with an upper-case letter are exported, where lowercased ones are not (myMethod is private, MyMethod is public).
  • Go also suffers from a sort of split-brain thing where some packages and capabilities require CGO and some are pure Go โ€” this can make distributing Go apps to multiple platforms a painful.

But for me, the one feature that Go needs if it wants to be taken seriously as a production-ready language, and one that is closer to a systems language than a scripting language, is the ability to dynamically import symbols from shared libraries. Go applications are always single binaries that contain all of the code needed to run that program. The application may read external configuration or files or whatnot, but no code is read from external files. This makes a lot of sense if you're writing self-contained applications like web apps, but anything that requires extensibility suddenly requires you perform some insane workarounds. The most common workaround is for the main process to spawn a subprocess running a whole other application and for the two to communicate via RPC. A great example of this is Terraform: Terraform providers are simply self-contained Go applications that communicate with the Terraform main process via RPC. Any other language would define a common interface for each provider and simply load the class or struct into the main application, not spawn entire processes and use an IPC method that requires the networking stack!

While Go has slowly added new features that should have been in place from the start โ€” like generics and convenience functions like slices.Contains ()โ€” the governance team of Go is so reluctant to change that I have no faith in their long-term steering of the language.

Java

Java, like JavaScript, is an old language that's been bolted to, modified, transformed, and refactored so much that its unrecognizable from its original form. Originally designed as the backbone of an in-home entertainment system, Java has morphed into the standard enterprise language for Fortune 500 companies and the 3 people who still develop applets, for some reason.

To me, the one thing that Java has going for it is the Virtual Machine (JVM) โ€” a translation layer between Java bytecode and machine code that allows the same code to be run on pretty much any hardware or platform you can imagine. Because the JVM handles all the translation, the code you write for an ARM-based embedded device will run on your Intel PC or Apple Silicon Mac, making the need for expensive testing setups or complicated virtualization unnecessary. However, the one drawback with this approach is speed: Java apps tend to not be "fast" without a lot of hand-holding, JIT compiler tuning, and forethought. Frameworks like Spring only add to this overhead by adding a lot of metaprogramming and code generation to the mix.

While Java doesn't have a lot of things that are necessarily "bad" (except its usage of semicolons), it doesn't have a lot that are "good" either, making it the Nickelback of programming languages. I think we'd all be better off if we just scrapped it and started over with something new designed to replace it.

A New Language

So now that I've [hopefully] made my case as to why the usual languages all kind-of suck, we can get into the fun part where we talk about what this hypothetical new language would look like.

To start, I think we should define some key tenets or features that make our language stand out from the rest:

  • Easy to read and learn โ€” like Python, it should be easy to get started and set up and slowly wade into the deep end.
  • Available everywhere โ€” it needs to be able to run everywhere, not just a few specific servers or processors.
  • Concurrency easy to do

So what would this look like in practice?

To begin, I think we should adopt the idea of a JVM from Java, but change it up in one important way: the JVM and runtime environment would be bundled with the application. Yes, this would require you to specify your target architecture at compile time, but it would have the benefit of allowing compile-time optimizations and removal of unnecessary code via static linking (similar to Java 9+'s custom JRE bundling), giving us a size and performance advantage over Java.

Syntax-wise, I think in general we should stay with the C-style syntax that Java, Go, JS, and others have adopted, just no semicolons. I know some of you are die-hard semicolon people, but typing a ; on every line is annoying and unnecessary.

From Python I want to adopt the idea of convenience functions and a very robust standard library. Python is all about convenience and niceties, and I think having those nice little methods and functions available is overlooked in a lot of languages, especially the more "serious" ones.

From Go, definitely its concurrency model. Coroutines, waitgroups, and channels make dispatching and managing concurrent work easy and effective. I would like to have a little more control over coroutines โ€” once they're started you can't force-stop them, so you have to either write perfect code or implement some type of failsafe in the routine itself โ€” maybe a way to see which ones were currently running and force-stop them if necessary.

Also important is the ability to take advantage of multi-core systems โ€” some languages do this better than others, but being able to take advantage of all compute and memory available to the application is critical to the long-term success of our new language.

Unlike Go, I would like to be explicit about things rather than rely on convention to convey meaning. This means using keywords like public or export on methods, variables, attributes, etc.

Package management is important, and if Rust has taught us anything, its that having a robust package management ecosystem is important. Our language will launch Day 1 with its own package manager and package repository. Unlike Go and some other newer langs like Deno, I do not want to rely directly on GitHub or other VCS's for packages.

Also like Rust, its important for the compiler to give detailed and reasonable feedback and error messages. Nothing is more frustrating than encountering an issue in a Go program only to be given a cryptic and not-helpful error message.

Like JavaScript, I want our language to be able to be run both client and server-side. That means having one of our compile targets be WebAssembly (or V8!) and native support for writing programs that target browsers.

While the compiled application will be statically linked, we should still support dynamic linking and symbol imports. This will allow for extensibility and flexibility in ways that Go cannot.

Finally, and possibly most importantly, I want to adopt the culture and steering from Python. Python is very much a community oriented language where everything from code style to new features are detailed as Python Enhancement Proposals (PEP's), publicly voted on, and referred to a steering committee for final adoption or veto. This has allowed Python to not only standardize things like style (PEP-8) but also add new features and stay current in ways that Python's users want, but within reason.

If you're interested in helping build this new language, leave me a comment and let me know. Also, comment any features I missed or that you'd like to see!

Like what I do and want to help in some way? Buy me a coffee!

โ˜• https://ko-fi.com/peterfraedrich