# How to Organize a Go Project Structure for Maintainability and Scalability

> Learn best practices for organizing your Go project structure for maintainability and scalability. Discover efficient layouts for cmd pkg and internal directories.

- Repository: [Go/go](https://github.com/golang/go)
- Tags: best-practices
- Published: 2026-02-16

---

**The best way to organize a Go project is to use a single module at the repository root with a clear directory layout separating commands (`cmd/`), public libraries (`pkg/`), and private implementation details (`internal/`), following the conventions established by the Go standard library itself.**

Organizing a Go project structure correctly from the start prevents technical debt and makes large codebases navigable. The Go project (`golang/go`) maintains one of the most scalable codebases in the industry, and its layout in the `src/` directory serves as the canonical reference for these patterns.

## Use a Single Go Module at the Repository Root

Place one `go.mod` file at the top-level of your repository to version the entire tree as a single module. This ensures consistent dependency resolution across all packages and commands within the project.

The Go repository follows this pattern. In `src/go.mod`, the standard library declares itself as the `std` module:

```text
module std

go 1.22

```

This single module approach eliminates the complexity of nested modules while allowing the `go` command to resolve imports consistently across the entire codebase.

## Follow a Clear Top-Level Directory Layout

A predictable directory structure makes the codebase self-documenting. Organize your Go project structure using these standard directories:

### cmd/

Place one subdirectory per executable command, named after the binary it produces. Each subdirectory contains a [`main.go`](https://github.com/golang/go/blob/main/main.go) file that imports and invokes library code.

The Go repository demonstrates this in [`src/cmd/go/main.go`](https://github.com/golang/go/blob/main/src/cmd/go/main.go), which implements the `go` command itself:

```go
package main

import (
    "os"
    "cmd/go/internal/base"
)

func main() {
    base.Main()
}

```

### internal/

Store packages that are implementation details and should not be imported by external modules. The Go compiler enforces this restriction, preventing accidental external dependencies.

For example, [`src/internal/trace/tracev2/trace.go`](https://github.com/golang/go/blob/main/src/internal/trace/tracev2/trace.go) contains tracing internals that are not part of the public standard library API:

```go
package trace

// Internal implementation details...

```

### pkg/

House public libraries intended for import by other projects. While the Go standard library places public packages directly under `src/` (e.g., `src/net/http`), application projects often use `pkg/` to clearly distinguish reusable code from implementation details.

### api/, configs/, scripts/, test/, examples/

- **api/**: Versioned API contracts (Protobuf, OpenAPI specs)
- **configs/**: Default configuration files (YAML, JSON)
- **scripts/**: CI/CD scripts, code generation helpers
- **test/**: Integration and end-to-end tests spanning multiple packages
- **examples/**: Demonstration programs that serve as executable documentation

## Maintain Sensible Package Granularity

Organize code into cohesive packages where each package has a single, clear purpose. Avoid "god" packages that accumulate unrelated functionality.

Follow the standard library's pattern of nested packages for related domains. For instance, `net/http`, `net/url`, and `net/mail` share the `net` parent but remain separate packages with distinct responsibilities.

## Write Self-Contained Tests Next to Source Code

Place test files in the same directory as the code they test, using the [`_test.go`](https://github.com/golang/go/blob/main/_test.go) suffix. This co-location ensures tests remain synchronized with implementation changes.

Include example functions (`ExampleX`) in these files. The `go test` command compiles and runs these as both tests and documentation, as seen in [`src/net/http/example_test.go`](https://github.com/golang/go/blob/main/src/net/http/example_test.go).

## Document Public APIs with Go Doc Comments

Every exported identifier must have a comment beginning with the identifier's name. This enables `go doc` and pkg.go.dev to generate useful documentation.

The Go repository exemplifies this in [`src/net/http/server.go`](https://github.com/golang/go/blob/main/src/net/http/server.go), where exported types and functions include comprehensive documentation comments that explain behavior, parameters, and return values.

## Version Your Module with Semantic Tagging

Tag releases using semantic versioning (`vX.Y.Z`) so dependent projects can pin to specific versions using `go get`. The Go project itself tags releases as `go1.22.0`, which the module system interprets according to semantic versioning rules.

## Implement Continuous Integration and Linting

Configure CI pipelines to run `go test ./...` on every pull request. Include static analysis using `go vet` and tools like `golangci-lint` to catch style violations and potential bugs before they reach production.

## Follow Go Naming Conventions

Use short, lowercase package names without underscores. Directory names should match package names, making import paths predictable. For example, the `bytes` package lives in the `bytes` directory, and the `encoding/json` package lives in `src/encoding/json`.

## Isolate Generated Code

Store generated files with a `*_gen.go` suffix or in a dedicated `gen/` subdirectory. Include a `//go:generate` directive in the source package that specifies the command to regenerate the files, ensuring the generation process is reproducible and documented.

## Keep the Repository Tidy with Documentation

Include a [`README.md`](https://github.com/golang/go/blob/main/README.md) at the repository root explaining the project's purpose, build instructions, and high-level architecture. For library projects, consider a [`doc.go`](https://github.com/golang/go/blob/main/doc.go) file that provides package-level documentation.

## Summary

- **Use a single module** at the repository root with a `go.mod` file to unify dependency management across the entire project.
- **Separate concerns** using `cmd/` for executables, `internal/` for private implementation details, and `pkg/` for public libraries.
- **Co-locate tests** with source code using [`_test.go`](https://github.com/golang/go/blob/main/_test.go) files and include example functions for documentation.
- **Document everything** using Go doc comments that start with the identifier name for all exported symbols.
- **Version releases** using semantic tags (`vX.Y.Z`) and enforce quality with CI pipelines running `go test ./...` and linting tools.

## Frequently Asked Questions

### What is the difference between the `internal/` and `pkg/` directories in a Go project?

The `internal/` directory contains packages that are implementation details and cannot be imported by code outside the module, enforced by the Go compiler. The `pkg/` directory contains public libraries intended for external consumption. Use `internal/` to hide complexity and prevent external dependencies on unstable APIs, while `pkg/` exposes stable, documented functionality for other projects to import.

### Should I use nested modules or a single module for my Go project?

For most projects, a **single module at the repository root** is the best practice. This simplifies dependency management, ensures consistent versioning across the codebase, and aligns with how the Go standard library itself is organized (using `src/go.mod`). Nested modules add complexity and should only be used when you have truly independent components with different release cycles that must be versioned separately.

### How does the Go standard library organize its commands and packages?

The Go standard library places executable commands in `src/cmd/` (such as [`src/cmd/go/main.go`](https://github.com/golang/go/blob/main/src/cmd/go/main.go) for the `go` tool itself) and public packages directly under `src/` (such as `src/net/http`). Private implementation details that should not be imported externally are placed in `src/internal/` (like [`src/internal/trace/tracev2/trace.go`](https://github.com/golang/go/blob/main/src/internal/trace/tracev2/trace.go)). This layout demonstrates the clear separation between user-facing commands, public APIs, and internal implementation details.

### Where should I place integration tests in a Go project?

Place integration tests that span multiple packages in a dedicated `test/` directory at the repository root, or use the `*_test.go` convention within specific packages when testing package boundaries. The Go repository uses `src/cmd/go/testdata` extensively for integration test data and scripts. For end-to-end tests, a top-level `test/` directory keeps them separate from fast unit tests while still allowing them to import the module's public packages from `pkg/` or internal helpers from `internal/` as needed.