Go

Lesson 01

Package and file organization

Keep packages small, cohesive, and named by what callers need instead of by internal technical layers.

Good Code

reviews/service.go
package reviews

type Service struct {
    repo Repository
}

func NewService(repo Repository) *Service {
    return &Service{repo: repo}
}

func (s *Service) Publish(ctx context.Context, id string) error {
    review, err := s.repo.Find(ctx, id)
    if err != nil {
        return err
    }

    review.Publish()
    return s.repo.Save(ctx, review)
}

Bad Code

utils/review_helpers.go
package utils

var ReviewDB *sql.DB

func PublishReview(id string) error {
    row := ReviewDB.QueryRow("select title from reviews where id = ?", id)
    var title string
    if err := row.Scan(&title); err != nil {
        return err
    }

    _, err := ReviewDB.Exec("update reviews set status = 'published' where id = ?", id)
    return err
}

Review Notes

What to review

Good Code

The good version gives the package a domain name, keeps dependencies explicit, and exports a small service boundary.

Bad Code

The bad version hides review behavior in a generic utils package with global database state. Callers cannot tell what the package owns.

Takeaways

  • A Go package is an API boundary; review whether its name and exported surface make that boundary clear.