Skip to content

Team standards

Languages, frameworks, deployment, and tools for each engineering team.

Updated View as MarkdownView on Github

Choose Backend, Web, Mobile, or Desktop for the tools your team uses. Backend includes trading services, FCM clearing and back-office, and web services.

By surface

Trading path backend

Non-trading path backends

FCM clearing and back-office services stay on Python.

Web backends

A web backend serves HTTP to our clients and to partners, and sits off the trading path. Start a new one from this stack.

Choose Express only when a dependency requires it, and record that under exceptions.

Repository layout. A web backend ships from the same repository as the client it serves when that client belongs to the same product. Use pnpm workspaces:

apps/
  api/        # NestJS service, deploys to GKE
  web/        # product client, deploys to Cloudflare Pages
  worker/     # long-running processes, deploys to GKE; add when needed
packages/     # code shared between the apps
cloudflare/   # Pages and Worker configuration
kustomize/    # base and per-environment overlays

Each app deploys on its own, so a change to one does not release the others. Give every app directory its own CODEOWNERS entry. See code ownership.

A client that belongs to the shared trader surface goes to the Web surface instead of into a product repository.

NinjaTrader Forge

Forge runs on macOS, Windows, and Linux.

NinjaTrader 8

  • Language: C#
  • Framework: WPF / NinjaScript
  • Deploy: signed Windows installer

Platform-wide

Concern Standard
Source control GitHub
Build & CI GitHub Actions on self-hosted runners; reusable actions from NT-NinjaTrader/gh-actions. See CI/CD.
CI to cloud auth Workload Identity, no long-lived keys. See security.
Operating systems Windows, macOS, and Linux; native Windows by default, WSL2 only for a hard dependency. See cross-platform repositories.
Containers & registry Docker to Artifact Registry; build once and promote.
Infrastructure Terraform on GCP (Atlantis on PRs)
Secrets GCP Secret Manager via Workload Identity (external-secrets operator in-cluster). See security.
Feature flags Statsig. See feature flags.
Client error tracking Sentry (web, mobile, desktop)
Service error tracking OpenTelemetry with structured logs, no dedicated tracker. See observability.
Telemetry & tracing OpenTelemetry to Cloud Trace
Logs Cloud Logging (log-based metrics drive alerting)
Dashboards & alerts Cloud Monitoring, defined in Terraform
Paging PagerDuty. See release and incidents.
Product analytics Heap, loaded through Google Tag Manager. See observability.
Consent OneTrust
Data warehouse BigQuery
Domain model Defined once in tradovate/master-scheme, generated downstream. See architecture.

Version-support policy

+ marks a minimum version. Keep exact versions in repository manifests and lockfiles. Later versions must meet the support policy below.

Surface Baseline Supported versions
Trading path JVM JDK 21 only Build and target JDK 21
Trading path Scala Scala 2.13 Stay on 2.13 while the Scala 3 decision is open
Non-trading / FCM Python 3.14+ Upstream-supported CPython releases. FCM runs on Python 3.11 and plans to migrate to 3.14.
Web / services Node LTS only Active LTS or Maintenance LTS releases
Mobile Dart / Flutter, current stable Track upstream stable
Desktop / Forge engine .NET 10+ SDK floor in engine/global.json; supported .NET LTS releases only
Desktop / Forge shell Electron 44+, React 19+, TypeScript 6+ Repository manifest and lockfile set exact versions; Electron supplies its own Node runtime
Desktop / NinjaTrader 8 WPF / NinjaScript Follow the product’s project files and supported Windows runtime; Forge’s .NET baseline does not apply

Build with JDK 21 LTS and set the compiler target to 21:

// build.sbt, the version floor is enforced, not documented
ThisBuild / scalaVersion := "2.13.14"
ThisBuild / javacOptions ++= Seq("--release", "21")
ThisBuild / scalacOptions ++= Seq("-release:21", "-Werror", "-Wunused:all", "-deprecation")

Quality-tooling baseline

Run these checks in CI as part of the quality gates.

Surface Format Lint / static analysis Types
Scala scalafmt scalafix compiler -Werror
Python ruff format ruff mypy
TypeScript Prettier ESLint tsc strict
Dart dart format dart analyze built-in
C# dotnet format Roslyn analyzers built-in
Go gofmt go vet + staticcheck compiler
Rust rustfmt Clippy compiler

Run locally the same checks that CI runs:

Build and dependency management

Use the build and dependency managers below.

Surface Build / dependency manager
Scala sbt + coursier
Python uv
TypeScript pnpm
Dart pub
C# NuGet
Go Go modules
Rust Cargo

Forge uses npm with package-lock.json in shell/frontend, rather than pnpm. Its C# projects use NuGet; the native addon uses Cargo. Run builds and checks through Forge’s repository scripts.

FCM uses pip, with pyproject.toml and == pins for direct dependencies, and does not commit a lockfile. FCM plans to adopt uv.

Commit each project’s lockfile and pin direct dependencies. Use Renovate for dependency updates:

// renovate.json, org-wide dependency currency policy
{
  "extends": ["config:recommended"],
  "lockFileMaintenance": { "enabled": true },
  "vulnerabilityAlerts": { "labels": ["security"] }
}

Supply chain

Build once and promote the same artifact through each environment. See The Twelve-Factor App for build/release/run separation and the Artifact Registry docs for registry configuration.

  • Generate an SBOM at build time and retain it with the artifact.
  • Scan dependencies with Renovate or Dependabot; treat vulnerability alerts as work.
  • Sign images and record build provenance so a promoted digest is verifiable downstream.

Governance

Every change to Engineering Standards requires a pull request and review from the Engineering Navigators.

Rule Standard
Ownership @NT-NinjaTrader/Engineering-Navigators owns all Engineering Standards pages and reviews them regularly.
Amendments To change an existing standard, open a pull request with an ADR that records the decision and its context, then request review from the Engineering Navigators.
New languages and tools To add a language or tool, follow the exceptions process before adding it to this page.
Shared CI Reuse the shared actions in NT-NinjaTrader/gh-actions; do not re-implement the CI toolchain in individual repositories.
Domain model Define shared domain entities once in tradovate/master-scheme and generate them downstream. Do not redefine them in a service.

See the Changelog for standards updates.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close