The MOST AI-maintainable tech stack (and why most people hate it)
The Poetry i.e Code
I recently watched David Heinemeier Hansson(DHH) on the Lex Fridman podcast 1. If you haven’t seen it, DHH speaks about Ruby with the kind of reverence most people reserve for first loves. He talks about expressiveness, beauty, the joy of writing code that reads like poetry.
And honestly? I loved it. There’s something deeply beautiful about people being so passionate about something, something that they love so much that they could keep talking about it for hours. Back when I started coding, Laravel 2 was THAT for me. So I understand what he feels ❤️. If I had found Ruby when I was learning coding, I probably would have loved it the same way, same way I loved JavaScript.
Then I started thinking - if its so readable, if it’s so language-like, would AI love it too? And wouldn’t it be win-win? Wouldn’t that improve DX and make things efficient? I wasn’t ready for unfortunate answers.
But why think about AI?
Well, ‘cause we’re living through a fundamental shift in how software gets written. I remember using AI for autocompletion but now it’s writing functions, debugging logic, refactoring entire modules, converting old codebase to newer technologies. So I found myself wondering: what would AI works BEST with?
I noticed a pattern: A JS codebase takes ages for AI to complete a feature, gets the solutions wrong more often for bugs, but in TypeScript project, it iterates well, less AI guesses are wrong.
Not what’s popular, not what’s beautiful, not what has the best job market or the most GitHub stars, or best-whatever. Because what I care about is maintainability. Has to be maintained. By humans, yes, but increasingly by AI. So the question is —
Which languages can AI learn most easily, reason about most reliably, and maintain most effectively?
I went down this rabbit hole. And the answers are… uncomfortable.
The uncomfortable truth
Here’s what I discovered: the languages AI works best with are often the ones developers find tedious. There’s an inverse relationship between “joy to write” and “easy for AI to understand.”
Why? Because the things that make code fun for humans—clever shortcuts, implicit behavior, expressive metaprogramming—are exactly what makes code opaque to AI.
AI doesn’t appreciate elegance like we do. It can’t hate verbosity. It just needs to understand what your code does, verify it’s correct, and modify it safely.
That’s a different set of priorities.
What makes code AI-friendly?
Before I give you the recommendations, here’s the framework/properties to keep in mind —
1. Explicit over implicit
If dependencies are declared rather than inferred, AI can trace data flow without guessing. If state changes are obvious function calls rather than reactive magic, AI can reason about what triggers what.
2. Strong static typing
Types are the closest thing we have to a machine-readable specification. AI can verify correctness without running code. It can refactor with confidence. It can catch errors before they become bugs.
3. One way to do things
When a language has seventeen ways to iterate over a list, AI has to handle all seventeen. When there’s one way, there’s no ambiguity in what to generate or what to expect.
4. Local reasoning
Can AI understand this function by looking at just this function? Or does it need to know about some metaclass three files away that modifies behavior at runtime? The more locally code can be understood, the better.
5. No magic
Metaprogramming, monkey patching, runtime code generation — these are powerful tools. They’re also invisible to static analysis. AI can’t reason about code that doesn’t exist until the program runs.
The recommendations
Frontend: TypeScript + React (not even Svelte 😭)
Svelte is beautifully designed. It’s concise, it’s fast, and we all love it.
But: Svelte uses implicit reactivity. The compiler infers dependencies. There’s a custom template DSL. The $: reactive statements require understanding compiler rules that aren’t visible in the code.
React is more verbose, yes. But:
- Dependencies are explicitly declared in hooks
- It’s just JavaScript with JSX, no custom language
- State updates are explicit function calls
- No compiler magic to mentally simulate
The trade-off is clear: Svelte optimizes for concision, React optimizes for explicitness. For AI, explicit wins any day.
Pair React with:
| Layer | Choice | Why? |
|---|---|---|
| Language | TypeScript (strict mode) | Types = easy understanding & verification |
| Styling | Tailwind CSS | All styles visible in component, no hunting through CSS files |
| Server State | TanStack Query | Handles caching and loading states explicitly |
| Client State | Zustand | Minimal boilerplate, TypeScript-friendly |
| Validation | Zod | Schema is the source of truth for types AND runtime validation |
Backend: Go (not Ruby, not Python)
Ruby is expressive. Python is readable. Both are loved by their communities for good reasons.
But dynamic languages are hard for AI because:
- Types can’t be statically determined
- Metaprogramming generates code at runtime
- Methods can be added or modified anywhere (monkey patching)
- Behavior depends on load order
- The same code can do different things in different contexts
Go is… boring and honestly a little ugly too.
Twenty-five keywords. One way to format code (enforced by gofmt). Explicit error handling instead of exceptions. No generics until recently, and still minimal.
But Go is transparent:
- What you see is what you get
- No hidden control flow
- Explicit error handling on every call
- Static types throughout
- No magic, no surprises
AI learnability score: Go gets 9/10. Ruby gets 4/10.
When you need performance: Rust
Rust has a steep learning curve. The borrow checker is DEMANDING 🤦♂️. It’s not a language you’d pick for quick prototypes.
For AI though, Rust is “hard to write, easy to verify.” Let’s see why:
Hard to write:
- Ownership and borrowing require tracking state across the entire function, not just locally.
- Lifetimes must be inferred / annotated correctly — If it gets it wrong, nothing compiles
- Many valid choices (String vs &str, Box vs Rc vs Arc) with cascading tradeoffs, which AI can’t reason about without knowing full context
Easy to verify:
- If it compiles, huge classes of bugs are already impossible — memory safety, thread safety, null pointer exceptions
- The compiler has already proven correctness; AI doesn’t need to run the code to trust it
| Use Case | Recommendation |
|---|---|
| Web API / REST service | Go |
| CLI tool | Go |
| Microservices | Go |
| Database / Storage engine | Rust |
| Systems programming | Rust |
| High-performance infrastructure | Rust |
| Rapid prototyping | Go |
The Final scorecard
Let’s rank languages AI learnability:
| Language | Learnability | Notes |
|---|---|---|
| Go | 9/10 | Ugliness is a feature |
| Elm 3 | 10/10 | Perfect, but only FE |
| Rust | 7/10 syntax, 9/10 semantics | Hard to write, easy to verify |
| TypeScript | 8/10 | Best-in-class for frontend |
| Python | 6/10 | Fine for simple scripts, struggles at scale relative to Go |
| Ruby | 4/10 | Beautiful for humans, unmanageable for AI |
Okay, why this matters now
We’re moving towards an ‘era’ where your code will be read and modified by AI as often as by humans. Probably more.
This does not mean you should rewrite everything in Go today. But it does mean the calculus has changed.
When DHH talks about the joy of Ruby, he’s optimizing for the human writing experience. That’s a very valid choice. For some projects, it probably is a right choice, even now. I probably would pick Ruby for a small side project just for the joy of it (I’ve never tried it, so I don’t know).
But if you’re building a system that will be maintained for years, touched by many developers (and increasingly, by AI), the cost-benefit analysis shifts significantly. The verbosity you pay upfront in Go or TypeScript buys you reliability, refactorability, and AI-assisted maintenance down the road.
The languages AI prefers aren’t the ones we love. They’re the ones that leave nothing to interpretation.
AI can’t appreciate elegance. It can’t hate boilerplate. It just needs to understand what your code does.
Key principles
If you take nothing else from this:
- TypeScript with strict mode everywhere — Types are documentation and verification
- Explicit dependencies — Declare what triggers what
- One way to do things — Consistency over flexibility
- Schema as source of truth — Define validation and types in one place
- Boring is a feature — Save cleverness for problems that genuinely need it
Footnotes
- ↩
-
https://laravel.com/ — I said this many times to my colleagues at the time — If I have a daughter, I’d name her Laravel. True story. ↩
-
https://elm-lang.org/ — We should talk about it more but some other time ‘cause its domain is limited to frontend web development. Still worth exploring. In short: Elm is pure functional programming language that compiles down to JS ↩
What to do next.
I don't have comments on my blog, but I love hearing your feedback on Twitter. I read everything I get and respond when I can.