Section 13

Packages & Modules

Python has modules (files), packages (directories with __init__.py), and a sprawling packaging ecosystem. Go has packages (directories) and modules (versioned dependency units defined by go.mod). The system is simple and deterministic.

Package Basics

// Every file starts with a package declaration
package store  // all .go files in this directory share this package

// Importing
import (
    "fmt"                          // standard library
    "encoding/json"                // nested stdlib package
    "github.com/you/project/internal/store"  // your package
)

Visibility

Python uses _underscore convention. Go uses capitalization. It's enforced by the compiler.

Python Go Rule
def public_func()func PublicFunc()Uppercase first letter = exported
def _private_func()func privateFunc()Lowercase first letter = unexported
class MyClasstype MyStruct structSame rule: MyStruct exported, myStruct not
self.public_fields.PublicFieldApplies to struct fields too

The internal Directory

// Code in internal/ can only be imported by parent packages
project/
├── cmd/server/main.go      // can import internal/
├── internal/
│   └── store/store.go      // private to this module
└── pkg/
    └── api/api.go           // public — importable by anyone

Dependency Management

# Initialize module
go mod init github.com/you/project

# Add dependency
go get github.com/gorilla/[email protected]

# Tidy — remove unused, add missing
go mod tidy

# Vendor — copy deps locally (optional)
go mod vendor
No Circular Imports

Go prohibits circular package dependencies at compile time. If package A imports B, B cannot import A. This forces clean dependency graphs. Python allows circular imports (with hacks) — Go does not. If you hit a cycle, extract shared types into a third package.

Section 14

Testing

Python uses pytest (third-party, installed separately). Go has testing built into the language and toolchain. Test files live next to production code, named *_test.go.

Python — pytest
def test_add():
    assert add(2, 3) == 5

def test_add_negative():
    assert add(-1, 1) == 0
Go — testing
func TestAdd(t *testing.T) {
    got := Add(2, 3)
    if got != 5 {
        t.Errorf("Add(2, 3) = %d, want 5", got)
    }
}

func TestAddNegative(t *testing.T) {
    got := Add(-1, 1)
    if got != 0 {
        t.Errorf("Add(-1, 1) = %d, want 0", got)
    }
}

Table-Driven Tests

Go's idiomatic pattern for parameterized tests — replaces pytest's @pytest.mark.parametrize.

func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive", 2, 3, 5},
        {"negative", -1, 1, 0},
        {"zeros", 0, 0, 0},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := Add(tt.a, tt.b)
            if got != tt.expected {
                t.Errorf("got %d, want %d", got, tt.expected)
            }
        })
    }
}

Benchmarks

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(2, 3)
    }
}

// Run: go test -bench=. -benchmem
// Output: BenchmarkAdd-8   1000000000   0.3 ns/op   0 B/op   0 allocs/op
No Assert Library Needed

Go's testing uses if statements and t.Errorf. No assert functions in the standard library. This is intentional — the team believes conditionals are clearer than assert DSLs. Third-party libraries like testify exist if you disagree, and many codebases use them.