Go Project Structure for CLI Applications: The Standard Layout Explained

The standard Go project layout places executable entry points in cmd/, private business logic in internal/, and public libraries in pkg/ to create scalable, testable command-line tools.

The golang-standards/project-layout repository defines the industry-standard directory hierarchy for Go applications. This structure provides a battle-tested blueprint for organizing CLI code that separates concerns, enforces encapsulation, and remains maintainable as your tool grows from a simple script to a complex multi-command application.

The Core Directory Structure for Go CLI Projects

cmd/ – Application Entry Points

The cmd/ directory contains the bootstrap code for every executable your project produces. According to the repository's cmd/README.md, each binary gets its own subdirectory (e.g., cmd/myapp/) containing a minimal main.go that wires together dependencies and invokes your internal packages. This directory should remain thin—only orchestration logic belongs here, not business rules.

internal/ – Private Implementation Packages

The internal/ directory houses packages that cannot be imported by external modules. As documented in internal/README.md, the Go compiler enforces this privacy boundary at the language level, making it the ideal location for application logic, domain models, and private helpers. This protection prevents accidental API leakage and allows you to refactor implementation details without breaking external consumers.

pkg/ – Public Library Code

Use pkg/ only when you explicitly intend to share code with other projects. The pkg/README.md explains that libraries placed here are importable by any module, making this directory suitable for reusable utilities, client SDKs, or public APIs that you version and distribute. If you do not need to support external imports, omit this directory entirely.

Supporting Assets (configs/, scripts/, build/)

Non-Go files—configuration templates, CI/CD configs, build scripts, and deployment manifests—belong in dedicated directories like configs/, scripts/, and build/. This separation keeps your repository root tidy, distinguishes code from operational concerns, and makes the project structure immediately comprehensible to new contributors.

Why This Layout Excels for CLI Development

According to the golang-standards/project-layout source documentation, this structure provides four critical advantages for command-line tools:

  • Separation of concerns: The cmd/ directory contains only entry-point orchestration, while heavy computation and business rules reside in importable, testable packages.
  • Compiler-enforced encapsulation: The internal/ package protection actively prevents other modules from importing implementation details, protecting your API surface.
  • Multi-binary support: Adding new CLI tools requires only creating cmd/newtool/main.go without refactoring existing code or shared libraries.
  • Scalability: The hierarchy accommodates growth from single-purpose scripts to complex applications with plugins, subcommands, and shared internal utilities.

Complete Implementation Example

The following files demonstrate a minimal CLI application following the standard layout. The cmd/hello/main.go file serves as the thin bootstrap, while internal/greeting/greeting.go contains the actual logic protected from external import.

File: cmd/hello/main.go

package main

import (
	"fmt"
	"myproject/internal/greeting"
)

func main() {
	fmt.Println(greeting.Hello())
}

File: internal/greeting/greeting.go

package greeting

// Hello returns a friendly greeting.
// This package is private – only this module may import it.
func Hello() string {
	return "Hello, world!"
}

File: pkg/util/logger/logger.go (optional public helper)

package logger

import "log"

// Info prints an informational message.
// Since this is under /pkg, other projects can import it.
func Info(msg string) {
	log.Printf("[INFO] %s", msg)
}

Run the application with:

go run ./cmd/hello

Essential Documentation in the Repository

The golang-standards/project-layout repository provides authoritative guidance in these specific files:

  • README.md – High-level overview of the entire layout and architectural rationale for each top-level directory.
  • cmd/README.md – Conventions for organizing executable entry points and handling multiple binaries.
  • internal/README.md – Detailed explanation of the private internal pattern with real-world project examples.
  • pkg/README.md – Guidelines for publishing public libraries, semantic versioning, and API compatibility guarantees.
  • configs/README.md – Standards for storing configuration templates and environment-specific files.
  • scripts/README.md – Build automation, linting, testing, and release scripts tailored for CLI workflows.

Summary

  • Place main packages in cmd/<app>/main.go to define entry points for each executable binary.
  • Store private business logic in internal/ to leverage the Go compiler's import restrictions and prevent API leakage.
  • Publish public APIs selectively in pkg/ only when supporting external consumers and maintaining backward compatibility.
  • Keep configuration and operational scripts in dedicated directories (configs/, scripts/) to maintain a clean repository root.
  • This structure enables unit testing of core logic without invoking the command line, supporting true test-driven development.

Frequently Asked Questions

Should I use internal/ or pkg/ for my CLI's core logic?

Use internal/ for all code that should remain implementation-specific to your module. The Go compiler actively prevents other modules from importing internal/ packages, protecting your API surface from accidental dependencies. Reserve pkg/ for libraries you explicitly intend to share with external projects, as these become part of your public API contract and require careful versioning.

How do I structure a CLI with multiple commands or subcommands?

Create subdirectories under cmd/ for each distinct binary (e.g., cmd/server/, cmd/client/). For a single binary with subcommands (using libraries like Cobra or Kong), keep the command definitions in cmd/myapp/ but delegate all execution logic to packages in internal/ or pkg/. This keeps the entry point thin and ensures business logic remains testable without executing through the CLI layer.

Can I omit the pkg/ directory entirely?

Yes. Many successful Go CLI projects use only cmd/ and internal/ directories. Include pkg/ only when you need to expose specific packages for external import. According to the repository's guidance in pkg/README.md, this directory is optional and should be used intentionally rather than by default.

What belongs in the repository root versus configs/?

Place only metadata files (go.mod, LICENSE, README.md) and entry-point documentation in the root. Move all configuration templates, example YAML files, and environment-specific manifests into configs/ to keep the root directory uncluttered. This distinction helps new contributors immediately identify the project's purpose without navigating through deployment artifacts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →