Skip to content

Static Binaries & the Go Toolchain

When you ship a TypeScript/Node.js application you need to provide:

  1. The Node.js runtime (the correct version — managed by .nvmrc, Docker base image, etc.)
  2. Every npm package your code depends on (node_modules/ — often hundreds of MB)
  3. A transpile step (tsc, esbuild, swc, etc.) to convert TypeScript to JavaScript

Deployment means ensuring all three pieces are present and version-matched on every target machine.

A Go program compiles to a single static binary that contains your code, all your dependencies, and the Go runtime — all linked in. The target machine needs nothing extra installed. No Go installation, no package manager, no interpreter.

TypeScript
# TypeScript deployment checklist
# 1. Install Node.js (exact version)
node --version # must match .nvmrc
# 2. Install dependencies
npm install # downloads node_modules/ (~hundreds of MB)
# 3. Build
npx tsc --build
# 4. Run
node dist/index.js
# Dockerfile must include:
# FROM node:20-alpine
# COPY package*.json ./
# RUN npm ci
# COPY dist/ ./dist/
# CMD ["node", "dist/index.js"]
Go
# Go deployment checklist
# 1. Build
go build -o myapp ./cmd/myapp
# 2. Copy binary to target (that's it)
scp myapp user@server:/usr/local/bin/
# 3. Run
./myapp
# Dockerfile can be:
# FROM scratch <-- empty base image!
# COPY myapp /myapp
# CMD ["/myapp"]
#
# Final image is often < 10 MB vs 200–500 MB for Node

Go has built-in cross-compilation. Set GOOS (target OS) and GOARCH (target CPU) as environment variables and run go build. No extra toolchain, no Docker emulation, no separate CI step per platform.

TypeScript
# TypeScript cross-platform is handled by Node.js itself
# But native addons (node-gyp, .node files) break cross-platform
# pkg / nexe / sea bundle Node + app, but are complex and large
Go
# Build for Linux amd64 on a Mac
GOOS=linux GOARCH=amd64 go build -o myapp-linux ./cmd/myapp
# Build for Windows
GOOS=windows GOARCH=amd64 go build -o myapp.exe ./cmd/myapp
# Build for ARM (Raspberry Pi, AWS Graviton, Apple Silicon)
GOOS=linux GOARCH=arm64 go build -o myapp-arm64 ./cmd/myapp
# All three commands run on your laptopno emulation needed
# Each output is a standalone executable for that platform

Go was designed with compile speed as a first-class goal. Large codebases that would take minutes in C++ or even seconds in TypeScript (full tsc) compile in seconds or sub-seconds in Go. The standard library is pre-compiled, dependency graphs are explicit, and there is no circular import detection overhead.

TypeScript
# TypeScript build times (realistic examples)
# tsc --build on a medium NestJS project: 5–30s
# esbuild (fastest TS bundler): 0.5–3s
# But: tsc is also used for type-checking only (no emit)
# Type-check of a large monorepo: 30s–3min
Go
# Go build times
# go build ./... on a medium service: < 2s
# go test ./... (build + run tests): 210s
# go vet ./... (static analysis): < 1s
# The toolchain is one binaryno plugin ecosystem needed:
go build # compile
go test # test
go vet # static analysis
go fmt # format (gofmtno config needed)
go mod # dependency management
What does a Go binary contain that a compiled TypeScript app does NOT?
How do you cross-compile a Go program for Linux from a Mac?
Which env var disables cgo to ensure a fully static binary?
What does go.sum provide that package-lock.json does not?