Question
I am building a Go project with multiple files and want to organize it correctly during development.
If my code belongs to a single package called mypack, I assume I should place all .go files for that package inside a mypack directory.
However, I also want to try the package while developing it, which means I need a separate file that declares package main so I can run something like:
package main
import "mypack"
func main() {
// try mypack here
}
What is the standard way to organize a Go project in this situation?
Should I create a separate main program for testing the package manually?
Do I need to run go install mypack every time I want to try the code, or is there a better development workflow?
Short Answer
By the end of this page, you will understand how Go projects are commonly organized using packages and executable programs, why package main should usually be kept separate from reusable library code, and how to test or run your code during development without reinstalling everything each time.
Concept
In Go, project organization is built around packages.
A package is a directory of .go files that all declare the same package name, except for special test files. If you are writing reusable code, that code should usually live in its own package directory such as:
mypack/
Inside that directory, your files might look like this:
// file1.go
package mypack
// file2.go
package mypack
If you also want an executable program that uses that package, that program should normally live in a different directory and use package main.
Why? Because in Go:
- A single directory normally represents one package.
- Files in the same directory must use the same package name.
package maincreates an executable program.- Other package names create reusable libraries.
So your reusable code and your runnable app are usually separated.
This matters because real Go projects often contain:
- one or more reusable packages
- one or more command-line programs
- test files
- sometimes example programs
A clean structure makes it easier to:
- run code during development
- write tests
- reuse packages in other programs
- keep executable code separate from library logic
In modern Go development, a common pattern is:
project-root/
go.mod
mypack/
file1.go
file2.go
mypack_test.go
cmd/
tryapp/
main.go
Here:
mypack/contains reusable package code.cmd/tryapp/main.gocontains a small executable you can run while developing.
You do not need to reinstall the package every time just to test it manually. You can use go run on the main program, and Go will build the package as needed.
Mental Model
Think of a Go package as a toolbox and package main as a worker using the toolbox.
- The toolbox contains reusable tools: functions, types, and methods.
- The worker picks up those tools and uses them to do a job.
You do not usually store the worker inside the toolbox.
In the same way:
mypackshould contain the reusable logic.mainshould live separately and call intomypack.
Tests are like a checklist for the toolbox. They live alongside the toolbox so you can quickly verify that each tool still works.
Syntax and Examples
The key rule is: one directory, one package.
Basic package structure
project-root/
go.mod
mypack/
math.go
format.go
// mypack/math.go
package mypack
func Add(a, b int) int {
return a + b
}
// mypack/format.go
package mypack
func Name() string {
return "mypack"
}
Separate runnable program
project-root/
go.mod
mypack/
math.go
cmd/
tryapp/
main.go
// cmd/tryapp/main.go
package main
import (
"fmt"
"project-root/mypack"
)
func main() {
fmt.Println(mypack.Add(2, 3))
}
You can run it with:
go run ./cmd/tryapp
Testing the package
// mypack/math_test.go
package mypack
import "testing"
func TestAdd(t *testing.T) {
got := Add(2, 3)
want := 5
if got != want {
t.Fatalf("got %d, want %d", got, want)
}
}
Run tests with:
go test ./...
Important idea
During development, you usually use:
go runto try executable programsgo testto verify package behaviorgo buildto check that code compiles
You do not need go install every time.
go install is mainly for installing a built executable into your Go bin directory.
Step by Step Execution
Consider this project:
project-root/
go.mod
mypack/
greet.go
cmd/
tryapp/
main.go
// mypack/greet.go
package mypack
func Greet(name string) string {
return "Hello, " + name
}
// cmd/tryapp/main.go
package main
import (
"fmt"
"project-root/mypack"
)
func main() {
message := mypack.Greet("Sam")
fmt.Println(message)
}
If you run:
go run ./cmd/tryapp
here is what happens:
- Go finds the
mainpackage in./cmd/tryapp. - It sees that
main.goimportsproject-root/mypack. - It loads the source files inside the
mypackdirectory. - It compiles both the package and the main program.
- It creates a temporary executable.
- It runs that executable.
- The output is:
Hello, Sam
Notice that you did not manually install mypack first. Go handled the dependency automatically for this local run.
If you instead run:
go test ./mypack
Go:
- loads the package source files
- loads
*_test.gofiles - builds a test binary
- runs the tests
This is the normal workflow for package development.
Real World Use Cases
This package-and-main separation is used in many real Go projects.
Command-line tools
A CLI tool often has:
- reusable logic in internal or package directories
- a small
main.goincmd/toolname
Example:
- parsing files in a package
- calling that package from a CLI command
Web services
A web app may separate:
- HTTP handlers
- business logic
- database access
- executable startup code
The main package sets up the server, while packages contain reusable logic.
SDKs and libraries
If you are building code for reuse by other Go programs, the package should be clean and independent from any particular executable.
Experiments and local demos
During development, many developers create a small runner program to manually try a package without mixing demo code into the package itself.
Real Codebase Usage
In real codebases, developers usually avoid putting temporary testing code directly into a library package.
Common patterns include:
Small cmd/... programs
A project may include one or more runnable programs:
cmd/
api-server/
worker/
debug-tool/
Each directory contains a separate main package.
Unit tests for behavior
Instead of manually trying everything through main, developers write tests:
- unit tests for functions
- table-driven tests for multiple cases
- benchmarks for performance-sensitive code
Example programs
Sometimes developers add examples for documentation or learning:
func ExampleAdd() {
fmt.Println(Add(2, 3))
// Output: 5
}
Guarding library boundaries
A good library package usually avoids:
- reading command-line flags directly
- printing too much directly to stdout
- hardcoding app-specific behavior
That logic belongs in main or in higher-level application code.
Fast development loop
A common workflow is:
go test ./...
go run ./cmd/tryapp
This gives both automated checks and a manual executable for experimentation.
Common Mistakes
Mixing package main and another package in one directory
This is one of the most common mistakes.
Broken example:
// mypack/file1.go
package mypack
// mypack/trypack.go
package main
This does not work because a directory should not contain files from different packages in normal use.
Using go install as the main development workflow
Beginners sometimes think they must install after every change.
Usually better:
go run ./cmd/tryapp
go test ./...
Use go install when you want the executable installed for repeated use.
Putting too much logic in main
Broken style:
package main
import "fmt"
func main() {
// all logic here
fmt.Println(2 + 3)
}
This works for tiny programs, but it becomes hard to test and reuse.
Better:
package mypack
func Add(a, b int) int {
return a + b
}
package main
import (
"fmt"
"project-root/mypack"
)
func main() {
fmt.Println(mypack.Add(2, 3))
}
Forgetting tests
Manual trial programs are useful, but they do not replace tests.
Use both when appropriate:
go testfor repeatable verificationgo runfor interactive experimentation
Confusion about import paths
Your import path comes from your module path in go.mod, not just the folder name.
For example, if go.mod says:
module github.com/alice/project
then import paths look like:
import "github.com/alice/project/mypack"
not simply:
import "mypack"
Comparisons
| Concept | Purpose | Typical Location | Runnable? |
|---|---|---|---|
package mypack | Reusable library code | mypack/ | No |
package main | Executable program | cmd/appname/ or project root | Yes |
*_test.go | Automated tests | Same directory as package | Only through go test |
go run vs go build vs go install
| Command | What it does | Common use during development |
|---|---|---|
go run | Builds and runs a program immediately | Quickly try your app |
go build | Compiles code without running it | Check that code compiles |
go install | Builds and installs executable binaries | Install a command for repeated use |
go test | Builds and runs tests | Verify correctness |
Package code vs test runner code
| Approach | Good for | Limitation |
|---|---|---|
Small main program | Manual experimentation | Not automated |
| Unit tests | Repeatable verification | Less interactive |
| Benchmarks/examples | Documentation and performance checks | Narrower purpose |
Cheat Sheet
Core rules
- One directory usually contains one package.
- Reusable code goes in a non-
mainpackage. - Executable code goes in
package main. - Keep
mainin a separate directory from library code. - Use
*_test.gofiles for tests.
Common structure
project-root/
go.mod
mypack/
file1.go
file2.go
mypack_test.go
cmd/
tryapp/
main.go
Common commands
go run ./cmd/tryapp
go test ./...
go build ./...
go install ./cmd/tryapp
When to use what
- Use
go runto quickly execute your app. - Use
go testto test package behavior. - Use
go buildto verify compilation. - Use
go installwhen you want the executable installed.
Remember
- Do not mix
package mainandpackage mypackin one directory. - Import paths come from
go.modmodule paths. - A small development runner is normal and useful.
FAQ
Do I need go install every time I change my Go package?
No. During development, use go run for executable programs and go test for package tests. go install is usually for installing a finished command.
Can I put package main and package mypack in the same folder?
Normally no. Go expects source files in a directory to belong to the same package.
How do I manually try a package while developing it?
Create a small separate program with package main, usually in a directory such as cmd/tryapp, and run it with go run.
Should I test with a manual runner or with go test?
Use both when useful. Manual runners help you experiment, while go test gives repeatable automated checks.
Where should Go test files go?
Usually in the same directory as the package they test, with names ending in _test.go.
What is the purpose of the cmd directory in Go projects?
It is a common convention for storing executable entry points. Each subdirectory usually contains one main package.
Can a Go project have multiple executable programs?
Yes. Many projects have several directories under cmd/, each building a different executable.
Mini Project
Description
Build a small Go project with a reusable package and a separate runnable program. This demonstrates the standard development layout: package code in one directory, a main package in another directory, and tests alongside the package code.
Goal
Create a reusable greet package, run it from a small command-line program, and verify it with a unit test.
Requirements
- Create a package that exposes a function to build a greeting message.
- Create a separate
mainprogram that imports the package and prints a greeting. - Add at least one automated test for the package.
- Run the program with
go runinstead of reinstalling after every change.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.