MidТеория5 min

Пакеты и модули

Система пакетов Go, модули, версионирование, workspaces и управление зависимостями

Система пакетов Go

Пакет (package) — основная единица организации кода в Go. Каждый файл .go принадлежит ровно одному пакету.

Правила пакетов

// File: math/calculator.go
package math // Every file in a directory must have the same package name

import "fmt"

// Exported — starts with UPPERCASE (visible outside the package)
func Add(a, b int) int {
    return a + b
}

// Unexported — starts with lowercase (private to the package)
func validate(n int) bool {
    return n >= 0
}

// Exported type with mixed visibility
type Calculator struct {
    Name    string  // Exported field
    history []int   // Unexported field
}

// Exported method
func (c *Calculator) Compute(expr string) (int, error) {
    result := 42 // Simplified
    c.history = append(c.history, result)
    return result, nil
}

// Unexported method
func (c *Calculator) reset() {
    c.history = nil
}
Правило Описание
Имя пакета Совпадает с именем директории (по конвенции)
Один пакет — одна директория Все файлы в директории должны иметь одно имя пакета
Экспорт через регистр Заглавная буква = экспортировано, строчная = приватно
package main Специальный пакет для создания исполняемых файлов
Неиспользуемый импорт Ошибка компиляции

Правила именования пакетов

// GOOD package names:
package http      // Short, lowercase, no underscores
package json
package user      // Singular, not users
package strconv   // Abbreviation is OK if widely known

// BAD package names:
package httpUtils   // Don't use camelCase
package http_util   // Don't use underscores
package common      // Too vague
package utils       // Too vague
package base        // Too vague
package models      // Avoid plural

Go Proverb: Имя пакета — это часть имени каждого экспортируемого идентификатора. http.Server, а не http.HTTPServer. Избегайте заикания (stuttering).

Импорт пакетов

package main

import (
    // Standard library
    "fmt"
    "io"
    "net/http"

    // Third-party (grouped separately by convention)
    "github.com/gorilla/mux"

    // Internal/local packages
    "github.com/myorg/myapp/internal/service"
)

// Named import — resolve name conflicts
import (
    "crypto/rand"           // Package name: rand
    mathrand "math/rand"    // Alias to avoid conflict
)

// Dot import — imports into current namespace (avoid!)
import . "fmt"
// Now you can call Println() instead of fmt.Println()
// NEVER use in production code — only in tests sometimes

// Blank import — import for side effects only
import _ "image/png" // Registers PNG decoder via init()

// Blank import is common for:
// - Database drivers: _ "github.com/lib/pq"
// - Image decoders: _ "image/jpeg"
// - Profiling: _ "net/http/pprof"

Go Modules

Go Modules (с Go 1.11, по умолчанию с Go 1.16) — официальная система управления зависимостями.

Создание модуля

# Initialize a new module
mkdir myproject && cd myproject
go mod init github.com/username/myproject

# This creates go.mod:
// go.mod
module github.com/username/myproject

go 1.25

require (
    github.com/gorilla/mux v1.8.1
    golang.org/x/sync v0.7.0
)

require (
    // Indirect dependencies (automatically managed)
    golang.org/x/text v0.16.0 // indirect
)

Файлы go.mod и go.sum

Файл Назначение
go.mod Определение модуля, зависимости и их версии
go.sum Криптографические хэши зависимостей для воспроизводимости
// go.sum (generated automatically, DO NOT edit manually)
github.com/gorilla/mux v1.8.1 h1:TuMoUvkRETW...
github.com/gorilla/mux v1.8.1/go.mod h1:DVbg23sWS...

Важно: go.sum обязательно должен быть в системе контроля версий. Он гарантирует, что все разработчики и CI используют идентичные зависимости.

Основные команды

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

# Add latest version
go get github.com/gorilla/mux@latest

# Update a dependency
go get -u github.com/gorilla/mux

# Update all dependencies
go get -u ./...

# Remove unused dependencies, add missing ones
go mod tidy

# Download dependencies to local cache
go mod download

# Copy dependencies to vendor/ directory
go mod vendor

# Verify dependency checksums
go mod verify

# Show dependency graph
go mod graph

# Explain why a module is needed
go mod why github.com/gorilla/mux

# Show available versions
go list -m -versions github.com/gorilla/mux

Версионирование (Semantic Versioning)

Go Modules строго следуют Semantic Versioning (semver):

v1.2.3
│ │ │
│ │ └── Patch: bug fixes, no API changes
│ └──── Minor: new features, backward compatible
└────── Major: breaking changes
Версия Импорт Описание
v0.x.x github.com/user/pkg Нестабильная, API может меняться
v1.x.x github.com/user/pkg Стабильная, обратная совместимость
v2.x.x github.com/user/pkg/v2 Несовместимые изменения, новый путь
v3.x.x github.com/user/pkg/v3 Каждый major — новый путь импорта

v2+ Convention

// go.mod for v2
module github.com/user/mylib/v2

go 1.25

