Robert Griesemer Style Guide
Overview
Robert Griesemer co-created Go and was the primary designer of Go's generics. He previously worked on the V8 JavaScript engine and the Java HotSpot VM. His focus: precise semantics, clean syntax, and type system clarity.
Core Philosophy
"The language should help you think clearly."
"Every feature adds complexity. Is the complexity worth it?"
Griesemer values precision and clarity. Go's spec is remarkably small and clear because every construct has well-defined semantics.
Design Principles
Precise Semantics: Every language construct has exactly one meaning.
Orthogonal Features: Features should combine predictably.
No Surprises: Behavior should be obvious from reading the code.
Spec-Driven: If it's not in the spec, it's not guaranteed.
When Writing Code
Always
-
Understand the exact semantics of operations
-
Use types to express constraints
-
Make nil behavior explicit and safe
-
Write code that matches Go's spec, not implementation details
-
Use generics when type safety improves clarity
-
Define clear type constraints
Never
-
Rely on unspecified behavior
-
Assume implementation details (memory layout, etc.)
-
Create ambiguous APIs
-
Use empty interface when a constraint works
-
Ignore the distinction between value and pointer receivers
Prefer
-
Explicit type constraints over any
-
Named types for domain concepts
-
Method receivers that match semantics (value vs pointer)
-
Clear zero values
Code Patterns
Generics Done Right (Go 1.18+)
// BAD: Empty interface loses type safety func Max(a, b interface{}) interface{} { // runtime type assertion needed switch v := a.(type) { case int: if v > b.(int) { return v } return b // ... repeat for every type } panic("unsupported type") }
// GOOD: Type constraints preserve safety type Ordered interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~float32 | ~float64 | ~string }
func Max[T Ordered](a, b T) T { if a > b { return a } return b }
// Usage: type-safe, no assertions result := Max(3, 5) // int result := Max(3.14, 2.71) // float64
Precise Type Constraints
// Constraint: any type with these methods type Stringer interface { String() string }
// Constraint: underlying type is int type Integer interface { ~int | ~int64 }
// Constraint: must be pointer to struct with Name field type Named[T any] interface { *T GetName() string }
// Combining constraints type OrderedStringer interface { Ordered Stringer }
// Generic data structure with constraint type Set[T comparable] struct { items map[T]struct{} }
func (s *Set[T]) Add(item T) { if s.items == nil { s.items = make(map[T]struct{}) } s.items[item] = struct{}{} }
func (s *Set[T]) Contains(item T) bool { _, ok := s.items[item] return ok }
Value vs Pointer Semantics
// Value receiver: method doesn't modify, type is small type Point struct { X, Y float64 }
func (p Point) Distance(q Point) float64 { dx := p.X - q.X dy := p.Y - q.Y return math.Sqrt(dxdx + dydy) }
// Pointer receiver: method modifies, or type is large func (p *Point) Scale(factor float64) { p.X *= factor p.Y *= factor }
// IMPORTANT: Be consistent within a type // If ANY method needs pointer receiver, use pointer for ALL type Buffer struct { data []byte }
func (b *Buffer) Write(p []byte) (int, error) { b.data = append(b.data, p...) return len(p), nil }
func (b *Buffer) String() string { // Also pointer, for consistency return string(b.data) }
Safe Nil Handling
// Make nil receiver safe type Stack[T any] struct { items []T }
func (s *Stack[T]) Push(item T) { if s == nil { panic("nil stack") // Or return error } s.items = append(s.items, item) }
func (s *Stack[T]) Len() int { if s == nil { return 0 // Safe: nil stack has zero length } return len(s.items) }
// Named types for clarity type UserID int64 type OrderID int64
// Now these can't be accidentally swapped func GetOrder(uid UserID, oid OrderID) (*Order, error) { // ... }
Precise Interface Design
// Small, precise interfaces type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }
type Closer interface { Close() error }
// Compose precisely type ReadCloser interface { Reader Closer }
type WriteCloser interface { Writer Closer }
type ReadWriteCloser interface { Reader Writer Closer }
// Generic interface with type parameter type Container[T any] interface { Add(T) Remove(T) bool Contains(T) bool Len() int }
Mental Model
Griesemer designs by asking:
-
What does the spec say? Not the implementation—the specification.
-
Is this unambiguous? Can two people read this differently?
-
What are the edge cases? nil, zero values, overflow?
-
Is the type constraint minimal? Don't over-constrain.
Spec-Level Thinking
Feature Spec Guarantee
Map iteration Random order
Goroutine scheduling Unspecified
Struct layout Unspecified
String indexing Bytes, not runes
Interface nil nil interface ≠ interface holding nil
Write code that works regardless of implementation details.