Go/
Lesson

Go's net/http package is production-grade out of the box. DockerWhat is docker?A tool that packages your application and all its dependencies into a portable container that runs identically on any machine., Kubernetes, and countless microservicesWhat is microservices?An architecture where an application is split into small, independently deployed services that communicate over the network, each owning its own data. run on it without any framework. When you ask AI to build an 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. server, it generates working code fast, but with subtle security gaps that won't show up until production traffic hits.

Your job: recognize what's missing from AI-generated servers.

The AI-generated server vs the production server

When you ask AI to build a server, you get this:

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

This works for demos. Here's what production needs:

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("GET /health", healthHandler)
    mux.HandleFunc("GET /api/users/{id}", getUser)
    mux.HandleFunc("POST /api/users", createUser)

    server := &http.Server{
        Addr:         ":8080",
        Handler:      mux,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  120 * time.Second,
    }

    log.Printf("Server starting on %s", server.Addr)
    log.Fatal(server.ListenAndServe())
}
AI pitfall
AI uses http.ListenAndServe(":8080", nil) which has zero timeouts. A slow client can hold a connection open forever, eventually exhausting your server's file descriptors. Always use http.Server{} with explicit timeouts. This is the single most common security gap in AI-generated Go servers.

What each timeout prevents

TimeoutDefaultWhat it prevents
ReadTimeoutNone (infinite)Slowloris attacks, slow request sends
WriteTimeoutNone (infinite)Clients that never read the response
IdleTimeoutNone (infinite)Connections held open doing nothing
ReadHeaderTimeoutNoneSlow header sends (more targeted than ReadTimeout)
02

Routing with Go 1.22+

Since Go 1.22, the built-in router handles method matching and path parameters. This is what AI should generate:

mux := http.NewServeMux()

// Method-specific routes
mux.HandleFunc("GET /api/users", listUsers)
mux.HandleFunc("POST /api/users", createUser)
mux.HandleFunc("GET /api/users/{id}", getUser)
mux.HandleFunc("PUT /api/users/{id}", updateUser)
mux.HandleFunc("DELETE /api/users/{id}", deleteUser)

// Wildcard (catch remaining path)
mux.HandleFunc("GET /files/{path...}", serveFile)

Extracting path parameters

func getUser(w http.ResponseWriter, r *http.Request) {
    idStr := r.PathValue("id")

    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, "Invalid user ID", http.StatusBadRequest)
        return
    }
    // ... fetch user by id
}
AI pitfall
AI sometimes generates pre-1.22 routing patterns, a single handler with switch r.Method and manual path parsing. If you're on Go 1.22+, this is unnecessary complexity. Tell AI your Go version explicitly.
03

Request handling patterns

Reading query parameters

func searchHandler(w http.ResponseWriter, r *http.Request) {
    q := r.URL.Query().Get("q")
    if q == "" {
        http.Error(w, "Missing search query", http.StatusBadRequest)
        return
    }

    page := r.URL.Query().Get("page")
    if page == "" {
        page = "1"
    }
}

Reading JSONWhat is json?A text format for exchanging data between systems. It uses key-value pairs and arrays, and every programming language can read and write it. request bodies

func createUser(w http.ResponseWriter, r *http.Request) {
    // ALWAYS limit body size
    r.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1MB

    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }

    if user.Name == "" || user.Email == "" {
        http.Error(w, "name and email required", http.StatusBadRequest)
        return
    }

    // ... create user
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(user)
}
AI pitfall
AI puts defer r.Body.Close() in HTTP handlers. The server automatically closes request bodies, you don't need to, and it's not wrong, but it's a sign AI is confusing client-side patterns (where you DO close resp.Body) with server-side patterns.
04

Writing responses

MethodPurposeOrder matters?
w.Header().Set(k, v)Set response headerMust be before WriteHeader/Write
w.WriteHeader(code)Set status codeMust be before Write
w.Write([]byte)Write body bytesImplicitly sends 200 if no WriteHeader
json.NewEncoder(w).Encode(v)Write JSON bodyCalls Write internally
http.Error(w, msg, code)Send error responseSets Content-Type to text/plain
AI pitfall
AI sometimes calls w.WriteHeader() after w.Write() or after json.NewEncoder(w).Encode(). Once you write to the body, the status code is already sent (defaults to 200). The second WriteHeader call logs a warning and is ignored. Set headers and status code BEFORE writing the body.
05

MiddlewareWhat is middleware?A function that runs between receiving a request and sending a response. It can check authentication, log data, or modify the request before your main code sees it.

Middleware in Go is a function that takes an http.Handler and returns an http.Handler. No framework magic needed.

func loggingMiddleware(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 %v", r.Method, r.URL.Path, time.Since(start))
    })
}

func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")

        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }
        next.ServeHTTP(w, r)
    })
}

func recoveryMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("panic: %v", err)
                http.Error(w, "Internal Server Error", 500)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

Chaining middleware

// Order matters: outermost runs first
var handler http.Handler = mux
handler = loggingMiddleware(handler)
handler = corsMiddleware(handler)
handler = recoveryMiddleware(handler) // Outermost - catches all panics
AI pitfall
AI generates CORS middleware with Access-Control-Allow-Origin: * which allows any domain. For production APIs with credentials (cookies, auth headers), you need to whitelist specific origins. The wildcard also doesn't work with credentials: 'include' on the frontend, the browser blocks it.
06

Complete production server pattern

When you ask AI to build a RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses., evaluate the result against this pattern:

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("GET /api/users", listUsers)
    mux.HandleFunc("POST /api/users", createUser)
    mux.HandleFunc("GET /api/users/{id}", getUser)

    handler := recoveryMiddleware(loggingMiddleware(corsMiddleware(mux)))

    server := &http.Server{
        Addr:         ":8080",
        Handler:      handler,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  120 * time.Second,
    }

    log.Fatal(server.ListenAndServe())
}

Production checklist for AI-generated servers

CheckWhy it matters
Uses http.Server{} with timeoutsPrevents connection exhaustion
Body size limited with MaxBytesReaderPrevents memory exhaustion
Recovery middleware installedPrevents one panic from killing server
CORS configured for specific originsPrevents credential leakage
JSON errors return proper status codesPrevents silent failures
Path parameters validated and convertedPrevents injection / zero-value bugs