// Importing v2
import "github.com/user/mylib/v2"

// You can use v1 and v2 simultaneously!
import (
    v1 "github.com/user/mylib"
    v2 "github.com/user/mylib/v2"
)

Это называется Import Compatibility Rule: если путь импорта одинаков, код должен быть совместим.

Директива replace

replace позволяет подменить модуль другим — полезно для локальной разработки:

// go.mod
module github.com/myorg/myapp

go 1.25

require (
    github.com/myorg/mylib v1.2.0
)

// Replace with a local path (for development)
replace github.com/myorg/mylib => ../mylib

// Replace with a fork
replace github.com/original/pkg => github.com/myfork/pkg v1.0.0

// Replace with a specific version
replace golang.org/x/crypto => golang.org/x/crypto v0.23.0

Важно: replace работает только в go.mod главного модуля. Зависимости не наследуют чужие replace директивы.

Директива exclude и retract

// go.mod

// Exclude a broken version
exclude github.com/broken/pkg v1.3.0

// Retract versions of YOUR module (Go 1.16+)
retract (
    v1.0.0 // Published with critical bug
    [v1.1.0, v1.2.0] // Range of retracted versions
)

Go Workspaces (Go 1.18+)

Workspaces позволяют работать с несколькими модулями одновременно без replace директив.

# Project structure:
# workspace/
# ├── go.work
# ├── myapp/
# │   ├── go.mod
# │   └── main.go
# └── mylib/
#     ├── go.mod
#     └── lib.go

# Create workspace
cd workspace
go work init

# Add modules to workspace
go work use ./myapp
go work use ./mylib

# Or add both at once
go work use ./myapp ./mylib

Файл go.work:

go 1.25

use (
    ./myapp
    ./mylib
)

// Optional: replace directives (workspace-level)
// replace github.com/someorg/somepkg => ./local-somepkg
Аспект replace go.work
Где объявляется В go.mod В go.work
Коммитится в git Не рекомендуется (для dev) Не коммитится (обычно)
Область действия Один модуль Все модули в workspace
Цель Подмена зависимости Мультимодульная разработка
# Workspace commands
go work init           # Create go.work
go work use ./path     # Add a module
go work sync           # Sync go.work with module dependencies
go work edit           # Edit go.work programmatically

# Build/test all modules in workspace
go build ./...
go test ./...

Совет: go.work обычно НЕ коммитится в git (добавляется в .gitignore). Он нужен только для локальной разработки.

Internal packages

Директория internal в Go имеет специальное значение на уровне компилятора:

myproject/
├── go.mod
├── cmd/
│   └── server/
│       └── main.go          # Can import internal/
├── internal/
│   ├── auth/
│   │   └── auth.go          # Only importable from myproject/...
│   └── database/
│       └── db.go
├── pkg/
│   └── api/
│       └── api.go            # Importable by anyone
└── other-project/
    └── main.go               # CANNOT import myproject/internal/...
// myproject/cmd/server/main.go
package main

import (
    "github.com/myorg/myproject/internal/auth"     // OK
    "github.com/myorg/myproject/internal/database"  // OK
    "github.com/myorg/myproject/pkg/api"            // OK
)

// another-project/main.go
import (
    "github.com/myorg/myproject/internal/auth"  // COMPILE ERROR!
    "github.com/myorg/myproject/pkg/api"         // OK
)

Правило: internal доступен только коду, находящемуся в дереве родительской директории internal.

Vendor directory

# Create vendor directory with all dependencies
go mod vendor

# Build using vendor instead of module cache
go build -mod=vendor ./...

# Verify vendor is consistent
go mod verify
Аспект Module cache Vendor
Расположение $GOPATH/pkg/mod ./vendor/
Общий для проектов Да Нет
Коммитится в git Нет Иногда (для автономности)
Используется по умолчанию Да Нет (нужен -mod=vendor)

Практические паттерны

Организация пакетов по функциональности

// GOOD: organized by domain/functionality
myapp/
├── user/
│   ├── handler.go     // HTTP handlers
│   ├── service.go     // Business logic
│   ├── repository.go  // Data access
│   └── model.go       // Types
├── order/
│   ├── handler.go
│   ├── service.go
│   └── repository.go
└── pkg/
    └── middleware/
        └── auth.go
// BAD: organized by layer (Java-style)
myapp/
├── handlers/
│   ├── user.go
│   └── order.go
├── services/
│   ├── user.go
│   └── order.go
├── models/
│   ├── user.go
│   └── order.go
└── repositories/
    ├── user.go
    └── order.go

Go Proverb: "A little copying is better than a little dependency." Не создавайте пакет utils — скопируйте маленькую функцию туда, где она нужна.

Проверь себя

Что произойдёт при импорте пакета с префиксом подчёркивания: `import _ "image/png"`?

Как правильно импортировать v2 модуля в Go?

Кто может импортировать пакеты из директории `internal/`?

Для чего нужен файл go.sum?

Что означает, если имя функции в Go начинается с заглавной буквы?