Standard library tour
Go Language › 11 Standard library tour
Go's standard library is one of the language's strongest features — it includes a production-grade HTTP server, a JSON encoder/decoder, TLS support, compression, cryptography, templating, testing, profiling, and more. Many services never need third-party code.
This tour covers the packages you will reach for most often: net/http, io, encoding, flag, time, os, and testing. Each is designed to compose with the others through shared interfaces — most notably io.Reader and io.Writer.
net/http — HTTP client and server
The net/http package provides both a server (http.Server) and a client (http.Client). The core abstraction is the http.Handler interface:
type Handler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
}Server
mux := http.NewServeMux()
mux.HandleFunc("GET /api/users", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`[{"id":1,"name":"Alice"}]`))
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Fatal(server.ListenAndServe())Key points:
ServeMuxsupports method-based routing ("GET /path","POST /path") since Go 1.22.http.HandlerFuncadapts any function with the right signature tohttp.Handler.- Timeouts prevent resource leaks — always set them in production.
ListenAndServeTLSserves HTTPS with a certificate.
Handler middleware pattern
func Logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start))
})
}
server.Handler = Logging(mux)Client
client := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
MaxIdleConnsPerHost: 10,
},
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.example.com/users", nil)
if err != nil { log.Fatal(err) }
req.Header.Set("Authorization", "Bearer token")
resp, err := client.Do(req)
if err != nil { log.Fatal(err) }
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)- Always set a
Timeouton the client — zero means no timeout. Transportcontrols connection pooling. Reuse a singlehttp.Clientacross your application.NewRequestWithContextsupports cancellation via context.defer resp.Body.Close()is required even if you do not read the body.
Default client vs custom client
The zero-value http.DefaultClient is convenient but has no timeout. For production code, always create a custom client with explicit timeouts and transport configuration.
io and io/fs — readers and writers
The io.Reader and io.Writer interfaces are the foundation of Go's I/O abstraction:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}Everything implements these: files, network connections, TLS tunnels, gzip streams, HTTP bodies, buffers, in-memory pipes. This composability means you can chain I/O operations without caring about the underlying data source.
io.Copy
// Copy from HTTP response to a file
resp, _ := http.Get("https://example.com/file.zip")
defer resp.Body.Close()
f, _ := os.Create("file.zip")
defer f.Close()
written, err := io.Copy(f, resp.Body)
// written is int64 — bytes copiedio.Copy reads from the source and writes to the destination in a 32 KB buffer. It is the most common I/O utility in Go.
io.MultiReader and io.LimitReader
// Concatenate multiple readers into one stream
r := io.MultiReader(
strings.NewReader("header\n"),
fileReader,
strings.NewReader("footer\n"),
)
// Read at most N bytes
limited := io.LimitReader(resp.Body, 10*1024*1024) // 10 MB max
data, _ := io.ReadAll(limited)MultiReaderconcatenates readers sequentially.LimitReaderreturns anio.Readerthat reads at most N bytes — useful for limiting input sizes.io.ReadAllreads until EOF (Go 1.16+) — replacesioutil.ReadAll.
io/fs (Go 1.16+)
// Walk a directory
fsys := os.DirFS("/path/to/templates")
files, _ := fs.ReadDir(fsys, ".")
for _, f := range files {
if !f.IsDir() {
data, _ := fs.ReadFile(fsys, f.Name())
fmt.Printf("%s: %d bytes\n", f.Name(), len(data))
}
}io/fs abstracts a filesystem — os.DirFS, embed.FS, and testing/fstest all implement it.
encoding — JSON, CSV, XML, and GOB
encoding/json
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email,omitempty"`
}
// Marshal — struct to JSON bytes
users := []User{{ID: 1, Name: "Alice", Email: "alice@example.com"}}
data, err := json.MarshalIndent(users, "", " ")
// Unmarshal — JSON bytes to struct
var decoded []User
err = json.Unmarshal(data, &decoded)
// Streaming decoder (for large inputs)
decoder := json.NewDecoder(resp.Body)
for decoder.More() {
var u User
if err := decoder.Decode(&u); err != nil {
break
}
fmt.Printf("%+v\n", u)
}- Always prefer struct tags to control field names — never rely on default field matching.
MarshalIndentproduces human-readable output;Marshalis more compact.Decoderreads from a stream,Encoderwrites to a stream — both avoid loading the entire payload into memory.
encoding/csv
f, _ := os.Open("data.csv")
defer f.Close()
r := csv.NewReader(f)
records, _ := r.ReadAll() // [][]string
for _, row := range records {
fmt.Println(row[0], row[1])
}
// Writing CSV
w := csv.NewWriter(os.Stdout)
w.Write([]string{"id", "name", "email"})
w.Write([]string{"1", "Alice", "alice@example.com"})
w.Flush()encoding/xml
Similar to JSON but with XML-specific features like namespaces:
type Doc struct {
XMLName xml.Name `xml:"document"`
Title string `xml:"title"`
Items []Item `xml:"items>item"`
}encoding/gob
Go's own binary encoding format — efficient, reflection-based, Go-only:
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
enc.Encode(someStruct)
dec := gob.NewDecoder(&buf)
var decoded MyStruct
dec.Decode(&decoded)Gob is used internally by net/rpc and is the most efficient wire format if both ends are Go. It is not suitable for cross-language interop.
flag — command-line parsing
var (
addr = flag.String("addr", ":8080", "server address")
debug = flag.Bool("debug", false, "enable debug logging")
dbPath = flag.String("db", "./data.db", "database path")
)
func main() {
flag.Parse()
fmt.Printf("Starting server on %s (debug=%v)\n", *addr, *debug)
}Positional arguments
flag.Parse()
args := flag.Args() // remaining positional arguments
if len(args) < 1 {
fmt.Fprintf(os.Stderr, "usage: myapp <command> [args]\n")
os.Exit(1)
}Custom flag types
type DurationFlag time.Duration
func (d *DurationFlag) Set(s string) error {
v, err := time.ParseDuration(s)
*d = DurationFlag(v)
return err
}
var timeout DurationFlag
flag.Var(&timeout, "timeout", "duration (e.g. 30s, 5m)")Alternatives
The standard flag package follows POSIX conventions with -- flags. The pflag package (used by Kubernetes) provides full POSIX/GNU-style flag support including - short flags and --flag=value syntax.
time — durations and formatting
// Duration (int64 nanoseconds)
d := 5 * time.Second
fmt.Println(d) // "5s"
fmt.Println(d.Seconds()) // 5
// Time
now := time.Now()
t := time.Date(2026, 7, 8, 12, 0, 0, 0, time.UTC)
// Formatting — reference time is Mon Jan 2 15:04:05 MST 2006
fmt.Println(t.Format(time.RFC3339)) // "2026-07-08T12:00:00Z"
fmt.Println(t.Format("2006-01-02 15:04")) // "2026-07-08 12:00"
// Time operations
fmt.Println(t.Add(24 * time.Hour)) // next day
fmt.Println(t.Sub(now)) // duration
fmt.Println(t.Before(now)) // true if t < now
// Timers and tickers
timer := time.NewTimer(5 * time.Second)
<-timer.C // blocks until 5 seconds
ticker := time.NewTicker(1 * time.Second)
for range ticker.C {
fmt.Println("tick")
}Key reference format: Go uses Mon Jan 2 15:04:05 MST 2006 as the canonical reference time. The specific digits matter — 01 is month, 02 is day, 03/15 is hour, 04 is minute, 05 is second, 2006 is year, MST is timezone.
os — file I/O and OS interaction
File operations (Go 1.16+)
// Read entire file into memory
data, err := os.ReadFile("config.json")
// Write entire file
err = os.WriteFile("output.json", data, 0644)
// Open and Close
f, err := os.Open("file.txt")
defer f.Close()
// Create (truncates if exists)
f, err := os.Create("newfile.txt")
defer f.Close()Stat and file info
info, err := os.Stat("file.txt")
fmt.Println(info.Name(), info.Size(), info.Mode(), info.ModTime())
// "file.txt 1024 -rw-r--r-- 2026-07-08 12:00:00"
if os.IsNotExist(err) {
fmt.Println("file does not exist")
}Signal handling
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
go func() {
s := <-sig
fmt.Printf("received signal %v, shutting down\n", s)
os.Exit(0)
}()signal.NotifyContext (Go 1.16+) combines signal handling with context:
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
// Use ctx for graceful shutdown
server.Shutdown(ctx)Discovering packages
# From the command line
go doc net/http # full documentation
go doc net/http.Server # specific type
go doc net/http.Client.Timeout # specific field
# Web
pkg.go.dev # official package discovery
godoc.org # redirects to pkg.go.devThe standard library is fully documented on pkg.go.dev with examples, source code links, and sub-package listings. Every public type, function, and method has a doc comment — this is enforced by the Go project's contribution guidelines.
Finding the right package
# Search standard library packages
go doc -src net/http.ServeMux
# List all packages in a module
go list std | grep encoding
# encoding/base64, encoding/binary, encoding/csv, encoding/gob,
# encoding/hex, encoding/json, encoding/pem, encoding/xmlLinks to existing tiers
The Advanced tier CI/CD page uses os/exec to run build commands and flag for configuration. The Beginner tier uses net/http for its API server, encoding/json for response encoding, and os.ReadFile/os.WriteFile for file persistence. The Intermediate tier's graceful shutdown pattern uses signal.NotifyContext.