go-packages

📁 cxuu/golang-skills 📅 Jan 27, 2026
58
总安装量
14
周安装量
#6901
全站排名
安装命令
npx skills add https://github.com/cxuu/golang-skills --skill go-packages

Agent 安装分布

claude-code 10
github-copilot 10
gemini-cli 9
codex 9
cursor 9
antigravity 8

Skill 文档

Go Packages and Imports

This skill covers package organization and import management following Google’s and Uber’s Go style guides.


Package Organization

Avoid Util Packages

Advisory: This is a best practice recommendation.

Package names should describe what the package provides. Avoid generic names like util, helper, common, or similar—they make code harder to read and cause import conflicts.

// Good: Meaningful package names
db := spannertest.NewDatabaseFromFile(...)
_, err := f.Seek(0, io.SeekStart)

// Bad: Vague package names obscure meaning
db := test.NewDatabaseFromFile(...)
_, err := f.Seek(0, common.SeekStart)

Generic names like util can be used as part of a name (e.g., stringutil) but should not be the entire package name.

Package Size

Advisory: This is best practice guidance.

When to combine packages:

  • If client code likely needs two types to interact, keep them together
  • If types have tightly coupled implementations
  • If users would need to import both packages to use either meaningfully

When to split packages:

  • When something is conceptually distinct
  • The short package name + exported type creates a meaningful identifier: bytes.Buffer, ring.New

File organization: No “one type, one file” convention in Go. Files should be focused enough to know which file contains something and small enough to find things easily.


Imports

Import Organization

Normative: This is required per Go Wiki CodeReviewComments.

Imports are organized in groups, with blank lines between them. The standard library packages are always in the first group.

package main

import (
	"fmt"
	"hash/adler32"
	"os"

	"github.com/foo/bar"
	"rsc.io/goversion/version"
)

Use goimports to manage this automatically.

Import Grouping (Extended)

Combined: Google + Uber guidance

Minimal grouping (Uber): stdlib, then everything else.

Extended grouping (Google): stdlib → other → protocol buffers → side-effects.

// Good: Standard library separate from external packages
import (
    "fmt"
    "os"

    "go.uber.org/atomic"
    "golang.org/x/sync/errgroup"
)
// Good: Full grouping with protos and side-effects
import (
    "fmt"
    "os"

    "github.com/dsnet/compress/flate"
    "golang.org/x/text/encoding"

    foopb "myproj/foo/proto/proto"

    _ "myproj/rpc/protocols/dial"
)

Import Renaming

Normative: This is required per Go Wiki CodeReviewComments and Google’s Go style guide.

Avoid renaming imports except to avoid a name collision; good package names should not require renaming. In the event of collision, prefer to rename the most local or project-specific import.

Must rename: collision with other imports, generated protocol buffer packages (remove underscores, add pb suffix).

May rename: uninformative names (e.g., v1), collision with local variable.

// Good: Proto packages renamed with pb suffix
import (
    foosvcpb "path/to/package/foo_service_go_proto"
)

// Good: urlpkg when url variable is needed
import (
    urlpkg "net/url"
)

func parseEndpoint(url string) (*urlpkg.URL, error) {
    return urlpkg.Parse(url)
}

Blank Imports (import _)

Normative: This is required per Go Wiki CodeReviewComments and Google’s Go style guide.

Packages that are imported only for their side effects (using import _ "pkg") should only be imported in the main package of a program, or in tests that require them.

// Good: Blank import in main package
package main

import (
    _ "time/tzdata"
    _ "image/jpeg"
)

Dot Imports (import .)

Normative: This is required per Go Wiki CodeReviewComments and Google’s Go style guide.

Do not use dot imports. They make programs much harder to read because it is unclear whether a name like Quux is a top-level identifier in the current package or in an imported package.

Exception: The import . form can be useful in tests that, due to circular dependencies, cannot be made part of the package being tested:

package foo_test

import (
	"bar/testutil" // also imports "foo"
	. "foo"
)

