Everything in Go — building, testing, formatting, fetching dependencies — goes through one go command.
Go ships as a single toolchain: the compiler, linker, formatter, test runner and dependency manager are all subcommands of go. After installing from go.dev/dl, go version should print something like go1.23.4.
A program lives in a module, a directory with a go.mod file. Create one with go mod init example.com/hello; the path is just a name until you publish it. Put the code from the example in main.go, then:
go run .compiles to a temporary binary and runs it — the everyday edit-run loop.go buildproduces a real executable named after the module. It is statically linked, so you can copy it to another machine with the same OS/arch and run it. Cross-compile by settingGOOS=linux GOARCH=arm64 go build.go fmt ./...(orgofmt -w .) rewrites files into the one canonical style. Nobody argues about formatting in Go; editors run it on save.go vet ./...catches suspicious code the compiler allows, such as aPrintfverb that doesn't match its argument.go doc fmt.Printlnprints documentation from the terminal.
The pattern ./... means "this directory and every package below it" and works with most subcommands.
Go compiles fast enough that go run feels like a scripting language, but you get a native binary with no runtime to install. Note that the compiler is strict: an unused import or unused local variable is a compile error, not a warning — delete it or assign it to _.
Example
// main.go — run with: go run .
package main
import "fmt"
func main() {
fmt.Println("Hello, Go!") // Hello, Go!
}Remember
go mod init <path>starts a module;go run .builds and runs itgo buildmakes a static native binary; set GOOS/GOARCH to cross-compilego fmtandgo vetare part of the normal workflow
Watch out for
- Leaving an unused import or variable — Go refuses to compile it.