Go Getting Started - Setup & Hello World
For developers with .NET (C#) experience, this guide covers installing Go, the go command, editor setup, and writing your first program.
1. Installing Go
Official installer
Download an installer or archive for your OS from go.dev/dl.
# Linux (extracting the archive; adjust the version as needed)
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.xx.x.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
# macOS (Homebrew)
brew install go
# Windows (winget)
winget install GoLang.Go
Verify the installation:
go version
go env GOPATH GOROOT GOOS GOARCH
Version management
The equivalent of .NET's global.json is the go / toolchain directives in go.mod. Since Go 1.21, if go.mod requires a newer version than the one installed, Go automatically downloads and uses the required toolchain (controlled by the GOTOOLCHAIN environment variable).
module example.com/hello
go 1.24
To switch between multiple versions manually, use the official golang.org/dl or a version manager such as mise or asdf.
2. .NET Mapping
| .NET (C#) | Go |
|---|---|
dotnet new console | go mod init <module> + create main.go |
dotnet run | go run . |
dotnet build | go build |
dotnet publish | go build / go install (output is a single binary) |
dotnet add package | go get <module> |
dotnet restore | go mod download (usually automatic) |
dotnet format | gofmt / go fmt ./... |
| Roslyn analyzers | go vet ./... / golangci-lint |
dotnet test | go test ./... |
global.json | go / toolchain in go.mod |
| Visual Studio / Rider / C# Dev Kit | VS Code + gopls / GoLand |
3. Key go Commands
| Command | Purpose |
|---|---|
go mod init <module> | Create go.mod and initialize a module |
go run . | Compile the current package and run it immediately (output goes to a temp directory) |
go build | Compile and produce an executable |
go install | Build and place the result in $GOPATH/bin (or GOBIN). Also used to install CLI tools |
go fmt ./... | Format code with the standard formatter (gofmt) |
go vet ./... | Detect suspicious code through static analysis |
go test ./... | Run tests |
go mod tidy | Remove unused dependencies and add missing ones |
go doc <pkg> | Read documentation in the terminal |
./... is a wildcard meaning "all packages under the current directory."
4. Editor Setup
VS Code
- Install the Go extension (Go Team at Google)
- Run
Go: Install/Update Toolsfrom the command palette to installgopls(language server),dlv(debugger), and others
Recommended settings (settings.json):
{
"go.formatTool": "gofmt",
"editor.formatOnSave": true,
"[go]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
}
}
Formatting and import organization run automatically on save. In Go, even the ordering of import statements is decided by tooling.
GoLand
A commercial IDE from JetBrains with a feel close to Rider. Go SDK detection, debugger, and refactoring work right after installation.
5. Hello World
Create the project
mkdir hello && cd hello
go mod init example.com/hello
This generates go.mod. It is similar to a .csproj, but you do not list files; all .go files in the directory are included automatically.
main.go
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!")
}
Run and build
go run . # run
go build -o hello . # produce a binary (hello.exe on Windows)
./hello # run the generated binary
Comparison with C#
// Program.cs (top-level statements)
Console.WriteLine("Hello, C#!");
// main.go
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!")
}
- Go has no top-level statements, so
package mainandfunc main()are required. - An executable program is a
package mainthat contains amainfunction. Any other package is a library. - Importing a package without using it is a compile error.
A slightly more practical example
This example takes an argument and handles errors.
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage: hello <count>")
os.Exit(1)
}
n, err := strconv.Atoi(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "invalid count: %v\n", err)
os.Exit(1)
}
for i := range n {
fmt.Printf("Hello #%d\n", i+1)
}
}
go run . 3
for i := range n (range over an integer) is available in Go 1.22 and later.
Using an external package
go get github.com/google/uuid
package main
import (
"fmt"
"github.com/google/uuid"
)
func main() {
fmt.Println(uuid.NewString())
}
go get updates go.mod and records checksums in go.sum. Commit both go.mod and go.sum. go.sum plays a role similar to packages.lock.json.
6. Common Pitfalls for C# Developers
go runworks per package, not per file:go run main.goworks, but once the code is split into several files, usego run ..- You do not need to think about GOPATH: It used to be required to place source under
GOPATH, but with Go Modules you can create a project in any directory.GOPATHis now mainly used for the downloaded module cache ($GOPATH/pkg/mod) and install location ($GOPATH/bin). - Installed tools are not found: Commands installed with
go installland in$(go env GOPATH)/bin. Add that directory to yourPATH. - Do not debate formatting: Indent with tabs and put opening braces at the end of the line. The output of
gofmtis the standard, and there are no settings. - Brace position is part of the grammar: Putting
{on a new line afterfunc main()is a compile error (because of automatic semicolon insertion). - Unused variables and imports are errors: Explicitly discard a variable you do not need yet with
_ = xor_. - Only
package mainis executable: Runninggo buildon a library does not produce a binary. It only type-checks. - No
bin/andobj/clutter:go buildwrites the executable to the current directory (changeable with-o). Add it to.gitignore.
7. Summary
- Installation is done with the official installer alone, and
go/toolchainingo.modpins the version (Go 1.21+ automatically fetches the required toolchain). go mod init→go run .→go buildis the basic flow, equivalent todotnet new→dotnet run→dotnet publish.- VS Code + gopls provides format-on-save, import organization, completion, and debugging.
- Next, move on to Modules and Packages and Basic Syntax.