DISCLOSURE: If you buy through affiliate links, I may earn a small commission. (disclosures)
I've spent years searching for a programming language that has it all: expressive types, a large ecosystem, good performance, and good devx.
Every language I've tried makes me choose. So here I wanted to share my view of the current language landscape, where they fall short, and what I think they can do to have it all and become S-Tier.
Expressive types - Sum types, exhaustive matching, native immutability, sound types that don't lie at runtime
Big ecosystem - Lots of packages, active maintainers, docs for everything, general-purpose across platforms
Large community - Jobs available, Stack Overflow answers, AI knows it well (after all, AI is here to stay)
Good performance - Fast enough for most use cases
Good DevX - No manual memory management (unless you want it), reasonable learning curve, and productivity is on par with other high level langs when you get up to speed
I don't think this is unreasonable. Each of these features exist, just not in the same package.
My Definition of Expressive Types
I get asked about this a lot and I think everyone has their own definition so I want to be clear about what I mean. When I say expressive types, I mean:
Sum types / discriminated unions - Can model "this is A or B" as a distinct type, not just interfaces or inheritance. Examples include: Rust's enum, F#'s discriminated unions, TypeScript's union types.
Exhaustive pattern matching - The compiler ensures you handle all cases. Add a new variant? Compiler errors everywhere you forgot to handle it so it's easy to go back and fix those.
Native immutability - Immutable bindings for runtime values (like Rust's let or F#'s let), not just const for compile-time constants (like C#). Immutable by default is even better. Ideally the immutability is structural - the data itself is immutable, not just preventing reassignment of the binding.
Sound types - Types don't lie at runtime. A variable of type int will contain an int - or an error is thrown. TypeScript and Python fail here - you can declare x: int and silently have it be a string at runtime. This includes null safety - if a type is non-nullable, it shouldn't be null / undefined at runtime. Languages without null safety (Java, Go, older C#) let you pass null anywhere, causing NullReferenceExceptions/NPEs at runtime instead of compile-time errors.
Option/Result capable - Easy to build or use these patterns. Doesn't have to be built-in, but the language shouldn't fight you.
Most languages have 1-2 of these. Few have all 5. But IMO these are powerful tools for modeling the domain and if you can model the domain well you can make invalid types unrepresentable and if you can do that you end with a system that is MUCH more robust to many kinds of common errors. If you can't do that then you have implicit rules engineers need to keep in mind long term and it's an error waiting to happen when someone eventually misses that implicit.
Note: Ergonomics matter too - the easier types are to read and write, the more developers will use them to model simple, correct domains. But since this is highly subjective I'm not including it in the formal criteria. But as a general rule, less boilerplate is good.
The Programming Language Landscape
Here's how I'd score popular languages across these dimensions:
Language
Expressive Types
Big Ecosystem
Large Community
Performance
DevX
Score
C#
Mixed
Yes
Yes
Yes
Yes
4.5
Kotlin
Yes
Yes
Mixed
Yes
Yes
4.5
TypeScript
Mixed
Yes
Yes
Mixed
Yes
4.0
Go
No
Yes
Yes
Yes
Yes
4.0
Rust
Yes
Yes
Yes
Yes
No
4.0
Swift
Yes
Mixed
Mixed
Yes
Yes
4.0
Java
Mixed
Yes
Yes
Yes
Mixed
4.0
F#
Yes
No
No
Yes
Yes
3.0
PHP
No
Yes
Yes
No
Yes
3.0
Python
No
Yes
Yes
No
Yes
3.0
Ruby
No
Yes
Yes
No
Yes
3.0
C/C++
No
Yes
Yes
Yes
No
3.0
Gleam
Yes
No
No
Mixed
Yes
2.5
Elixir
No
Mixed
Mixed
Mixed
Yes
2.5
Zig
Mixed
No
No
Yes
Mixed
2.0
Scoring: Yes=1, Mixed=0.5, No=0. Max possible = 5.0 (S-Tier). No current language achieves it.
Some patterns:
Languages with expressive types (Rust, F#, Gleam, Swift, Kotlin) tend to have smaller ecosystems or other tradeoffs
Languages with big ecosystems (Go, Python) tend to lack expressive types entirely, while others (C#, Java, TypeScript) have partial support
Rust comes closest on paper but fails on DevX (borrow checker learning curve)
C# and Kotlin score highest but each has a critical gap
Here's my reasoning for each language:
C# - Almost There (4.5)
Expressive Types: Mixed - Discriminated unions "coming" for years. Mads Torgersen (C# designer) outlined plans for C# 15 (Nov 2026), though he explicitly noted it's not a committed timeline (NDepend coverage). Has immutable records, pattern matching, but no local immutability (var is mutable, no let/val). Nullable reference types provide partial null safety since C# 8, but it's opt-in, produces warnings not errors, and nulls can still sneak in via interop, reflection, array defaults, and the null-forgiving operator (!). Types are otherwise sound.
Big Ecosystem: Yes - Huge NuGet ecosystem, decades of enterprise packages, great tooling, works everywhere (web, CLI, games, data).
Large Community: Yes - Top 5 language, tons of jobs, excellent AI support, Stack Overflow answers for everything.
Performance: Yes - .NET is fast, keeps getting faster with almost every release.
DevX: Yes - No manual memory management, productive quickly, familiar syntax. Though there is a decent amount of boilerplate.
Closest to S-tier for me. If unions land well in C# 15, this could be the answer. But I have reservations about whether it actually will be in this release and how the ergonomics play out in terms of boilerplate and interop with the rest of the language / ecosystem.
Expressive Types: Yes - Sealed classes are proper sum types, null safety built-in, val for local immutability, exhaustive when, types are sound.
Big Ecosystem: Yes - Full JVM ecosystem access, plus Kotlin-native libraries. Multiplatform story improving.
Large Community: Mixed - Strong in Android, growing elsewhere, but not as universal as Java/Python/TS. Job market skews mobile.
Performance: Yes - JVM performance is solid.
DevX: Yes - Much better than Java, concise syntax, no memory management.
If you're okay with JVM, this is a strong choice. The Android-centric community is the main gap.
TypeScript - Great Types That Lie (4.0)
Expressive Types: Mixed - Great union syntax and exhaustive matching possible, but types lie at runtime (unsound) requiring you to add your own runtime checks. const exists for immutable bindings but objects remain mutable - no native deep immutability. Has strictNullChecks for null safety but since types are erased at runtime, you can still get unexpected nulls / undefineds. And of course you can always just fudge the types with any or X as Ys.
Big Ecosystem: Yes - Entire npm ecosystem, largest package registry on earth. Works web, server, CLI, mobile.
Large Community: Yes - Top 3 language, massive job market, AI is excellent at TS.
Performance: Mixed - Fast enough for most web workloads, but won't win benchmarks against compiled languages.
DevX: Yes - Easy to learn, productive quickly, no memory management.
Most pragmatic choice. Types don't lie often enough to matter for most projects. Web-native is a huge plus.
Expressive Types: No - No sum types (proposal #19412 open since March 2017, one of the most requested features). No local immutability - everything mutable by default. No null safety - any pointer/interface can be nil.
Big Ecosystem: Yes - Great standard library, solid package ecosystem, excellent for CLI/infra/backend.
Large Community: Yes - Top 10 language, lots of jobs, good AI support.
Performance: Yes - Fast compilation, fast runtime, low memory footprint.
DevX: Yes - Simple syntax, no memory management, productive quickly.
If Go had expressive types, it would be THE language. The deliberate design against it is one of the primary factors I rank it in C Tier.
Rust - The Learning Curve (4.0)
Expressive Types: Yes - Excellent enums with associated data, exhaustive matching, Option/Result built-in, immutable by default. Best-in-class type system.
Big Ecosystem: Yes - Cargo/crates.io has grown substantially, good packages for most needs.
DevX: No - Borrow checker is a "notorious learning barrier" per academic research ("The Usability of Ownership") and anecdata from 1ks of forum posts. Async ecosystem is fragmented though does seem to be coalescing around tokio. Fighting the compiler is real.
On paper, Rust is closest. In practice, the DevX cost is real. I'm currently trying "high-level Rust" with Arc/Clone to avoid the edges during my time at Recurse Center but we'll see how this works in practice.
Swift - Apple's Walled Garden (4.0)
Expressive Types: Yes - Enums with associated values, exhaustive switch, optionals built-in, let for local immutability, types are sound.
Big Ecosystem: Mixed - Excellent for Apple platforms, limited elsewhere. Server-side Swift exists but niche.
Large Community: Mixed - Strong in iOS/macOS, weak outside Apple ecosystem. Jobs are Apple-focused.
Performance: Yes - Fast, compiled, good memory characteristics.
DevX: Yes - ARC handles memory, productive syntax, good tooling (on Mac).
Great language trapped in Apple's ecosystem. If you're building for Apple, it's excellent. Otherwise, limited.
Java - The Boilerplate King (4.0)
Expressive Types: Mixed - Has sum types via sealed classes with exhaustive pattern matching, but very verbose and clunky compared to native discriminated unions. Local immutability requires verbose final keyword. No null safety - any reference can be null, leading to NPEs (though there's an open proposal to change this). Types are otherwise sound.
Big Ecosystem: Yes - Massive enterprise ecosystem, Maven Central is huge, decades of libraries.
Large Community: Yes - Still a top 5 language, enormous job market, AI knows it well.
Performance: Yes - JVM is fast, especially with modern GCs.
DevX: Mixed - No memory management, but verbose syntax, slow to evolve, boilerplate everywhere.
Enterprise workhorse but showing its age. Kotlin exists for a reason.
PHP - Surprisingly Decent Now (3.0)
Expressive Types: No - Has types now but not expressive. No sum types, no exhaustive matching.
Big Ecosystem: Yes - Massive web ecosystem, Composer/Packagist, WordPress alone is huge.
Large Community: Yes - Top 10 language, lots of jobs (especially web/WordPress), AI knows it well.
Performance: No - PHP 8+ has improved significantly with JIT, but still slow. Laravel benchmarks at 16,800 RPS vs Django's 32,651 RPS on TechEmpower - slower than Python in web workloads.
DevX: Yes - No memory management, easy to deploy, productive for web.
PHP has improved dramatically but the type system hasn't kept up. Fine for web and a very productive language but I think there are better options out there.
F# - Beautiful but Lonely (3.0)
Expressive Types: Yes - Discriminated unions, exhaustive matching, Option/Result built-in, immutable by default, types are sound. One of the best type systems.
Big Ecosystem: No - Tiny, many abandoned packages. Can use C# libs but nulls still leak in, wrappers needed.
Large Community: No - 42nd most popular language with 0.9% usage per Stack Overflow 2024 survey. Few jobs, limited AI support, sparse Stack Overflow.
Performance: Yes - .NET is fast.
DevX: Yes - No memory management, functional-first is productive once learned.
My favorite language that I can't fully commit to. It's fun but the ecosystem gap is painful.
Expressive Types: No - Type hints exist but they lie - not enforced at runtime (unsound). Union types exist (X | Y) but no exhaustive matching, no native immutability. You can declare x: int and assign a string.
Big Ecosystem: Yes - Massive, especially for data/ML/AI.
Large Community: Yes - Top language, jobs everywhere, AI is excellent at Python.
Performance: No - Slow. Yes, there are workarounds (Cython, etc.) but core Python is slow.
DevX: Yes - No memory management, easy syntax, productive quickly for small projects.
Great for scripts and ML, painful at scale. The lack of real types catches up with you.
Expressive Types: No - Dynamically typed, no static types built-in. Third-party type systems exist (RBS - Ruby 3's official type signature format, and Sorbet - Stripe's static type checker) but neither is widely adopted in the ecosystem. No sum types, no exhaustive matching.
Big Ecosystem: Yes - RubyGems is mature, Rails ecosystem is extensive, lots of web dev packages.
Large Community: Yes - Still popular for web dev, Rails jobs exist, AI knows it well.
Performance: No - Slow like Python. YJIT has improved things but still not competitive with compiled languages.
DevX: Yes - "Developer happiness" is a core philosophy. No memory management, expressive syntax, productive for web apps.
Similar story to Python - great DevX and ecosystem, but no real types and slow. Rails is still a productivity powerhouse if you accept the tradeoffs.
C/C++ - Manual Everything (3.0)
Expressive Types: No - No sum types (C++ has std::variant but clunky). Type system is primitive by modern standards.
Big Ecosystem: Yes - Decades of libraries, runs everywhere, foundational.
Large Community: Yes - Still top 5, jobs exist, AI knows it.
Performance: Yes - As fast as it gets.
DevX: No - Manual memory management. Segfaults. Undefined behavior. Decades of footguns.
Still necessary for systems programming, but I'm not reaching for it unless I have to - and even then I'm trying very hard to bend other langs to accomplish the task instead.
Gleam - The New Contender (2.5)
Expressive Types: Yes - Proper algebraic data types, exhaustive matching, Result built-in, immutable by default, types are sound. ML-family type system.
Big Ecosystem: No - Very young, small package ecosystem. BEAM ecosystem access helps.
Large Community: No - Just hit stable in 2024, tiny but enthusiastic community.
Performance: Mixed - BEAM is great for concurrency, not for compute-intensive work.
DevX: Yes - No memory management, clean syntax, productive.
Most promising new language. If BEAM's compute limits don't matter for your use case, watch this one. Though it probably needs corporate backing and several years to mature before it can gain the ecosystem and community it needs to compete.
Lua - Embedded Niche (2.5)
Expressive Types: No - Dynamically typed, no static types built-in. Teal (a typed dialect that compiles to Lua) exists but is not widely adopted. No sum types, no exhaustive matching in base Lua.
Big Ecosystem: Mixed - Small but focused. Great for game scripting, embedded uses.
Large Community: Mixed - Niche. Jobs exist in games/embedded.
Performance: Mixed - LuaJIT is fast for a scripting language, but it's still a scripting language.
DevX: Yes - Tiny footprint, easy to embed, no memory management for user code.
Excellent at what it does (embedding), not a general-purpose choice.
Elixir - BEAM Without Types (2.5)
Expressive Types: No - Dynamically typed. Type specs exist but not enforced.
Big Ecosystem: Mixed - Hex is decent, BEAM ecosystem, good for web with Phoenix.
Large Community: Mixed - Niche but dedicated (only 3% of developers use Elixir). Jobs exist, especially for real-time/distributed.
Performance: Mixed - BEAM excels at concurrency, struggles with compute.
DevX: Yes - No memory management, pattern matching is nice, productive for web.
Great for specific use cases (real-time, distributed web), but no types and niche usecase is a dealbreaker for me.
Zig - Not Ready Yet (2.0)
Expressive Types: Mixed - Better than C, has tagged unions, but type system is still minimal compared to Rust/F#.
Big Ecosystem: No - Very young, small package ecosystem.
Large Community: No - Niche, growing interest but small.
Performance: Yes - C-level performance, that's the point.
DevX: Mixed - No GC but manual-ish memory (allocators). Simpler than C++ but still systems-level.
Interesting C replacement, but not trying to be high-level. Not what I'm looking for.
Why Does This Gap Exist?
Network Effects Lock In Flawed Languages
Once a language is big, ecosystem compounds. Jobs exist, so people learn it, so more jobs exist. Even flawed languages stay dominant because switching costs are too high.
JavaScript - no native types, weird quirks ([] + [] = ""), but the web ecosystem is too massive.
Python - slow and no sound types, but the data science/ML ecosystem locked it in.
PHP - historically poor design, but WordPress and the web ecosystem keep it relevant.
Java - verbose and no sum types, but enterprise investments are too large to abandon.
Once a language has billions of lines of code in production, adding fundamental features becomes nearly impossible without breaking existing code.
Go - Can't add sum types because they would "interfere [with interfaces] in destructive ways" per a Go team member in a 2011 golang-nuts discussion.
C# - Slowly adding F# features but they're clunky adaptations (unions "coming" for years).
Java - Sealed classes are verbose compared to native discriminated unions.
Python - Type hints can't be enforced at runtime without breaking the billions of lines that rely on duck typing.
Easier to design right from scratch than retrofit.
Design Philosophy Misalignment
Sometimes languages miss important features not because of technical constraints, but because the creators' backgrounds and problem domains didn't prioritize those needs.
Go - Created by systems programmers (Rob Pike, Ken Thompson, Robert Griesemer from Unix/Plan 9/JVM backgrounds) for "working programmers at Google building large-scale server software." They prioritized simplicity, fast compilation, and concurrency over type safety. Sum types weren't in their mental model.
Ruby - Matz designed Ruby to "make programmers happy," explicitly prioritizing developer experience and dynamic typing over type safety. He's stated he believes type signatures will be obsolete, reflecting a deliberate philosophical choice.
Python - The "Zen of Python" emphasizes "readability" and "one obvious way" - static types weren't part of the original vision. Guido later came around, stating type hints are valuable above 10,000 lines of code.
AI Amplifies the Adoption Gap Between Mainstream and Niche Languages
AI is better at top 10 languages (more training data). Small languages get worse AI support, making them harder to use. So larger languages will be easier to onboard to and stick with.
Languages with excellent AI support: Python, JavaScript, TypeScript, Java, C#, Go - massive training data, AI can write idiomatic code and knows ecosystem quirks.
Languages with poor AI support: F#, Gleam, Elixir, Zig - small codebases mean AI hallucinates, suggests wrong patterns, or falls back to related languages (C# patterns for F#, Erlang patterns for Elixir).
This accelerates the death spiral - developers choose languages where AI helps them, making those languages more popular, generating more training data, making AI even better at them.
Is There Hope?
C# Unions (2026?)
Mads Torgersen outlined plans for C# 15. Even if it lands, will the ergonomics be good? F# interop is an open question. But if it works, C# becomes the closest thing to an S-tier language for general-purpose development.
High-Level Rust
I'm experimenting with writing Rust like a high-level language - Using Arc and Clone generously to avoid ownership and lifetime complexity. Trades some performance (10%) for productivity (??%). If this works, Rust could be the language I've been looking for but really all depends on the devx gains.
Gleam
Great types, BEAM ecosystem. 70% admiration in its first Stack Overflow survey appearance (second only to Rust). If your use case is web/distributed and not compute-intensive, worth considering. But I do think it has several years of maturing / growing to do before it's "stable".
Kotlin Multiplatform
Already has the type system. JetBrains is actively pushing Kotlin Multiplatform for cross-platform development. If the multiplatform story matures and community expands beyond Android, it could hit S-tier.
Main gap: Community perception as "the Android language" - hard to shake even when the tech is there.
TypeScript Runtime Validation
If runtime type checking became standard (runtime validators, ts-native libraries that enforce types), it would close the soundness gap. V8 keeps improving, Bun/Deno close the performance gap with compiled languages.
Main gap: Unsound types are a design choice, not a bug. Tooling can mitigate but may never fully fix.
Someone Builds It
The features exist. Someone just needs to combine them right. With a Rust backend for performance, TS-like syntax for familiarity, F#-like types for safety. A Zig moment for typed functional programming.
Next
The missing language is real. Each option forces a tradeoff I don't want to make.
For now, my approach: TypeScript for web stuff (pragmatic), C# for backend (best of the mainstream), trying out high-level Rust to see if it can be productive.
Maybe C# 15 is the answer. Maybe someone builds something new. Maybe I'm too picky.
But I don't think wanting good types AND a good ecosystem is unreasonable. We should demand better from our tools.
Q: What's your current favorite language? What tradeoffs are you making for it?