Go/
Lesson

Go fixed one of the oldest bugs in programming: accidental fall-through. In C, JavaScript, and Java, forgetting break in a switch means execution "falls through" to the next case. Go flipped the default. Cases break automatically. This is a small change that eliminates an entire category of bugs -- but it also means AI trained on those other languages generates incorrect Go switches.

Expression switches

When you ask AI to "handle different cases" or "dispatch based on a value," it generates this:

switch day {
case "Monday":
    fmt.Println("Start of the work week")
case "Tuesday", "Wednesday", "Thursday":
    fmt.Println("Midweek")
case "Friday":
    fmt.Println("Almost weekend!")
default:
    fmt.Println("Weekend!")
}

No break needed. Each case runs its block and exits the switch. Multiple values per case use commas.

AI pitfall
AI trained on C or JavaScript frequently adds break at the end of Go switch cases. It compiles fine (break is valid inside a switch), but it is unnecessary noise that signals the AI did not understand Go's switch semantics. Remove them.
02

fallthrough -- you almost never want it

Go makes fall-through explicit with the fallthrough keyword:

switch {
case number > 10:
    fmt.Println("Greater than 10")
    fallthrough
case number > 5:
    fmt.Println("Greater than 5")
    fallthrough
case number > 0:
    fmt.Println("Greater than 0")
}

If number is 15, this prints all three lines. Each fallthrough forces execution into the next case unconditionally -- it does not even check the next case's condition.

AI pitfall
AI sometimes uses fallthrough when it means "also check the next condition." But fallthrough skips the condition check entirely. If number is 15 and only the first case has fallthrough, it executes the second case's body even though number > 5 is not re-evaluated. The behavior is "execute the next case body, period." This catches AI off guard when it tries to simulate C-style fall-through logic.

When fallthrough is actually useful

Almost never in practice. In years of Go codebases, you might see it a handful of times. If AI generates fallthrough, treat it as a red flag and verify the logic manually.

03

Tagless switches

This is Go's cleanest alternative to if-else chains. When you ask AI to handle multiple conditions, it often generates this:

switch {
case score >= 90:
    grade = "A"
case score >= 80:
    grade = "B"
case score >= 70:
    grade = "C"
case score >= 60:
    grade = "D"
default:
    grade = "F"
}

No value after switch means "switch on true" -- the first case whose condition is true wins. Cases are evaluated top to bottom, so order matters.

Switch vs if-else: when to use which

ScenarioUseWhy
Comparing one value to constantsExpression switchCleaner, easier to extend
Multiple range conditionsTagless switchReads like a decision table
Complex boolean with &&/\|\|if-elseSwitch cases cannot combine conditions
Two branchesif-elseSwitch is overkill for binary choices
Dispatching on string/enumExpression switchClear intent, easy to add cases
04

Type switches

When working with interface{} or any values (common in 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. handling, plugin systems), type switches let you branch on the actual type:

func describe(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Integer: %d\n", v)
    case string:
        fmt.Printf("String: %s (length: %d)\n", v, len(v))
    case bool:
        fmt.Printf("Boolean: %t\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

Inside each case, v has the concrete type -- not interface{}. So in the string case, you can call len(v) directly.

AI pitfall
AI sometimes generates type assertions (v, ok := i.(string)) when a type switch would be cleaner and safer. If you see a chain of if v, ok := i.(Type); ok { } else if ..., ask AI to refactor it into a type switch. Conversely, AI sometimes uses type switches when a simple type assertion would suffice for a single type check.
05

The default case

default runs when no case matches. If there is no default and nothing matches, the switch silently does nothing -- no error, no panic.

switch role {
case "admin":
    showAdminPanel()
case "user":
    showDashboard()
default:
    // Always think about this case
    redirectToLogin()
}
Good to know
AI often omits the default case. For non-exhaustive switches (string matching, dynamic values), always ask yourself: "What happens when none of these match?" If the answer matters, add a default.
06

Real-world pattern: error handling with switch

AI generates this pattern frequently in web servers:

func handleError(w http.ResponseWriter, err error) {
    switch {
    case errors.Is(err, ErrNotFound):
        http.Error(w, "Not found", 404)
    case errors.Is(err, ErrUnauthorized):
        http.Error(w, "Unauthorized", 401)
    case errors.Is(err, ErrBadRequest):
        http.Error(w, "Bad request", 400)
    default:
        log.Printf("unexpected error: %v", err)
        http.Error(w, "Internal error", 500)
    }
}

This is a tagless switch using errors.Is() for error matching. Clean, readable, easy to extend. When AI generates this pattern, verify it includes the default case with logging -- otherwise unexpected errors vanish silently.