In this case, the test file cannot be in package foo because it uses bar/testutil, which imports foo. So the import . form lets the file pretend to be part of package foo even though it is not.

Except for this one case, do not use import . in your programs.

// Bad: Dot import hides origin
import . "foo"
var myThing = Bar() // Where does Bar come from?

// Good: Explicit qualification
import "foo"
var myThing = foo.Bar()

Avoid init()

Source: Uber Go Style Guide

Avoid init() where possible. When init() is unavoidable, code should:

  1. Be completely deterministic, regardless of program environment
  2. Avoid depending on ordering or side-effects of other init() functions
  3. Avoid global/environment state (env vars, working directory, args)
  4. Avoid I/O (filesystem, network, system calls)
// Bad: init() with I/O and environment dependencies
var _config Config

func init() {
    cwd, _ := os.Getwd()
    raw, _ := os.ReadFile(path.Join(cwd, "config.yaml"))
    yaml.Unmarshal(raw, &_config)
}
// Good: Explicit function for loading config
func loadConfig() (Config, error) {
    cwd, err := os.Getwd()
    if err != nil {
        return Config{}, err
    }

    raw, err := os.ReadFile(path.Join(cwd, "config.yaml"))
    if err != nil {
        return Config{}, err
    }

    var config Config
    if err := yaml.Unmarshal(raw, &config); err != nil {
        return Config{}, err
    }
    return config, nil
}

Acceptable uses of init():

  • Complex expressions that cannot be single assignments
  • Pluggable hooks (e.g., database/sql dialects, encoding registries)
  • Deterministic precomputation

Exit in Main

Source: Uber Go Style Guide

Call os.Exit or log.Fatal* only in main(). All other functions should return errors to signal failure.

Why this matters:

  • Non-obvious control flow: Any function can exit the program
  • Difficult to test: Functions that exit also exit the test
  • Skipped cleanup: defer statements are skipped
// Bad: log.Fatal in helper function
func readFile(path string) string {
    f, err := os.Open(path)
    if err != nil {
        log.Fatal(err)  // Exits program, skips defers
    }
    b, err := io.ReadAll(f)
    if err != nil {
        log.Fatal(err)
    }
    return string(b)
}
// Good: Return errors, let main() decide to exit
func main() {
    body, err := readFile(path)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(body)
}

func readFile(path string) (string, error) {
    f, err := os.Open(path)
    if err != nil {
        return "", err
    }
    b, err := io.ReadAll(f)
    if err != nil {
        return "", err
    }
    return string(b), nil
}

Exit Once

Prefer to call os.Exit or log.Fatal at most once in main(). Extract business logic into a separate function that returns errors.

// Good: Single exit point with run() pattern
func main() {
    if err := run(); err != nil {
        log.Fatal(err)
    }
}

func run() error {
    args := os.Args[1:]
    if len(args) != 1 {
        return errors.New("missing file")
    }

    f, err := os.Open(args[0])
    if err != nil {
        return err
    }
    defer f.Close()  // Will always run

    b, err := io.ReadAll(f)
    if err != nil {
        return err
    }

    // Process b...
    return nil
}

Benefits of the run() pattern:

  • Short main() function with single exit point
  • All business logic is testable
  • defer statements always execute

Quick Reference

Topic Rule Type
Import organization std first, groups separated by blank lines Normative
Import grouping std → other (→ proto → side-effect) Combined
Import renaming Only when necessary; prefer renaming local/project import Normative
Blank imports Only in main packages or tests Normative
Dot imports Only for circular test dependencies Normative
Util packages Avoid; use descriptive names Advisory
Package size Balance cohesion vs. distinct concepts Advisory
init() Avoid; must be deterministic if used Advisory
Exit in main Only exit from main(); return errors Advisory

See Also

  • For core style principles: go-style-core
  • For naming conventions: go-naming
  • For error handling patterns: go-error-handling
  • For defensive coding: go-defensive
  • For linting tools: go-linting