Skip to content

Principles

Guidance on picking the right language for a new service, and the rules that govern everything you build after.

Updated View as Markdown

Starting a new server-side service? Pick its language first. That one choice sets your deploy target, your on-call, and the service pattern you follow. Here is how to decide, and the rules that hold once you have.

Mobile and desktop get no say: mobile is Dart, desktop is C#, fixed by surface. See team-standards. This page is about the server-side languages you actually choose between.

Choosing a language

Ask what happens when the service fails, and stop at the first answer that fits.

  • Does a failure lose orders or breach a millisecond latency budget? Use Scala, owned by Trading Services. This is order handling, accounting and risk, and exchange connectivity: the paths where a slow response means an order never reaches the exchange.
  • Does it clear trades, move money, or emit regulatory or back-office output? Use Python, under the FCM standard. It integrates with vendor and exchange systems we do not control, and a mistake here moves money wrong or misses a filing.
  • Does it ingest raw inbound market data from an outside vendor? Use C++. This is the one carve-out within the failure-mode questions above, so treat it as its own rule, not a footnote to Scala.
  • Anything else? Use TypeScript, front and back, owned by Web.
flowchart TD
A[New service] --> B{"Failure loses orders<br/>or breaches ms latency?"}
B -- yes --> Scala["Scala · Trading Services"]
B -- no --> C{"Clears trades, moves money,<br/>or emits regulatory output?"}
C -- yes --> Python["Python · FCM"]
C -- no --> D{"Raw inbound market data<br/>from an outside vendor?"}
D -- yes --> Cpp["C++"]
D -- no --> TS["TypeScript · Web"]

Two languages sit outside these defaults, reached for by exception when a specific job calls for them, not as a general option:

  • Go, for operational tooling and the Kubernetes ecosystem: CLIs, build and ops glue, and the controllers, operators, and admission webhooks that extend GKE. It is the native language there, so that code stays close to the upstream APIs.
  • Rust, for systems-level or memory-safety-critical work where a garbage-collector pause is unacceptable but you want stronger guarantees than C++: high-throughput data processing, native extensions, and Wasm modules.

Reach for either only when the job genuinely fits. Both are exceptions to the four defaults, not a fifth and sixth default.

Within a language, use the same tool for the same problem. Define shared domain types once in tradovate/master-scheme and generate them downstream; a service that redefines a shared entity drifts out of sync with everything that reads it. The language you land on decides the service pattern waiting for you on the architecture page.

You build it, you run it

The team that ships a service owns it in production: its on-call, its SLOs, its dashboards. No service is someone else’s problem at 3am. That is why these principles run straight into code-ownership, observability, and release-and-incidents.

Contracts don’t break

Keep contracts backward-compatible. When a change has to break one, version it explicitly and follow SemVer; a silent breaking change takes down every consumer you forgot to warn. Record the decisions that are expensive to walk back as ADRs, through the design-docs-rfcs-adrs process.

Secure by default

Least-privilege IAM, no long-lived keys, no secrets in code. Any change that touches authentication gets a security review before it ships, because authentication is the one place a mistake is a breach, not a bug.

Boring and reversible

Prefer the choice that is cheap to undo, and the proven tool over the shiny one: Cloud Run before bespoke Kubernetes, managed services before self-hosted. A new language or tool beyond the ones named here is an exception that needs justification and sign-off, because every new language is one more on-call rotation, one more hiring constraint, and one more toolchain to keep patched.

See also

  • team-standards — the exact language, framework, and tooling for every surface.
  • architecture — the service pattern each language maps to.
  • exceptions — what to do when a principle does not fit.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close