Packages and Modules
Go modules and packages vs C# namespaces and NuGet
Introduction
In this lesson, you'll learn about packages and modules in Go. Coming from C#, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.
In C#, you're familiar with go modules and packages vs c# namespaces and nuget.
Go has its own approach to go modules and packages vs c# namespaces and nuget, which we'll explore step by step.
The Go Way
Let's see how Go handles this concept. Here's a typical example:
// Every .go file declares its package
package math // directory name convention
// ONLY visibility: Uppercase = exported, lowercase = unexported
func Add(a, b int) int { return a + b } // exported (public)
func helper() {} // unexported (internal)
// init() — auto-runs before main (like C# static constructor)
func init() { /* one-time package setup */ }
// go.mod — dependency manifest
// module github.com/user/myapp
// go 1.21
// require github.com/gin-gonic/gin v1.9.1
// Import
import (
"fmt" // stdlib
"github.com/user/myapp/math" // local package
"github.com/gin-gonic/gin" // external
)
// Commands
// go mod init github.com/user/myapp (like dotnet new)
// go get github.com/gin-gonic/gin (like dotnet add package)
// go mod tidy (remove unused)
// go build -o app . (single binary — no runtime needed)
// GOOS=linux go build -o app-linux . (cross-compile)Comparing to C#
Here's how you might have written similar code in C#:
// Namespace (like Go package)
namespace MyApp.Math;
// Access modifiers
public class Calculator { } // accessible everywhere
internal class Helper { } // assembly-private
private class Nested { } // class-private
// File-scoped namespace (C# 10)
namespace MyApp.Utils;
public static class StringUtils { }
// Global using (C# 10)
global using System.Collections.Generic;
// .csproj — dependency management
// <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
// dotnet add package Newtonsoft.Json
// NuGet commands
// dotnet add package Serilog
// dotnet restore
// dotnet build
// dotnet run
// dotnet publish -c Release -r linux-x64 --self-containedYou may be used to different syntax or behavior.
Uppercase = exported (public); lowercase = unexported — no public/internal/private keywords
You may be used to different syntax or behavior.
go.mod is the C# .csproj equivalent; go.sum is the lockfile
You may be used to different syntax or behavior.
Go compiles to a single static binary — no .NET runtime needed on target machine
You may be used to different syntax or behavior.
Cross-compilation: GOOS=linux GOARCH=amd64 go build — no equivalent dotnet command
You may be used to different syntax or behavior.
Package path = module path + directory, not C#'s namespace which is arbitrary
Step-by-Step Breakdown
1. Export by Capitalization
In Go, there are no access modifiers. Capitalize to export — applies to functions, types, constants, struct fields.
public class Calc {
public int Add(int a, int b) => a + b;
private int Helper() => 0;
}package calc
func Add(a, b int) int { return a + b } // exported
func helper() int { return 0 } // unexported2. go.mod vs .csproj
go.mod declares the module path (used in import paths) and dependencies with pinned versions. go.sum is the lockfile.
<PackageReference Include="Serilog" Version="3.1.1" />
dotnet add package Serilog// go.mod
require github.com/rs/zerolog v1.31.0
// Commands
go get github.com/rs/zerolog@latest
go mod tidy // remove unused
cat go.sum // lockfile — commit it3. Single Binary Deployment
go build produces a self-contained binary with no runtime dependency. Cross-compilation is built-in with GOOS/GOARCH env vars.
dotnet publish -c Release -r linux-x64 --self-contained# Build for current OS
go build -o app .
# Cross-compile for Linux
GOOS=linux GOARCH=amd64 go build -o app-linux .
# Cross-compile for Windows
GOOS=windows go build -o app.exe .4. internal Package
Go's 'internal' directory restricts packages to the parent module only — equivalent to C# internal access modifier.
// C# internal class
internal class Helper { }// app/internal/db/db.go
package db
// Can only be imported by code in app/ or its subdirectories
// Prevents external packages from using app/internal/dbCommon Mistakes
When coming from C#, developers often make these mistakes:
- Uppercase = exported (public); lowercase = unexported — no public/internal/private keywords
- go.mod is the C# .csproj equivalent; go.sum is the lockfile
- Go compiles to a single static binary — no .NET runtime needed on target machine
Key Takeaways
- Uppercase exports, lowercase doesn't — no access modifier keywords
- go.mod = .csproj; go get = dotnet add package; go mod tidy = remove unused
- Single static binary — no .NET runtime needed; built-in cross-compilation with GOOS/GOARCH
- internal/ directory restricts package access — Go's equivalent of C# internal access