Chapter 36: Nested if
Nested if means an if statement inside another if statement (or inside else / else if blocks). In Go this is perfectly legal, very common, but also very easy to get wrong — so we need to talk about it carefully.
Many beginners write deeply nested ifs at first, then after a few weeks/months they learn the Go community strongly prefers flat code (early returns + guard clauses) instead of deep nesting.
So today I want to show you both worlds:
- how nested if works (syntax + mechanics)
- real examples (good and bad)
- why deep nesting is usually considered bad style in Go
- the idiomatic alternatives most experienced Go developers use in 2025–2026
1. Basic Syntax of Nested if
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
if outerCondition { // some code if innerCondition { // this only runs if BOTH outer AND inner are true } else { // runs if outer is true but inner is false } } else { // outer was false → inner if never even looked at } |
Important rules (same as normal if):
- braces {} always required
- no parentheses around conditions
- you can nest as deep as you want (but please don’t go deeper than 3–4 levels)
2. Simple Nested Example – Temperature + Rain Check
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package main import "fmt" func main() { temperature := 38 isRaining := false hasUmbrella := true if temperature > 30 { fmt.Println("It's hot outside ☀️") if isRaining { if hasUmbrella { fmt.Println("→ Take umbrella and go out") } else { fmt.Println("→ No umbrella → better stay inside") } } else { fmt.Println("→ No rain → enjoy the sun (but hydrate!)") } } else { fmt.Println("Cooler weather — no heat problem") } } |
Output when temperature=38, isRaining=false:
|
0 1 2 3 4 5 6 7 |
It's hot outside ☀️ → No rain → enjoy the sun (but hydrate!) |
3. Classic Bad Example – Deep Nesting (What Beginners Often Write)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
func processUserOrder(user *User, order *Order, payment *Payment) error { if user != nil { if user.IsLoggedIn { if order != nil { if order.Items != nil && len(order.Items) > 0 { if payment != nil { if payment.IsValid() { // finally do the work return nil } else { return ErrInvalidPayment } } else { return ErrNoPaymentInfo } } else { return ErrEmptyOrder } } else { return ErrNoOrder } } else { return ErrNotLoggedIn } } else { return ErrNilUser } } |
Problems with this style:
- Hard to read (code moves far right)
- Hard to test / debug
- Easy to miss cases
- Difficult to add new conditions later
4. Idiomatic Go Way – Flat Style with Early Returns (Guard Clauses)
This is what most experienced Go developers prefer in 2025–2026:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
func processUserOrder(user *User, order *Order, payment *Payment) error { if user == nil { return ErrNilUser } if !user.IsLoggedIn { return ErrNotLoggedIn } if order == nil { return ErrNoOrder } if order.Items == nil || len(order.Items) == 0 { return ErrEmptyOrder } if payment == nil { return ErrNoPaymentInfo } if !payment.IsValid() { return ErrInvalidPayment } // happy path – everything is fine // ... do the real work ... return nil } |
Advantages of this style:
- Code stays on the left margin (happy path is easy to see)
- Each condition is independent and easy to test
- Adding / removing checks is trivial
- Much easier to read & maintain
- Matches Go proverb: “Clear is better than clever”
5. When Nested if is Still Okay (Small & Shallow)
Small nested ifs are fine and common when:
- the nesting level is only 2
- the logic is very closely related
- early return would make code less readable
Example (still acceptable):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
if user.IsPremium { if user.HasDiscountCode { finalPrice = price * 0.8 } else { finalPrice = price * 0.9 // premium but no code → 10% off } } else { finalPrice = price } |
6. Your Quick Practice – Try Both Styles
Task: Write code that checks:
- user not nil
- user age >= 18
- user has ticket
- event is not cancelled
Print different messages.
First write it with deep nesting, then rewrite it with early returns.
Which version do you find easier to read?
Any part still confusing?
- Why Go forces {} braces even for single statements?
- How many levels of nesting is “too many”?
- When to choose if-else chain vs switch vs early returns?
- Or ready to move to switch statement next?
Keep experimenting with both styles — most Go developers start with nested if and gradually move to flat/early-return style as they read more real code.
You’re making fantastic progress — keep asking! 💪🇮🇳🚀
