Choose Your Hammer Wisely 2: Programming Languages

Languages are tools. You don’t use a hammer for screws.

Most developers marry a language. They learn Python in college, write Python for a decade, and convince themselves Python is the answer to everything. This is a mistake.

The tradeoffs aren’t what you think they are.

The Moat Has Collapsed

Companies still hire for language-specific expertise. “5+ years Rust experience required.” This made sense when learning curves were real barriers.

AI coding assistants changed the equation.

You can now write production Rust in your first week with the right tools. The assistant knows the borrow checker. It knows the idioms. It catches the mistakes you’d make as a newcomer and explains why.

The moat that protected language experts has practically disappeared.

The world hasn’t caught up yet. Hiring managers still filter for years of experience in specific languages because that’s how it’s always been done. There’s a recognition lag between what experience signals and what it actually predicts about productivity.

In a more efficient future, employers will test for problem-solving ability and tool selection, not years of language experience. Current hiring criteria overvalue domain-specific expertise that AI has commoditized.

I wrote about this recognition lag in Opportunities Surfaced by AI Lags.

The Landscape

Here’s how I think about it:

                    SYSTEMS
                       │
           ┌───────────┴───────────┐
           │                       │
           │        Rust           │
           │    (backend, CLI,     │
           │     WASM, embedded)   │
           │                       │
           └───────────┬───────────┘
                       │
    ┌──────────────────┼──────────────────┐
    │                  │                  │
SCRIPTING          OVERLAP            GPU/ML
    │                  │                  │
┌───┴───┐         ┌────┴────┐        ┌────┴────┐
│  TS   │◄───────►│ Python  │◄──────►│Python+  │
│(tools,│         │(glue,   │        │  JAX    │
│ web)  │         │scripts) │        │(training│
└───────┘         └─────────┘        │ infra)  │
                                     └─────────┘

         LOGIC                    FRONTEND
           │                          │
      ┌────┴────┐               ┌─────┴─────┐
      │ Prolog  │               │  Svelte5  │
      │(rules,  │               │  (web)    │
      │ arch)   │               │           │
      └─────────┘               │ Swift/    │
                                │ Kotlin    │
                                │ (native)  │
                                └───────────┘

Different problems. Different tools.

Rust Over Go, Every Time

I’ll say it directly: for backend services, I pick Rust over Go.

Go’s appeal is obvious. Simple syntax. Fast compilation. Goroutines. You can hire for it. I get it.

But Rust gives you memory safety without a garbage collector. Real ownership semantics, not “hope the GC handles it.” When your service is processing millions of requests, you don’t want pause times. You want predictability.

The learning curve is real. The borrow checker will fight you for a week. Then it clicks, and you realize it’s catching bugs at compile time that would’ve been 3am production incidents.

Go is fine. Rust is better.

TypeScript for Orchestration

When I’m building coding assistants, I reach for TypeScript.

These tools orchestrate. They call APIs, parse responses, manage state across dozens of tool interactions. The work is inherently dynamic. You’re juggling heterogeneous data from different sources.

TypeScript gives you just enough type safety to catch stupid mistakes, with enough flexibility to handle the chaos. The Node ecosystem has integrations for everything. Need to call an LLM? Parse markdown? Spawn subprocesses? There’s a package.

Python could do this. But TypeScript’s async model is cleaner, and the tooling is better for long-running interactive processes.

For the frontend that wraps these tools? Svelte5. Reactivity done right. No virtual DOM diffing, no useState ceremony. Just reactive statements that work.

Python and JAX for GPU Work

If you’re training models or doing anything GPU-accelerated, you use Python.

Not because Python is fast. It’s not. But the ecosystem is unbeatable. CUDA bindings, hardware vendor support, pretrained models. It’s all Python.

Specifically, I use JAX over PyTorch when I can. Functional transformations, JIT compilation, easy parallelism across devices. PyTorch won the mindshare war, but JAX is the better tool for research-style iteration.

Python is the glue. JAX does the heavy lifting. Don’t fight the ecosystem.

The Prolog Argument

Here’s my spicy take: Prolog deserves a place in your toolkit.

Logic programming is different. You declare relationships and constraints, then ask questions. The runtime figures out how to answer them.

I’ve used it for reasoning about software architecture. Given a set of microservices with dependencies, what’s the minimal set of changes to refactor X into Y? What are all the paths through the system that touch this database? These are constraint satisfaction problems. Prolog is built for them.

It’s not for everything. It’s not even for most things. But when you need to reason about rules and relationships, an imperative language makes you do the work the runtime should handle.

Different paradigm. Different insights.

WASM: Promising, Painful

Rust compiles to WebAssembly. In theory, this gives you near-native performance in the browser, type safety across the stack, shared code between server and client.

In practice, the tooling hurts.

Debugging is rough. Bundle sizes balloon if you’re not careful. The interop between WASM and JavaScript is friction you feel on every project.

I’m watching this space. The benefits are real. But I’m not defaulting to it yet.

Cross-Platform is a Trap (Mostly)

Flutter lets you write once, run everywhere. React Native too. The pitch is compelling.

But coding assistants changed the equation.

When researching platform best practices and implementing native code is low-friction, when you can ask an AI to scaffold a Swift view or generate Kotlin boilerplate, the calculus shifts. The cost of writing native went down. The cost of cross-platform quirks stayed the same.

My current stack: Svelte5 for web. Swift for iOS. Kotlin for Android.

Three codebases? Yes. But each one uses the platform properly. No fighting the abstraction layer. No “works on iOS but breaks on Android” bugs.

When AI handles the boilerplate, native wins.

The Summary

DomainMy PickWhy
Backend ServicesRustSafety, performance, no GC
Tool OrchestrationTypeScriptAsync, ecosystem, flexibility
Web FrontendSvelte5Reactivity done right
GPU/MLPython + JAXEcosystem, hardware access
Logic/RulesPrologDifferent paradigm, constraints
iOS NativeSwiftPlatform APIs, performance
Android NativeKotlinPlatform APIs, modern language
Cross-platform(avoid)AI makes native cheaper

Match the tool to the problem.

Don’t optimize for “knowing one language really well.” That’s developer ego, not engineering sense.

Optimize for picking the right one each time.


Choose Your Hammer Wisely Series