Frontend Labs
⋯

Phase 1: Go Fundamentals (Days 1–30)

Phase Goal: Go from "I can read Go" to "I can write Go without looking things up." This is the foundation. Don't rush it.

Week 1: Go Syntax, Types & Functions (Days 1–7)

Why this week matters: Every Go service you'll build — microservice, proxy, security middleware — starts here. Writing and reviewing Go confidently requires fluency in these basics.


Day 1 — Go toolchain & your first program

  • Why: Understanding go build, go run, go test, go mod is what you'll reference when engineers say "it works locally"
  • Topic: Install Go, workspace setup, go mod init, package main, func main(), fmt.Println
  • Exercise: Write a program that prints a formatted system info report: current time, hostname (os.Hostname), number of CPUs (runtime.NumCPU), Go version (runtime.Version)
  • Key packages: fmt, os, runtime, time

Day 2 — Variables, types & zero values

  • Why: Go's type system prevents a whole class of bugs you'd otherwise debug at runtime in dynamic languages
  • Topic: var, :=, basic types (string, int, bool, float64), zero values, type conversion, const, iota
  • Exercise: Build a unit converter: convert between MB/GB/TB, ms/s/min. Use typed constants with iota for units. No hardcoded magic numbers.

Day 3 — Functions & multiple return values

  • Why: Go's (value, error) return pattern is in every library call. Understanding it is essential for reading Go code
  • Topic: Function syntax, multiple return values, named returns, variadic functions, first-class functions
  • Exercise: Build a parseConfig(input string) (Config, error) function that parses key=value pairs. Return structured errors. Write 5 test cases with if err != nil handling.

Day 4 — Slices & maps

  • Why: These are the workhorse data structures in 90% of Go code. Misuse causes production memory leaks
  • Topic: Slice internals (len/cap), append, slice of slice, map operations, nil vs empty slice/map, iteration with range
  • Exercise: Build an in-memory request log: store last 1000 requests (IP, path, status, latency) in a slice with a fixed-size ring buffer. Query by IP using a map index.

Day 5 — Structs & methods

  • Why: Go uses structs + methods instead of classes. Every service, every config, every request object is a struct.
  • Topic: Struct definition, field tags, methods with value vs pointer receivers, struct embedding, anonymous fields
  • Exercise: Model a Server struct with config fields. Add methods: Start(), Stop(), Status() string. Use pointer receivers. Embed a Logger struct.

Day 6 — Interfaces

  • Why: Go interfaces enable the pluggable, testable architectures your best engineers design. Understanding them helps you spot good vs bad design in PRs
  • Topic: Interface definition, implicit satisfaction, interface{} (any), type assertions, type switches
  • Exercise: Define a Storage interface with Set(key, value string), Get(key string) (string, bool), Delete(key string). Implement it with an in-memory map. Write a CachedStorage that wraps any Storage and adds metrics.

Day 7 — Error handling

  • Why: Go's explicit error handling is why Go services tend to be more reliable. Knowing the patterns helps you require them in code review
  • Topic: error interface, errors.New, fmt.Errorf, errors.Is, errors.As, wrapping with %w, sentinel errors
  • Exercise: Build a multi-layer error system for the storage from Day 6: ErrNotFound, ErrStorageFull, ErrInvalidKey. Each wraps context. Write a handler that distinguishes each error type.

Week 1 Mini Project: Configuration Server

  • Build an HTTP server (net/http) that serves runtime config as JSON
  • Endpoints: GET /config (return all config), PUT /config/{key} (update a value), GET /health (return status + uptime)
  • Use a struct for config, interface for storage, proper error responses
  • Key packages: net/http, encoding/json, os, time
  • Engineer Takeaway: This is the skeleton of every internal CLI and admin tool. You can now choose deliberately between var and :=, and between pointer and value receivers.

Week 2: Control Flow, Packages & Testing (Days 8–14)

Why this week matters: Go's testing story is one of its strongest features. By end of week you'll write tests the way your senior engineers do.


Day 8 — Control flow

  • Why: Understanding defer explains a lot of "why does this cleanup always run?" patterns in Go servers
  • Topic: if/else, switch (no fallthrough by default), for (only loop in Go), break/continue, defer (LIFO, arguments evaluated immediately), goto (avoid)
  • Exercise: Write a request lifecycle simulator: defer cleanup functions in the right order (close body → release connection → log → decrement counter). Observe execution order.

