Basic Golang Project Structure

Understanding the standard layout for Go applications

Go projects follow a conventional structure that makes code organization clear and maintainable. While Go doesn't enforce a strict project layout, there are widely accepted conventions that help teams collaborate and make projects easier to understand. This guide shows the basic structure for a typical Go application.

Standard Project Layout

my-go-project/ (project root)
├── go.mod Module definition & dependencies
├── go.sum Dependency checksums
├── README.md Project documentation
├── .gitignore Git ignore patterns
├── main.go Application entry point
├── cmd/ Executable applications
│ └── app/ Main application
│ └── main.go
├── internal/ Private application code
│ ├── handlers/ HTTP handlers
│ │ └── user.go
│ ├── models/ Data models
│ │ └── user.go
│ ├── database/ DB connection & queries
│ │ └── db.go
│ └── config/ Configuration
│ └── config.go
├── pkg/ Public library code
│ ├── utils/ Utility functions
│ │ └── helpers.go
│ └── validator/ Validation logic
│ └── validator.go
├── api/ API definitions
│ └── routes.go
├── configs/ Configuration files
│ ├── config.yaml
│ └── config.env
├── migrations/ Database migrations
│ ├── 001_create_users.sql
│ └── 002_create_posts.sql
├── tests/ Test files
│ └── user_test.go
└── docs/ Documentation
└── api.md

Root Files

go.mod

Defines the module path and Go version. Lists all dependencies with their versions. Created with go mod init github.com/username/project.

go.sum

Contains cryptographic checksums of all dependencies. Ensures reproducible builds. Auto-generated by Go.

main.go

The entry point for simple applications. Contains the main() function. For larger projects, use cmd/ instead.

README.md

Project documentation, installation instructions, usage examples, and contribution guidelines.

.gitignore

Excludes build artifacts, IDE files, and environment-specific files from version control.

Directory Structure

cmd/

Contains the main applications for your project. Each subdirectory is a separate executable. This allows multiple binaries in one project (e.g., cmd/server, cmd/cli).

Example: cmd/web/main.go for a web server, cmd/migrate/main.go for database migrations.

internal/

Private application code that cannot be imported by other projects. Go enforces this restriction. Use for business logic, handlers, models, and database code specific to your application.

Common subdirectories: handlers/, models/, database/, config/, middleware/.

pkg/

Public library code that can be imported by other projects. Use for reusable components, utilities, and shared libraries.

Example: pkg/utils/ for helper functions, pkg/validator/ for validation logic.

api/

API definitions, protocol buffer files, or OpenAPI specs. Useful for documenting and versioning your API.

configs/

Configuration files (YAML, JSON, TOML, .env). Store default configurations and environment-specific settings.

migrations/

Database migration files. Use tools like golang-migrate or sql-migrate to manage schema changes.

tests/

Integration tests and test fixtures. Note: Go convention is to put *_test.go files next to the source files.

docs/

Additional documentation, API specs, architecture diagrams, and design documents.

Best Practices

  • Keep it simple: Small projects don't need all directories. Start with main.go and add structure as needed.
  • Use internal/ for app-specific code: Prevents external imports and keeps your code encapsulated.
  • Put test files next to source: Go's testing convention uses *_test.go files in the same package directory.
  • Organize by feature for larger projects: Instead of internal/handlers/, consider internal/user/, internal/order/ (domain-driven design).
  • Version control go.sum: Always commit go.sum to ensure reproducible builds.

Quick Start Example

# Initialize a new Go module
go mod init github.com/username/my-go-project

# Create basic structure
mkdir -p cmd/app internal/handlers internal/models
mkdir -p pkg/utils configs migrations tests docs

# Create main.go
cat > main.go << 'EOF'
package main

import "fmt"

func main() {
    fmt.Println("Hello, Go!")
}
EOF

# Run the project
go run main.go