Organizing Go Projects: Best Practices and Conventions
In Go, having a well-organized project structure is crucial for maintainability, scalability, and collaboration. Go does not enforce a specific project structure, giving developers flexibility. However, certain conventions have emerged as best practices within the community. Below is an outline of a common Go project structure.
Basic Project Structure
A simple project might look like this:
myapp/
├── cmd/
│ └── myapp/
│ └── main.go
├── pkg/
│ └── mypackage/
│ └── mypackage.go
├── internal/
│ └── myinternal/
│ └── myinternal.go
├── api/
│ ├── v1/
│ │ └── api.proto
│ └── v2/
│ └── api.proto
├── configs/
│ └── config.yaml
├── scripts/
│ └── build.sh
├── web/
│ ├── static/
│ └── templates/
├── build/
│ ├── ci/
│ └── docker/
├── docs/
│ ├── README.md
│ └── api.md
├── vendor/
├── go.mod
└── go.sum
Directory Breakdown
-
cmd/: This directory contains the entry points for your applications. Each subdirectory represents a different application.
// cmd/myapp/main.go package main import ( "myapp/pkg/mypackage" "fmt" ) func main() { fmt.Println(mypackage.Hello()) } -
pkg/: This directory is for libraries and packages that you want to share across multiple projects and applications.
// pkg/mypackage/mypackage.go package mypackage func Hello() string { return "Hello, World!" } -
internal/: This directory contains packages that are meant to be used only within your project. This ensures encapsulation.
// internal/myinternal/myinternal.go package myinternal func Secret() string { return "This is an internal secret" } -
api/: This directory is often used for defining API specifications, typically using protocol buffers (gRPC) or OpenAPI.
// api/v1/api.proto syntax = "proto3"; package v1; service Example { rpc SayHello (SayHelloRequest) returns (SayHelloResponse); } message SayHelloRequest { string name = 1; } message SayHelloResponse { string message = 1; } -
configs/: Configuration files like YAML, JSON, or TOML files.
// configs/config.yaml debug: true port: 8080 -
scripts/: Scripts for various tasks like building, running tests, or deployment.
# scripts/build.sh #!/bin/sh go build -o myapp cmd/myapp/main.go -
web/: Assets for web servers, including static files (HTML, CSS, JavaScript) and templates.
// web/templates/index.html <!DOCTYPE html> <html> <head> <title>My App</title> </head> <body> <h1>Welcome to My App</h1> </body> </html> -
build/: Infrastructure and CI/CD-related files.
// build/ci/.gitlab-ci.yml stages: - build build-job: stage: build script: - go build cmd/myapp/main.go -
docs/: Project documentation. This could include README files, API documentation, etc.
// docs/README.md # My App This is the README for My App. -
vendor/: Dependencies managed by Go Modules when vendoring is enabled. This is optional as Go Modules are generally handled automatically.
-
go.mod and go.sum: These files are for dependency management with Go modules.
// go.mod module myapp go 1.18
Practical Example
Here's a small practical example combining the above snippets:
// cmd/myapp/main.go
package main
import (
"fmt"
"myapp/pkg/mypackage"
"myapp/internal/myinternal"
)
func main() {
fmt.Println(mypackage.Hello())
fmt.Println(myinternal.Secret())
}
// pkg/mypackage/mypackage.go
package mypackage
func Hello() string {
return "Hello, World!"
}
// internal/myinternal/myinternal.go
package myinternal
func Secret() string {
return "This is an internal secret"
}
# configs/config.yaml
debug: true
port: 8080
#!/bin/sh
# scripts/build.sh
go build -o myapp cmd/myapp/main.go
Conclusion
A well-structured Go project allows for easier navigation, maintenance, and collaboration. While Go doesn’t enforce a specific structure, following these conventions will help ensure that your projects are well-organized and scalable. The key is to remain consistent and use a structure that best fits the needs of your project.