Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
2: Anatomy of a Go Program
Now that you've got your environment set up, we need to talk about how a Go program is actually put together. If you're coming from a scripting language like Python or Ruby, your instinct might be to just start writing code at the top of a file and let it run linearly. In Go, that's not how it works. Go is strictly structured, and while that might feel like overhead at first, it's exactly what keeps large Go codebases from turning into a nightmare as they grow.
The Scripting Temptation
I often see developers treat their first few Go programs like scripts. They cram every single line of logic—variable declarations, calculations, and output—directly into the main function. Let's look at a naive implementation of a simple tool that calculates the final price of an item after tax.
package main
import "fmt"
func main() {
price := 100.0
taxRate := 0.07
total := price + (price * taxRate)
fmt.Printf("The total price is: %.2f\n", total)
}
On the surface, this works perfectly. It compiles, it runs, and it gives you the right answer. But as a professional engineer, I'd tell you this is "wrong"—not because it doesn't work, but because it's fragile. Everything is trapped inside main. If you suddenly need to calculate taxes for ten different items, or if you want to write a test to ensure your tax logic is correct, you're stuck. You can't "call" the logic inside main from anywhere else; it's a dead end.
Decoupling Logic from Execution
The better way is to treat your main function as a "coordinator" rather than a "doer." The main function should handle the program's entry and exit, but the actual work—the business logic—should live in its own functions. This is the core of Go's anatomy: the package declaration, the imports, and the separation of concerns.
package main
import "fmt"
// calculateTotal handles the math. It doesn't care about printing;
// it just cares about returning a value.
func calculateTotal(price, taxRate float64) float64 {
return price + (price * taxRate)
}
func main() {
price := 100.0
taxRate := 0.07
total := calculateTotal(price, taxRate)
fmt.Printf("The total price is: %.2f\n", total)
}
I might seem like I'm just adding more lines of code for no reason, but look at the trade-off. By moving the logic into calculateTotal, I've created a reusable tool. I can now call that function a hundred times with different values without rewriting the math. More importantly, I can now write a unit test for that function in a separate file without having to actually "run" the whole program.
Where the Structure Actually Breaks
You'll notice that both examples start with package main. This is a non-negotiable rule in Go: if you want to create an executable program, the file must be part of the main package and it must contain a function called main(). If you rename the package to calculator, the Go compiler will treat it as a library (a package meant to be used by other programs) and will refuse to run it as a standalone app.
Another detail is the import block. You'll see people use a grouped import style—import ( "fmt"; "math" )—once they have more than one dependency. I highly recommend this. It keeps the top of your file clean and prevents a long, vertical list of imports from pushing your actual code off the screen. It's a small habit, but it's the standard in every production Go project I've ever worked on.
📋 Practical Task
Refactoring the Messy Tip Calculator
Below is a program that calculates a restaurant bill including a tip. It's written in the "naive" style, with all the logic crammed into the main function. Your task is to refactor this program to follow the professional anatomy we discussed.
package main
import "fmt"
func main() {
billAmount := 50.0
tipPercentage := 0.20
tip := billAmount * tipPercentage
total := billAmount + tip
fmt.Printf("Bill: %.2f, Tip: %.2f, Total: %.2f\n", billAmount, tip, total)
}
Requirements for your refactor:
- Create a separate function called
calculateTipthat takes the bill amount and percentage as arguments and returns the tip amount. - Create a second function called
calculateTotalthat takes the bill amount and the calculated tip and returns the final sum. - Keep the
mainfunction solely for initializing the variables and printing the final result. - Ensure the program still outputs the exact same string format.
There are no comments for now.