Day 9 — Packages & modules

  • Why: go mod and package structure drive how Go code is organized. Knowing this lets you lay out a repo that scales past one package
  • Topic: Package naming, exported vs unexported, go.mod, go.sum, internal packages, init() function, circular imports (why Go forbids them)
  • Exercise: Refactor the Week 1 mini project into proper packages: config/, storage/, server/, health/. Each package has one clear responsibility. No circular dependencies.

Day 10 — Testing in Go

  • Why: Go has testing built in — no framework needed. Understanding go test helps you set coverage requirements
  • Topic: testing.T, table-driven tests, t.Run subtests, t.Helper, test file naming, -run flag, -v flag
  • Exercise: Write table-driven tests for the storage interface from Day 6. Cover: empty key, value too long, concurrent reads, delete non-existent key. Run with -v and -count=3.

Day 11 — Benchmarks & examples

  • Why: Benchmark-driven performance discussions beat intuition. Knowing how to read benchmarks changes how you evaluate "it's slow" reports
  • Topic: testing.B, b.N, b.ResetTimer, b.ReportAllocs, go test -bench, go test -benchmem, example functions
  • Exercise: Benchmark three implementations of the same lookup: linear scan slice, map lookup, sorted slice + binary search. Report ns/op and allocs/op. Which wins at 100 items? At 10,000?

Day 12 — Goroutines (introduction)

  • Why: Go's concurrency model is why Go handles high traffic efficiently. This is the single most important Go concept for a networking/security stack
  • Topic: go keyword, goroutine lifecycle, the fact that goroutines are not OS threads, runtime.NumGoroutine(), goroutine leaks
  • Exercise: Write a program that starts 1000 goroutines, each sleeping for a random duration. Count active goroutines with runtime.NumGoroutine() every 100ms. Observe the ramp-up and drain. Ensure all goroutines exit cleanly.

Day 13 — Channels

  • Why: Channels are how Go goroutines communicate safely. This pattern appears in every concurrent server you'll write
  • Topic: Make channel, send/receive, buffered vs unbuffered, close, range over channel, select, default
  • Exercise: Build a job queue: producer goroutine sends jobs into a buffered channel; 5 worker goroutines consume and process jobs; a results channel collects outcomes. Print summary when all done.

Day 14 — sync package

  • Why: sync.Mutex and sync.WaitGroup are in every Go service. Misuse causes deadlocks. Knowing them helps you spot issues in review
  • Topic: sync.Mutex, sync.RWMutex, sync.WaitGroup, sync.Once, sync.Map
  • Exercise: Implement a thread-safe request counter: tracks requests per endpoint, reads are frequent (use RWMutex), resets happen rarely. Write a concurrent test with 100 goroutines hammering it. Run with -race.

Week 2 Mini Project: Concurrent URL Checker

  • Build a tool that takes a list of URLs (from a file) and checks each one concurrently (HTTP GET, 5s timeout)
  • Worker pool: configurable concurrency (default: 10)
  • Results: status code, latency, error (if any), written to output file
  • Graceful: if any URL check takes > 30s total, cancel remaining
  • Key packages: net/http, sync, context, os, time
  • Engineer Takeaway: This pattern (worker pool + channel pipeline + context cancellation) is in every batch job, health checker, and data pipeline you'll write.

Week 3: Context, I/O & JSON (Days 15–21)

Why this week matters: context and encoding/json are in literally every production Go service. These are not optional topics.


Day 15 — context package

  • Why: Context is how timeouts and cancellations propagate through your entire stack — from load balancer to database query. It's why Go services can be reliably cancelled
  • Topic: context.Background(), context.TODO(), WithCancel, WithTimeout, WithDeadline, WithValue, ctx.Done(), ctx.Err()
  • Exercise: Build a request simulator with 3 stages (auth → fetch data → format response). Each stage has its own timeout. Cancel everything if any stage fails or the parent context is cancelled. Use select on ctx.Done().

