Twenty-odd years of C#, then Go in production. Each post takes something you already know in .NET and shows you the Go shape of it: where it is the same, where it is different, and where your muscle memory will hurt you.

Read the phrasebook first. Then read the chapter that matches the problem in front of you.

Start here The Phrasebook: C# Concepts in Go Terms A C# to Go phrasebook. Familiar .NET concepts, keywords and operators mapped to their Go equivalents in one bookmarkable reference.

Chapter 1 Getting started

One tool, one file, and a vocabulary swap. Ten minutes with the phrasebook pays for itself in the first hour of reading Go.

  1. The Phrasebook: C# Concepts in Go Terms

    A C# to Go phrasebook. Familiar .NET concepts, keywords and operators mapped to their Go equivalents in one bookmarkable reference.

  2. You Know .NET, Now Meet the Go Toolchain

    A .NET developer's first look at the Go toolchain. go build, go run, go test and go mod compared with the dotnet CLI and MSBuild.

  3. Goodbye .csproj, Hello go.mod

    Go modules and go.mod explained for .NET developers used to NuGet and csproj. No registry, no version ranges, and why it works anyway.

Chapter 2 Types

Structs instead of classes, capitalisation instead of access modifiers, and the two features C# developers ask about first: enums and nil.

  1. Your Class Is Now a Struct (And That's Fine)

    Go has structs, not classes. How methods, constructors and encapsulation work without inheritance, for C# developers who design class …

  2. Where Did My Properties Go?

    Go has no properties and no public or private keywords. How capitalisation controls visibility, and how to encapsulate without getters and …

  3. No Enums? No Problem (Sort Of)

    Go has no enums. How iota, typed constants and stringer stand in for C# enums, and where the substitute falls short.

  4. Generics: Late to the Party

    Go generics compared with C# generics. Type parameters, constraints and the deliberate limits that stop you building your usual …

  5. The Million Dollar Mistake, Differently

    Go's nil compared with C# nullable reference types. Nil pointers, nil slices, nil maps, the nil interface trap and how to stay safe without …

Chapter 3 Control flow and errors

The switch you always wanted, the error handling you did not, and why panic is an emergency exit rather than an exception.

  1. The switch Statement You Always Wanted

    Go's switch statement compared with C# switch and pattern matching. No fallthrough by default, tagless switches, type switches and the …

  2. if err != nil: Your New Reality

    Go error handling for C# developers. Why if err != nil replaces exceptions, how to wrap and inspect errors, and when the verbosity pays off.

  3. Panic and Recover: The Emergency Exit

    Go's panic and recover look like exceptions but are not meant to be used like them. When they belong, and why Go developers judge you for …

Chapter 4 Memory

Pointers, slices and the stack, for people who have let the garbage collector think about all three for twenty years.

  1. Pointers Without the Pain

    Pointers in Go for C# developers who have never needed them. Value versus pointer receivers, when to pass by pointer, and why it is less …

  2. Slices Are Not Arrays (And Neither Is List<T>)

    Go slices are not arrays and not List<T>. Length, capacity, shared backing arrays and the append gotchas that catch every C# developer.

  3. Stack vs Heap: Go's Escape Analysis

    How Go decides between stack and heap at compile time. Escape analysis explained for C# developers used to value types and the garbage …

Chapter 5 Concurrency

Goroutines against async/await, then channels, select, context and the sync package for when channels are the wrong tool.

  1. Async/Await vs Goroutines: A Mindset Shift

    Goroutines explained for C# developers who think in async/await and Task. No coloured functions, no thread affinity, and a different mental …

  2. Channels: The sync You Didn't Know You Wanted

    Go channels explained for C# developers who know BlockingCollection and message passing. Buffered, unbuffered, closing, ranging and the …

  3. select: Multiplexing Like a Pro

    Go's select statement for multiplexing channels. Timeouts, non-blocking operations and cancellation, compared with Task.WhenAny in C#.

  4. Context Is King

    Go's context.Context compared with C# CancellationToken. Cancellation, deadlines and request-scoped values, and why every I/O function takes …

  5. Mutexes and WaitGroups: When Channels Aren't the Answer

    Mutexes, RWMutex, WaitGroup, Once and atomic in Go's sync package, compared with lock and SemaphoreSlim in C#. When to use them instead of …

Chapter 6 Interfaces and composition

No implements keyword, no inheritance, and an empty interface that is more dangerous than object. This chapter is where Go stops feeling strange.

  1. Implicit Interfaces: Scary Then Brilliant

    Go interfaces are satisfied implicitly, with no implements keyword. Why that feels like chaos to C# developers and why it turns out to be …

  2. Composition Over Inheritance (For Real This Time)

    Go has no inheritance at all. How struct embedding replaces C# class hierarchies, and why it stops being frightening after the first week.

  3. The Empty Interface and Type Assertions

    Go's any type explained for C# developers. How type assertions and type switches compare to object and casting, and when reaching for any is …

Chapter 7 Testing and project layout

Tests without xUnit, mocks without Moq, benchmarks without BenchmarkDotNet, and a project layout that owes nothing to the solution file.

  1. Testing Without a Framework

    Testing in Go with the built-in testing package instead of xUnit or NUnit. Table-driven tests, subtests, test helpers and coverage.

  2. Mocking in a Language Without Mockito

    Mocking in Go without Moq or NSubstitute. Hand-written fakes, small interfaces, and generated mocks with mockery and gomock.

  3. Benchmarks and Profiling Out of the Box

    Go has benchmarking and profiling built into the toolchain. A C# developer's guide to go test -bench and pprof, compared with …

  4. Organising a Real Project

    How to organise a real Go project. Packages, internal directories, cmd layouts and why .NET's solution and namespace hierarchy does not …

  5. Multi-Module Repos: Monorepo Thinking

    Multi-module Go repositories and go.work workspaces, for teams coming from .NET solutions with shared projects and many services.

Chapter 8 Building services

The ASP.NET Core chapter. Routing, JSON, databases, configuration and logging, mostly from the standard library.

  1. HTTP Services: net/http vs ASP.NET Core

    Building HTTP services with Go's net/http instead of ASP.NET Core. Routing, middleware, handlers and JSON without a framework.

  2. JSON: Struct Tags and the Marshal Dance

    JSON in Go with encoding/json and struct tags, compared with System.Text.Json and Newtonsoft. Omitempty, custom marshalling and the quirks …

  3. Database Access: database/sql vs Entity Framework

    Go's database/sql versus Entity Framework. Writing SQL by hand, scanning rows into structs and living without change tracking or LINQ.

  4. GORM and Friends: When You Do Want an ORM

    GORM for developers who miss Entity Framework. What the most popular Go ORM offers, why the community argues about it, and the alternatives …

  5. Configuration Without IOptions<T>

    Configuration in Go without IOptions or IConfiguration. Environment variables, flags, file parsing and the libraries .NET developers reach …

  6. Logging: slog and the Structured Logging Story

    Structured logging in Go with log/slog, compared with ILogger and Serilog. Handlers, attributes, context and when to still pick zerolog or …

Chapter 9 Tooling

gofmt ends the formatting argument, Delve is not Visual Studio, and go generate replaces most of what you used reflection for.

  1. Linting and Formatting: gofmt Is Non-Negotiable

    gofmt, go vet and golangci-lint for developers used to .editorconfig debates. Why Go formatting is not up for discussion, and the linters …

  2. Debugging: Delve vs Visual Studio's Comfort

    Debugging Go with Delve after years of Visual Studio. What works, what is missing, and how to set up breakpoints and watches in VS Code and …

  3. Generate All the Things

    Code generation with go generate in place of C# reflection and source generators. Why Go developers write code that writes code, and the …

Chapter 10 Deployment

The reason most people make the move. Single binaries, tiny containers, Kubernetes, Lambda, observability and a CI pipeline to match.

  1. Single Binary Deploys: The Killer Feature

    Go compiles to a single static binary with no runtime to install. Why that changes deployment for .NET developers, and how cross-compilation …

  2. Shrinking Binaries and Build Tags

    Shrink Go binaries with ldflags and control what gets compiled with build tags. A guide for .NET developers used to self-contained …

  3. Dockerfile for Go: Simpler Than You'd Think

    Multi-stage Dockerfiles for Go that produce images under 20MB. How static binaries and scratch base images compare with .NET container …

  4. Kubernetes and Go: A Natural Fit

    Running Go services on Kubernetes. Graceful shutdown, resource limits, health checks and why Go and Kubernetes fit together so naturally.

  5. Health Checks and Readiness Probes

    Liveness and readiness probes in Go for Kubernetes. Health check endpoints that tell orchestrators something useful, compared with ASP.NET …

  6. AWS Lambda and Go: Cold Starts That Don't Hurt

    Go on AWS Lambda cold starts in around 100ms. How that compares with .NET Lambda functions, and how to build and deploy a Go handler.

  7. Metrics, Traces, and Logs: The OpenTelemetry Way

    Instrumenting a Go service with OpenTelemetry. Metrics, traces and logs wired up end to end, compared with the .NET OpenTelemetry setup.

  8. GitHub Actions for Go Projects

    A complete GitHub Actions workflow for Go projects. Build, test, lint, cross-compile and release, compared with the .NET CI setup you …

  9. Versioning Your Modules

    Versioning Go modules with semantic versioning and git tags. Major version suffixes, pseudo-versions and the differences from NuGet …

Chapter 11 Looking back

What I miss, what I will never go back for, and whether it was worth it after six months in production.

  1. What I Actually Miss From C#

    The C# features I actually miss after months of writing Go full time. LINQ, proper enums, nullable reference types and a few others.

  2. What I'll Never Go Back For

    The Go features a long-time C# developer would struggle to give up. Single binaries, fast builds, implicit interfaces and the rest.

  3. Six Months In: Was It Worth It?

    Six months of production Go after twenty years of C#. An honest retrospective on what was worth it, what hurt, and what I would tell past …