Go/
Lesson

Go function syntax is straightforward enough that AI handles it well. The interesting part is not how to write functions, it's understanding the design constraints AI-generated Go code operates under. No overloading. No default parameters. No exceptions. These constraints shape every function AI writes for you.

Function declarations

When you ask AI to write a Go function, it will generate something like this:

func greet(name string) string {
    return "Hello, " + name + "!"
}

func add(a int, b int) int {
    return a + b
}

// Shorthand when params share a type
func multiply(a, b int) int {
    return a * b
}

Types come after parameter names. This reads naturally as speech: "greet takes a name of type string and returns a string." AI gets this syntax right almost every time, it's not where mistakes happen.

AI pitfall
When you ask AI to port a function from Python or JavaScript to Go, it sometimes tries to use optional parameters or default values. Go doesn't have either. If AI generates func connect(host string, port int = 8080), that won't compile. The Go patterns for optional configuration are option structs or functional options, covered later.
02

Multiple return values

This is where Go diverges sharply from most languages. Functions routinely return two or more values, and this is the mechanism for error handling:

func divide(dividend, divisor float64) (float64, error) {
    if divisor == 0 {
        return 0, errors.New("cannot divide by zero")
    }
    return dividend / divisor, nil
}

func main() {
    result, err := divide(10, 3)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Printf("Result: %.2f\n", result)
}

The result, err := pattern is everywhere. Every file operation, network call, and type conversion in 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. follows it. When you read AI-generated Go, you'll see this pattern dozens of times per file.

Return patternWhen it's usedExample signature
Single valuePure computation, no failure modefunc add(a, b int) int
Value + errorAnything that can failfunc readFile(path string) ([]byte, error)
Multiple valuesRelated data that belongs togetherfunc bounds(s []int) (min, max int)
Bool + valueMap-style lookupsfunc lookup(key string) (string, bool)
03

Named returns and naked returns

AI loves generating naked returns because they save keystrokes. You need to recognize when this hurts readability:

// Named returns - the names document what comes back
func calculateStats(numbers []int) (sum int, avg float64, count int) {
    count = len(numbers)
    if count == 0 {
        return // naked return: returns 0, 0.0, 0
    }
    for _, n := range numbers {
        sum += n
    }
    avg = float64(sum) / float64(count)
    return // naked return: returns sum, avg, count
}

Named returns are useful for documentation, you can see what the function returns without reading the body. But naked returns (bare return without values) make it hard to trace what's actually being returned, especially in longer functions with multiple exit points.

AI pitfall
AI frequently generates naked returns in functions over 15 lines. This makes code review painful because you have to track which named variables got modified before each return. If AI gives you a long function with naked returns, ask it to make the returns explicit: return sum, avg, count.
FeatureGood forBad for
Named returnsShort functions, self-documenting signaturesLong functions with multiple return paths
Naked returnsTrivial 3-5 line functionsAnything complex, hurts readability
Explicit returnsEverything, always safeNothing, always acceptable
04

Variadic functions

The ... syntax lets functions accept variable numbers of arguments:

func sum(numbers ...int) int {
    total := 0
    for _, n := range numbers {
        total += n
    }
    return total
}

func main() {
    fmt.Println(sum(1, 2, 3))    // 6
    fmt.Println(sum(10, 20))     // 30
    fmt.Println(sum())           // 0

    // Spread a slice into variadic args
    nums := []int{5, 10, 15}
    fmt.Println(sum(nums...))    // 30
}

Inside the function, numbers is a []int slice. The ... in the call site (nums...) unpacks a slice into individual arguments.

AI pitfall
AI sometimes generates sum(nums) instead of sum(nums...) when passing a slice to a variadic function. This is a type mismatch, the function expects individual ints, not a slice. The compiler will catch it, but knowing why it fails saves debugging time.
05

No overloading, no defaults, by design

Coming from Python or TypeScript, the lack of function overloading and default parameters feels restrictive. But it's intentional. Go's alternatives are more explicit:

// Instead of default parameters, use an options struct
type ServerConfig struct {
    Host    string
    Port    int
    Timeout time.Duration
}

func NewServer(cfg ServerConfig) *Server {
    if cfg.Host == "" {
        cfg.Host = "localhost"
    }
    if cfg.Port == 0 {
        cfg.Port = 8080
    }
    if cfg.Timeout == 0 {
        cfg.Timeout = 30 * time.Second
    }
    return &Server{config: cfg}
}

When AI generates Go code, evaluate whether it chose the right pattern for configurability. If it generates a function with 6+ parameters, that's a signal it should use an options struct instead.

"I want..."Python/JS approachGo approach
Optional paramsdef f(x, y=10):Options struct or functional options
Different param typesFunction overloadingDifferent function names or interfaces
Variable args*args, **kwargs...Type (variadic), options struct
06

Pass by value

Go functions receive copies of their arguments. If you pass a struct and modify it inside the function, the original is unchanged:

type Point struct{ X, Y int }

func moveRight(p Point) {
    p.X += 10 // modifies the copy, not the original
}

func moveRightPtr(p *Point) {
    p.X += 10 // modifies the original through pointer
}
AI pitfall
AI sometimes forgets to use pointer receivers or pointer parameters when a function needs to modify its input. If AI generates a function that takes a struct by value and modifies it, the caller won't see the changes. Look for this mismatch when AI-generated code "should work but doesn't."