← Back to Go Course | Chapter 1: Setup & Basics | Lesson 6 of 7

Go Project Workspace Structure

A Go project's folder layout is like organizing a toolbox -- each drawer (folder) has a clear job so anyone can find what they need.

A Minimal Layout

The simplest possible Go project is just a go.mod file and a main.go in the same directory. As a project grows, code is split into subdirectories, each subdirectory becoming its own importable package named after its folder.

Example: A Minimal Layout

markup
package main

import "fmt"

func main() {
	fmt.Println("myapp/")
	fmt.Println("  go.mod")
	fmt.Println("  main.go")
}

Organizing Packages by Folder

Each directory containing .go files (other than the root) typically represents one package, imported by its path relative to the module root, such as github.com/example/myapp/utils. This convention keeps related code together and makes the import graph easy to follow just by looking at the file tree.

Note: Package names are conventionally short, lowercase, and match the folder name.

Example: Organizing Packages by Folder

markup
package main

import "fmt"

func main() {
	layout := []string{"myapp/go.mod", "myapp/main.go", "myapp/utils/strings.go"}
	for _, path := range layout {
		fmt.Println(path)
	}
}

The internal/ Convention

Go treats any package inside a directory named internal specially: it can only be imported by code rooted at the parent of that internal directory. This is the standard way to mark packages as private implementation details that outside modules should not depend on.

Example: The internal/ Convention

markup
package main

import "fmt"

func main() {
	fmt.Println("myapp/internal/auth can only be imported from within myapp")
}

Where Tests Live

Go test files sit right next to the code they cover and are named with a _test.go suffix, such as strings_test.go alongside strings.go. The 'go test' tool automatically finds and runs every _test.go file in a package, so there's no separate test-discovery configuration needed.

Example: Where Tests Live

markup
package main

import "fmt"

func main() {
	fmt.Println("utils/strings.go")
	fmt.Println("utils/strings_test.go")
}
Common Mistakes
  1. Putting all code in package main inside one giant file instead of splitting reusable logic into separate packages.
  2. Creating a top-level src folder out of old GOPATH habit, which modern Go modules don't require or expect.
  3. Mixing test files, main application code, and documentation with no consistent naming, making 'go build ./...' behave unpredictably.
Chapter Summary
  • A typical Go project has a go.mod at its root and a main.go (or a cmd/ directory) as the entry point.
  • Reusable logic is organized into small, focused packages, each in its own directory.
  • The 'internal/' directory name has special meaning: it restricts imports to within the module.
  • Test files live alongside the code they test, named with a _test.go suffix.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.