Encapsulation hides state behind controlled operations or boundaries. A common mistake is treating a package-level variable as convenient shared state: any package function can alter it, and tests lose isolation. section 2.3.2
var db *sql.DB
func init() {
dataSourceName :=
os.Getenv("MYSQL_DATA_SOURCE_NAME") #1
d, err := sql.Open("mysql", dataSourceName)
if err != nil {
log.Panic(err)
}
err = d.Ping()
if err != nil {
log.Panic(err)
}
db = d #2
}
This initialization exposes a database connection through the global db variable. init runs before tests, creates an external dependency automatically, and leaves error handling inside initialization. section 2.3.2
func createClient(dsn string) (*sql.DB, error) { #1
db, err := sql.Open("mysql", dsn)
if err != nil {
return nil, err #2
}
if err = db.Ping(); err != nil {
return nil, err
}
return db, nil
}
A plain function keeps the connection local, returns errors to its caller, and allows integration tests to invoke setup explicitly. section 2.3.2
Getters and setters can encapsulate behavior, not merely fields. They can validate values, return computed results, protect access with a mutex, hide representation, or provide a debugging interception point. They are not automatically idiomatic in Go. section 2.4
timer := time.NewTimer(time.Second) <-timer.C #1
Direct field access can be appropriate: the standard library exposes timer.C for receiving events rather than requiring a getter. section 2.4
currentBalance := customer.Balance() #1
if currentBalance < 0 {
customer.SetBalance(0) #2
}
Use methods when access needs a policy or behavior: Balance reads the value, while SetBalance can enforce a nonnegative balance. section 2.4
Choose the boundary according to the behavior it must protect:
- Replace global initialization with an explicit function when callers should handle errors, choose setup timing, or isolate tests. section 2.3.2
- Keep init when setup cannot fail, needs no global state, and does not affect unit tests, such as static HTTP handler registration. section 2.3.2
- Prefer direct fields when methods add no value; use Balance and SetBalance when validation, representation flexibility, synchronization, or debugging justifies them. section 2.4













