Whether you’re curious about Go or ready to make the switch, this series takes you through the journey from a .NET developer’s perspective. Each post compares familiar C# concepts with their Go equivalents, helping you build on what you already know.

All Posts in Order

  1. Goodbye .csproj, Hello go.mod

    Right, let’s talk about dependencies. In .NET land, we’ve got NuGet, .csproj files, PackageReference …

  2. The Phrasebook: C# Concepts in Go Terms

    Before we get into the weeds of types and patterns, let’s establish some vocabulary. Go uses different names for …

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

    After twenty-odd years writing C#, I’m learning Go. Not because .NET has failed me (it hasn’t) but because …

  4. Generics: Late to the Party

    Go shipped generics in version 1.18 (March 2022). C# has had them since 2.0 (November 2005). That’s a …

  5. No Enums? No Problem (Sort Of)

    Go doesn’t have enums. Not “Go has something enum-like”. It genuinely doesn’t have a dedicated …

  6. The Million Dollar Mistake, Differently

    Tony Hoare called null references his “billion dollar mistake.” Both C# and Go inherited some form of this …

  7. Where Did My Properties Go?

    Coming from C#, one of the first things you’ll notice is that Go structs look… naked. Where are the { get; …

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

    Here’s something that’ll feel wrong for about a week: Go doesn’t have classes. Not “Go has …

  9. if err != nil: Your New Reality

    Let’s address the elephant in the room. You’re going to write if err != nil hundreds of times. Thousands, …

  10. Panic and Recover: The Emergency Exit

    So Go doesn’t have exceptions. Except… it kind of does. They’re called panic and recover, and they …

  11. The switch Statement You Always Wanted

    C#’s switch statement has evolved a lot over the years. Pattern matching, switch expressions, when guards. …

  12. Pointers Without the Pain

    Right, let’s talk about pointers. If you’ve spent your career in C#, you’ve probably used pointers …

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

    Every C# developer coming to Go makes the same mistake: they see []int and think “array” or …

  14. Stack vs Heap: Go's Escape Analysis

    In C#, you mostly don’t think about where variables live. Value types go on the stack (usually). Reference types …

  15. Async/Await vs Goroutines: A Mindset Shift

    Go’s concurrency model is going to feel backwards. You’ve spent years learning that async operations need …

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

    If goroutines are Go’s lightweight threads, channels are how they talk to each other. Think …

  17. Context Is King

    Every Go function that does I/O, might take a while, or should be cancellable will take a context.Context as its first …

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

    Go’s mantra is “share memory by communicating,” but sometimes you just need a bloody mutex. The sync …

  19. select: Multiplexing Like a Pro

    The select statement is where Go’s channel system goes from “neat” to “powerful.” It lets …

  20. Composition Over Inheritance (For Real This Time)

    We’ve all heard “favour composition over inheritance.” We’ve all nodded sagely. And then …

  21. Implicit Interfaces: Scary Then Brilliant

    Here’s a sentence that’ll make every C# developer uncomfortable: Go interfaces don’t require an …

  22. The Empty Interface and Type Assertions

    Go has a type called any. Before Go 1.18, it was written interface{}. Same thing, nicer name. And it’s basically …

  23. Benchmarks and Profiling Out of the Box

    Here’s something that surprised me: Go has built-in benchmarking. No BenchmarkDotNet to install. No configuration. …

  24. Mocking in a Language Without Mockito

    In C#, you reach for Moq or NSubstitute without thinking. Interface? Mock it. Verify calls? Easy. Set up return values? …

  25. Multi-Module Repos: Monorepo Thinking

    Most Go projects have one go.mod file at the root. One module, one version, simple. But what happens when your repo …

  26. Organising a Real Project

    So you’ve got the basics. You can write Go code. Now you need to organise it into something that won’t …

  27. Testing Without a Framework

    In C#, you pick a test framework: xUnit, NUnit, MSTest. You install packages. You learn attributes. You configure test …

  28. Configuration Without IOptions<T>

    In ASP.NET Core, configuration is a whole subsystem. IConfiguration, IOptions<T>, IOptionsSnapshot<T>, …

  29. Database Access: database/sql vs Entity Framework

    Entity Framework is an ORM. It maps objects to tables, generates SQL, tracks changes, handles migrations. You can build …

  30. Debugging: Delve vs Visual Studio's Comfort

    Visual Studio’s debugger is exceptional. Breakpoints, watch expressions, edit-and-continue, conditional …

  31. Generate All the Things

    Go developers love code generation. Where C# uses reflection, attributes, and source generators, Go often uses tools …

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

    Despite Go’s “just write SQL” culture, ORMs exist and are popular. GORM is the most widely used. If …

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

    ASP.NET Core is a sophisticated web framework. Dependency injection, middleware pipelines, model binding, routing with …

  34. JSON: Struct Tags and the Marshal Dance

    In C#, you add [JsonProperty("name")] or rely on naming conventions. The serializer figures out the rest. …

  35. Linting and Formatting: gofmt Is Non-Negotiable

    In C#, code formatting is a matter of preference. Tabs or spaces? Braces on the same line or next? Teams debate, …

  36. Logging: slog and the Structured Logging Story

    For years, Go’s logging story was “use the log package or pick a third-party library.” The standard …

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

    If you’ve run .NET on Lambda, you know the cold start pain. 3-5 seconds for a managed runtime. Even Native AOT …

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

    Go’s static binaries make for tiny Docker images. Where a .NET container might be 200MB+, a Go container can be …

  39. GitHub Actions for Go Projects

    CI/CD for Go projects is refreshingly simple. Fast builds, built-in testing, cross-compilation. Everything you need is …

  40. Health Checks and Readiness Probes

    We touched on health checks in the Kubernetes post. Let’s go deeper. Proper health checks that actually tell …

  41. Kubernetes and Go: A Natural Fit

    Kubernetes is written in Go. The CLI tools are Go. The ecosystem is Go. Running Go services on Kubernetes feels natural. …

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

    Observability in production means three things: metrics (what’s happening), traces (how requests flow), and logs …

  43. Shrinking Binaries and Build Tags

    Go binaries are already small compared to .NET self-contained deployments. But sometimes you want smaller: Lambda …

  44. Single Binary Deploys: The Killer Feature

    What sold me on Go for production: you build a binary, you copy it to a server, you run it. No runtime to install. No …

  45. Versioning Your Modules

    Versioning in Go is simple in concept (semantic versioning with git tags) but has quirks that’ll catch you if …

  46. What I Actually Miss From C#

    I’ve been writing Go full-time for a couple of months now. I like it. I’m productive. But let’s be …

  47. What I'll Never Go Back For

    Last time I talked about what I miss from C#. Now let’s flip it: what has Go given me that I’d properly …

  48. Six Months In: Was It Worth It?

    Six months ago, I started this series with a simple goal: document learning Go as an experienced C# developer. Not a …