name: language-selection
description: Catalog + decision procedure for choosing a programming language. TRIGGER whenever you are about to pick, default, recommend, or assume a language for new work — a greenfield project/service/script/CLI/library, a component in a polyglot system, a rewrite or port, or when a user asks "what should I build this in". Also when weighing two stacks, or judging whether an existing language still fits a new requirement. Don't default to Python/JS from habit: consult the catalog (references/index.md) so the pick is matched to domain, hard constraints, stack, and ecosystem — with tradeoffs stated.
Language Selection
This skill exists to push a language choice past your default priors. Picking a
language is a consequential, hard-to-reverse decision — it sets the deploy
target, the performance ceiling, the libraries within reach, and the failure
modes you live with. Reason from the task's actual requirements instead of
reaching for whatever language is most probable by default (Python, JavaScript),
then consult the catalog in references/index.md to weigh those requirements
against the full field before you commit.
The procedure
- Surface HARD constraints first — these eliminate candidates; they do
not trade off. Read these off the task itself, before any candidate language
anchors your thinking:
- Target/runtime: browser → JS/TS (or a WASM-source lang); iOS → Swift;
no-GC / hard-real-time / tiny footprint → C / C++ / Rust / Zig; an existing
JVM or .NET shop; serverless cold-start ceilings.
- Interop / FFI: must it call into existing C, Java, .NET, or Python?
- Performance floor: are GC pauses or a memory ceiling actually disqualifying,
or just assumed to be? Demand a number before letting this drive the choice.
- Certification / safety: safety-critical → Ada/SPARK, MISRA-C, DO-178C lanes.
- Surface SOFT factors — these trade off, weighted to THIS project:
- Existing codebase / current stack. Reusing what's already in place — its
build, deploy, and libraries — usually beats adding a second language;
weigh it unless a hard constraint forbids.
- Ecosystem maturity for this domain. The right library beats language
elegance; a missing one sinks an elegant choice.
- Iteration speed vs runtime performance for the project's current phase.
- Long-term maintainability and readability of the result.
- Build the candidate set from those requirements — consult
references/index.md and assemble candidates from what actually fits, not from
what comes to mind first. The index is the source of truth; reason over it
rather than recalling a usual answer. (The quick table below is a fast starting
point for common cases — candidates to verify, not answers.)
- Intersect candidates with the hard constraints → shortlist 2–3.
- Break ties with soft factors. Recommend ONE. Name the runner-up. State
explicitly what you trade away and the condition under which the runner-up wins.
- If the user already named a language, your job is fit-check, not override:
confirm it clears the hard constraints, flag genuine mismatches with evidence,
and do not relitigate taste.
Falsifiable rules (the habits this skill exists to break)
- Don't reach for the language that's merely most probable by default. Fitting an
existing codebase IS a legitimate SOFT factor — name it as one; don't dress a
default up as a domain match.
- Fitting an existing codebase usually beats a greenfield-optimal pick unless a
hard constraint forbids it — introducing a new language carries real,
front-loaded cost (build, deploy, interop).
- "Rewrite it in Rust/Go" needs a hard-constraint justification (perf floor,
memory safety, deploy target) — not aesthetics. A rewrite discards working code
and the edge-case knowledge baked into it.
- Prefer boring/proven for load-bearing systems. Reserve emerging languages
(Zig, Mojo, Gleam, Roc) for where their specific edge IS the point and you can
absorb the ecosystem gaps.
- Every added language in a polyglot system is a tax: build/CI, interop,
context-switching. Justify each one; "right tool for the job" is not a free pass.
- Performance intuition is unreliable. A "slow" language with the right library
(NumPy, the JVM JIT, V8) often beats a hand-rolled fast-language path. Measure
the hot path; don't pick the whole stack to optimize a path that isn't hot.
Quick domain → candidates (the common 80%)
A fast starting point for common cases — entries are candidates to verify against
your requirements and the catalog, never defaults to settle for. The genuinely
forced cases (browser → JS/TS, iOS → Swift) are hard constraints, already in step 1.
| Domain / task | Lead candidates | Also consider |
|---|
| Systems / OS / embedded / no-GC | Rust, C | C++, Zig, Ada (safety) |
| CLI tools / dev tooling | Go, Rust | Python (quick), TypeScript (Node) |
| Backend web service / REST/gRPC API | Go, TypeScript, Java/Kotlin | C#, Python, Elixir, Ruby |
| Very high concurrency / soft-real-time | Elixir/Erlang, Go | Rust, Java (virtual threads) |
| Web frontend (browser UI) | TypeScript | (WASM source: Rust, C++) |
| ML / data science / AI | Python | R (stats), Julia (numerical) |
| Data engineering / ETL / big data | Python, SQL | Scala/Java (Spark), Go |
| Scientific / HPC / numerics | C++, Julia, Fortran | Python+native, CUDA C++ (GPU) |
| Scripting / automation / glue | Python, Bash | PowerShell (Windows), Ruby |
| iOS / macOS | Swift | Objective-C (legacy interop) |
| Android | Kotlin | Java (legacy) |
| Cross-platform mobile | Dart/Flutter, TS/React Native | Kotlin Multiplatform |
| Game engine / real-time graphics | C++, C# (Unity) | Rust (emerging), Lua (scripting) |
| Enterprise / large team / long-lived | Java, C# | Kotlin, Go |
| Correctness-critical / heavy domain logic | OCaml, F#, Haskell | Scala, Rust, Elixir |
| Smart contracts | Solidity (EVM) | Move (Aptos/Sui), Rust (Solana) |
| Quick data analysis / notebooks | Python (pandas) | R, Julia |
Full catalog
references/index.md — the language repository: ~45 languages grouped by family
(systems, managed-runtime backend, frontend, data/ML/scientific, functional,
mobile, scripting/shell, emerging, and domain-specific), each with a card covering
paradigm, typing, runtime, concurrency model, performance tier, killer ecosystem,
best-fit domains, and explicit pick-when / avoid-when. It is the source of
truth the quick table only summarizes — open the relevant family every time you
build a candidate set (step 3), not only for unfamiliar domains or niche targets.
The families exist so you can scan the whole relevant field, not just the names
that came to mind.