Day 16 — io.Reader & io.Writer

  • Why: Every file read, HTTP body, network socket, and log writer in Go is an io.Reader or io.Writer. Composing them is fundamental
  • Topic: io.Reader, io.Writer, io.Copy, io.TeeReader, io.LimitReader, io.MultiWriter, io.Pipe, bufio.Reader/Writer
  • Exercise: Build a request logger that: reads an HTTP request body, logs it (via io.TeeReader), limits to 10KB (via io.LimitReader), and passes it downstream. Don't read the body twice.

Day 17 — encoding/json

  • Why: JSON is the wire format for APIs. Understanding Go's JSON encoder/decoder explains why engineers write struct tags the way they do
  • Topic: json.Marshal, json.Unmarshal, struct tags (json:"name,omitempty"), json.Decoder (streaming), json.Encoder, custom MarshalJSON/UnmarshalJSON, json.RawMessage
  • Exercise: Build a JSON API request/response pipeline: decode incoming JSON request (streaming, don't buffer full body), validate required fields, transform, encode response. Test with malformed JSON.

Day 18 — File I/O & os package

  • Why: Config files, log files, PID files — services interact with the filesystem constantly. Knowing the patterns prevents bugs
  • Topic: os.Open, os.Create, os.ReadFile, os.WriteFile, os.Stat, filepath, os.MkdirAll, os.TempDir, file permissions
  • Exercise: Build a rotating log writer: write to app-YYYY-MM-DD.log, create new file daily, keep last 7 files (delete older ones), concurrent-safe with sync.Mutex.

Day 19 — Strings & bytes

  • Why: Security-critical code manipulates strings constantly — URLs, headers, payloads. Knowing strings vs bytes prevents subtle bugs
  • Topic: strings package (Builder, Split, TrimSpace, Contains, HasPrefix), bytes package, string immutability, []byte conversions, strings.Builder for efficient concatenation
  • Exercise: Build a URL normalizer: lowercase scheme and host, remove default ports (:80, :443), normalize path (remove double slashes, resolve ./ and ../), sort query params. No regex yet.

Day 20 — regexp

  • Why: WAF rules and log parsers rely on regex. Understanding Go's regexp package performance (compiled vs interpreted) matters for high-throughput security middleware
  • Topic: regexp.MustCompile, regexp.Compile, FindString, FindAllString, ReplaceAll, SubexpNames, pre-compiling patterns, regexp.MatchString pitfall (re-compiles every call)
  • Exercise: Build a log line parser using named capture groups: parse nginx/Apache access log format ($ip - - [$time] "$method $path $proto" $status $bytes). Compile all patterns at startup.

Day 21 — time package

  • Why: Rate limiting, token expiry, metrics windows, log rotation — all time-dependent. Go's time package is rich and often misused
  • Topic: time.Now(), time.Duration, time.Since, time.Until, time.After, time.Tick (leak risk), time.NewTicker, time.NewTimer, time zones, time.Parse/Format
  • Exercise: Build a sliding-window rate limiter (purely time-based, no external state): each call checks if the count in the last N seconds exceeds a limit. Use time.NewTicker for cleanup. Thread-safe.

Week 3 Mini Project: Structured Request Logger

  • Build an HTTP middleware that logs every request as structured JSON to a rotating file
  • Log fields: timestamp, request ID (UUID-like, from crypto/rand), method, path, status, latency, client IP, user-agent, request size, response size
  • Context: inject request ID into context at entry, retrieve at response write
  • Rotation: new file daily, keep 14 days
  • Key packages: net/http, encoding/json, context, sync, time, os, crypto/rand
  • Engineer Takeaway: Audit logging is a compliance requirement. You now know what "structured logging" means in code, how to choose a log format, and why log rotation matters.

Week 4: Advanced Go Patterns (Days 22–30)

Why this week matters: These are the patterns that separate junior Go from senior Go. Knowing them helps you review architecture proposals.


Day 22 — Generics (Go 1.18+)

  • Why: Generics reduce code duplication in shared libraries. You'll write a generic cache or pool yourself, and know when generics are worth it
  • Topic: Type parameters, constraints, comparable, any, generic functions, generic types
  • Exercise: Build a generic RingBuffer[T] (fixed-size circular buffer). Use it with string (log entries) and int (metrics). Then build a generic Result[T] type wrapping (T, error).

Day 23 — sync.Pool & memory efficiency

  • Why: In high-traffic services (security proxies, WAF), allocations cause GC pressure. sync.Pool is how teams reduce that
  • Topic: sync.Pool, GC interaction with Pool, when to use (reusable buffers), when not to use (long-lived objects), bytes.Buffer pooling pattern
  • Exercise: Build a byte buffer pool. Write a benchmark comparing: (1) allocate new buffer each request, (2) use sync.Pool. Measure allocs/op and ns/op at 10k/req.

Day 24 — Functional patterns

  • Why: Go middleware chains, option functions, and builder patterns are functional patterns. They appear everywhere in idiomatic Go
  • Topic: Function types, middleware pattern (func(Handler) Handler), options pattern (func(*Config)), method chaining builders
  • Exercise: Build a configurable HTTP server using the options pattern: NewServer(WithPort(8080), WithTimeout(30*time.Second), WithTLS(certFile, keyFile)). No config struct in the constructor signature.

Day 25 — Embedding & composition

  • Why: Go uses composition over inheritance. Understanding embedding explains how interfaces get "extended" in Go
  • Topic: Struct embedding, interface embedding, method promotion, shadowing, embedding vs field
  • Exercise: Build a MeteredConn that embeds net.Conn and counts bytes read/written. Build a TimedHandler that embeds http.Handler and records latency. Both are transparent wrappers.

Day 26 — expvar & runtime metrics

  • Why: expvar is built-in observability. Every production Go binary should expose /debug/vars. Expose it in every service you ship
  • Topic: expvar.Int, expvar.Float, expvar.Map, expvar.String, /debug/vars HTTP endpoint, net/http/pprof
  • Exercise: Add expvar metrics to the Week 3 request logger: requests total, errors total, p50/p99 latency (ring buffer of last 1000 latencies), bytes served total. Expose at /debug/vars.

Day 27 — Graceful shutdown

  • Why: Abrupt server shutdowns drop in-flight requests. Every production HTTP server needs graceful shutdown. This is a common incident cause
  • Topic: os/signal, signal.NotifyContext, http.Server.Shutdown, sync.WaitGroup for in-flight tracking, shutdown timeout budget
  • Exercise: Build an HTTP server with 3-stage graceful shutdown: (1) stop accepting (10s), (2) wait for in-flight (30s), (3) force close. Test by sending requests mid-shutdown.

Day 28 — Profiling

  • Why: When engineers say "the service is slow," you need to know what a flame graph means and how to generate one
  • Topic: go tool pprof, CPU profile (-cpuprofile), heap profile (-memprofile), goroutine profile, net/http/pprof endpoints, reading flame graphs
  • Exercise: Add pprof endpoints to your server. Write a load generator (100 goroutines, 10s). Collect a CPU profile. Open in pprof. Find the hottest function. Explain what the flame graph shows.

Day 29 — Testing: httptest & net.Pipe

  • Why: Testing network code without real network calls is how you get fast, reliable CI
  • Topic: net/http/httptest.NewRecorder, httptest.NewServer, net.Pipe(), table-driven HTTP handler tests
  • Exercise: Write tests for the Week 3 middleware using httptest. Test: request logging, rotation trigger, concurrent requests, context cancellation mid-request. No real HTTP server.

Day 30 — Review & code audit

  • Review all Phase 1 code with fresh eyes
  • Apply: consistent error handling, no goroutine leaks (go test -race), defer cleanup, proper context propagation
  • Engineer Takeaway: You can now read production Go code comfortably, write tests, benchmark performance, and profile CPU/memory. You're ready for networking.

Week 4 Mini Project: In-Memory Metrics Server

  • Build a self-contained metrics collection + query server
  • Ingest: POST /metrics accepts {name, value, tags} JSON
  • Query: GET /metrics/{name}?window=60s returns min/p50/p95/p99/max over the time window
  • Storage: ring buffer per metric name, concurrent-safe
  • Observability: itself exposes /debug/vars and /debug/pprof
  • Graceful shutdown with drain
  • Key packages: net/http, encoding/json, sync, time, expvar, net/http/pprof
  • Engineer Takeaway: This is a miniature Prometheus. You now know how metrics systems work inside, and why they use ring buffers, not unbounded slices.

Generated from golang-90-day-plan.md · 8 Oct 2026 16:36