Go ships with over 150 packages. When you ask AI to build something in Go, it reaches for the standard libraryWhat is standard library?A collection of ready-made tools that come built into a language - no install required. Covers common tasks like reading files or making web requests. first, and that's exactly right. Unlike Node's "install a package for everything" culture, Go's stdlib handles most real-world needs out of the box.
This matters for you as an AI-assisted builder: stdlib code is what AI generates most reliably. The patterns are consistent, well-documented, and deeply embedded in training data.
Formatted I/O with fmt
When AI generates Go code, fmt appears in nearly every file. The package handles printing, string formatting, and scanning input.
AI will generate something like:
package main
import "fmt"
func main() {
name := "Alice"
age := 30
// Printf for formatted output
fmt.Printf("Name: %s, Age: %d\n", name, age)
// Sprintf returns a string instead of printing
message := fmt.Sprintf("Hello, %s!", name)
// Println for quick debug output
fmt.Println(message)
}Format verbs you need to recognize
| Verb | What it does | When you'll see it |
|---|---|---|
%v | Default format | Printing any value, AI's go-to |
%+v | Struct with field names | Debugging structs |
%#v | Go-syntax representation | Deep debugging |
%T | Type of the value | Type inspection |
%d | Decimal integer | Numbers |
%s | String | Strings |
%q | Quoted string | Logging, JSON debugging |
%f | Float | Decimal numbers |
%p | Pointer address | Memory debugging |
%w | Wrap error (Errorf only) | Error chains |
fmt.Sprintf to concatenate strings like fmt.Sprintf("%s%s", a, b). This is slower than a + b or strings.Builder for simple concatenation. When you see AI reaching for Sprintf without format verbs, simplify it.File and OS operations
The os package is how Go talks to the operating system. When you ask AI to read a file, write output, or check environment variables, it uses os.
AI will generate something like:
// Read entire file into memory
data, err := os.ReadFile("config.json")
if err != nil {
log.Fatal(err)
}
// Write to file (creates or truncates)
err = os.WriteFile("output.txt", []byte("Hello"), 0644)
// Check if file exists
if _, err := os.Stat("data.txt"); os.IsNotExist(err) {
fmt.Println("File does not exist")
}
// Environment variables
apiKey := os.Getenv("API_KEY")os.ReadFile for everything. It loads the entire file into memory. For a 2GB log file, that's 2GB of RAM. Ask AI specifically for streaming with bufio.Scanner when files could be large. AI won't think about file size unless you tell it to.The io interfaces: Reader and Writer
io.Reader and io.Writer are the most important interfaces in Go. Files, network connections, HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. bodies, buffers, they all implement these interfaces. This is Go's composition model in action.
// io.Copy works with ANY Reader and Writer
// File to stdout? Works.
// HTTP body to file? Works.
// String to network connection? Works.
reader := strings.NewReader("Hello, World!")
_, err := io.Copy(os.Stdout, reader)| Function | What it does | When to use |
|---|---|---|
io.Copy(dst, src) | Stream from Reader to Writer | Large data, network I/O |
io.ReadAll(r) | Read everything into []byte | Small, known-size data |
io.LimitReader(r, n) | Cap reads at n bytes | Untrusted input (HTTP bodies) |
io.TeeReader(r, w) | Read and write simultaneously | Logging request bodies |
io.ReadAll everywhere, it's the fs.readFileSync of Go. For HTTP response bodies from untrusted sources, always wrap with io.LimitReader first. AI never adds size limits unprompted.String manipulation
The strings package is comprehensive. AI knows it well and generates correct code almost every time.
text := " Hello, World! "
strings.TrimSpace(text) // "Hello, World!"
strings.ToUpper("hello") // "HELLO"
strings.Split("a,b,c", ",") // ["a", "b", "c"]
strings.Join([]string{"a", "b"}, "-") // "a-b"
strings.Contains("hello", "ell") // true
strings.HasPrefix("http://x", "http") // true
strings.ReplaceAll("foo bar foo", "foo", "z") // "z bar z"String conversion with strconv
Converting between strings and numbers is explicit in Go, no implicit coercion.
// String to int (returns error if invalid)
num, err := strconv.Atoi("42")
// Int to string
str := strconv.Itoa(42)
// Float parsing
f, err := strconv.ParseFloat("3.14", 64)
// Bool parsing
b, err := strconv.ParseBool("true")strconv.Atoi. In HTTP handlers, that means a malformed query parameter like ?page=abc causes a silent zero value instead of a 400 error. Always check conversion errors on user input.Working with time
Go's time package has one infamous quirk: the reference time. Instead of YYYY-MM-DD format strings, Go uses a specific reference date: Mon Jan 2 15:04:05 MST 2006. The numbers are chosen to be sequential (1, 2, 3, 4, 5, 6, 7) which makes them mnemonic but confusing at first.
now := time.Now()
// Format using the reference time
fmt.Println(now.Format("2006-01-02 15:04:05"))
// Durations
duration := 2*time.Hour + 30*time.Minute
future := now.Add(24 * time.Hour)
// Measuring elapsed time
start := time.Now()
doWork()
elapsed := time.Since(start)| Format pattern | Output example | When used |
|---|---|---|
2006-01-02 | 2024-03-15 | Date only |
15:04:05 | 14:30:00 | Time only |
time.RFC3339 | 2024-03-15T14:30:00Z | APIs, JSON |
Monday, January 2 | Friday, March 15 | Display |
3:04 PM | 2:30 PM | 12-hour format |
time.Parse("YYYY-MM-DD", dateStr), that's not Go syntax, that's Python/JS syntax leaking through. Go uses the reference time 2006-01-02. If you see any format string that isn't based on the reference time, the code is wrong.Logging and CLIWhat is cli?Short for Command Line Interface. A tool you use by typing commands in the terminal instead of clicking buttons. basics
Go's log package adds timestamps automatically. For CLI tools, combine with flag for argument parsing.
// Logging
log.Println("Server started") // 2024/03/15 14:30:00 Server started
log.Fatalf("Cannot connect: %v", err) // Logs and exits with status 1
// CLI flags
port := flag.Int("port", 8080, "Server port")
verbose := flag.Bool("verbose", false, "Verbose output")
flag.Parse()
fmt.Println(*port) // Note: flag returns pointerslog.Fatal inside HTTP handlers. Fatal calls os.Exit(1), which kills the entire server process, not just the request. In handlers, return an error or use http.Error() instead. log.Fatal belongs only in main() during startup.