Question
I have a Go program with a main.go file in the main package. I want to move some code into another file while keeping both files in the same package, rather than creating a separate package.
My goal is to use a directory structure like this:
foo/
├── main.go
└── bar.go
bar.go:
package main
import "fmt"
func Bar() {
fmt.Println("Bar")
}
main.go:
package main
func main() {
Bar()
}
I want main.go to call Bar() from bar.go. However, when I run:
go run main.go
I get this error:
# command-line-arguments
./main.go:4:2: undefined: Bar
How can I split a Go main package into multiple files and run the program without this error?
Short Answer
By the end of this page, you will understand how Go treats multiple files in the same package, why go run main.go fails in this case, and how to correctly run all files in a package together. You will also learn how this works in real Go projects and how to avoid common package and file-organization mistakes.
Concept
In Go, a package can be made up of multiple .go files in the same directory. If those files all declare the same package name, such as package main, they are compiled together as one unit.
That means this is completely valid:
main.gocontainsmain()bar.gocontains helper functions- both files use
package main - both files live in the same folder
The important detail is how you run the program.
When you execute:
go run main.go
Go only compiles and runs that one file. It does not automatically include other files from the same directory. Since bar.go is not part of that command, Bar() is unknown, so you get undefined: Bar.
To run all files in the package, you should run the package, not just one file:
go run .
or explicitly list both files:
go run main.go bar.go
This matters in real programming because Go organizes code around packages, not around one giant source file. Splitting code into multiple files improves readability and maintenance, but you must still compile the package correctly.
Mental Model
Think of a Go package like a folder of recipe cards that together describe one meal.
main.gois one cardbar.gois another card- both belong to the same meal because they are in the same folder and have the same package name
If you say, "Use only this one card," Go only sees main.go.
If that card refers to instructions written on bar.go, it fails.
If you say, "Use the whole folder," Go reads all matching cards together and everything works.
So the key idea is:
go run main.go= run one filego run .= run the whole package in the current directory
Syntax and Examples
In the same directory, files with the same package name are part of the same package.
Basic syntax
main.go
package main
func main() {
Bar()
}
bar.go
package main
import "fmt"
func Bar() {
fmt.Println("Bar")
}
Correct ways to run it
Run the whole package in the current directory:
go run .
Or list the files explicitly:
go run main.go bar.go
Output
Bar
Why this works
Both files:
- are in the same directory
- use
package main
Step by Step Execution
Consider this program:
main.go
package main
func main() {
Bar()
}
bar.go
package main
import "fmt"
func Bar() {
fmt.Println("Bar")
}
What happens with go run main.go
- Go reads only
main.gobecause that is the only file you asked it to run. - It sees a call to
Bar()insidemain(). - It looks for a definition of
Bar()in the compiled input. bar.gowas not included.- Compilation fails with:
undefined: Bar
What happens with go run .
Real World Use Cases
Splitting one Go package across multiple files is very common.
Common scenarios
- CLI tools:
main.gostarts the app, while other files handle flags, validation, and output. - Small web servers: one file for routes, one for handlers, one for configuration.
- Data scripts: one file for input parsing, another for processing, another for reporting.
- Prototypes: keep everything in
package mainat first, then refactor later if code becomes reusable.
Example structure for a small CLI
myapp/
├── main.go # program entry point
├── config.go # config loading
├── output.go # printing helpers
└── validate.go # input checks
All of these can stay in package main if the code is only for that application.
Why developers do this
- easier to read than one very long file
- related code can be grouped together
- no need to create a new package too early
- makes later refactoring simpler
Real Codebase Usage
In real Go projects, developers often start with multiple files in one package and split into separate packages only when reuse or clearer boundaries become necessary.
Common patterns
Keep main.go small
A common pattern is to keep main.go focused on startup only:
package main
func main() {
run()
}
Then put run() and other logic in other files.
Use helper files by responsibility
Examples:
config.goserver.gohandlers.godb.goutil.go
Use guard clauses and validation
A file like validate.go might contain checks used by main.go:
package main
{
name == {
errors.New()
}
}
Common Mistakes
1. Running only one file
Broken command:
go run main.go
If main.go depends on functions in other files, this fails.
Use instead:
go run .
or:
go run main.go bar.go
2. Mixing package names in one directory
Broken example:
main.go
package main
func main() {
Bar()
}
bar.go
package helpers
func Bar() {}
This does not work in the same folder as one package build target.
To keep files together as one package, they must use the same package name.
3. Thinking uppercase is required inside the same package
This is valid too:
Comparisons
Running one file vs running a package
| Command | What Go includes | Good for | Result in this case |
|---|---|---|---|
go run main.go | Only main.go | Very small single-file programs | Fails because Bar() is missing |
go run main.go bar.go | Exactly those files | Explicit file-based runs | Works |
go run . | All Go files in the current package | Normal project workflow | Works |
Same package vs separate package
| Approach | When to use it |
|---|
Cheat Sheet
Quick rules
- Files in the same folder with the same
packagename belong to one package. go run main.goruns onlymain.go.go run .runs the whole package in the current directory.go run main.go bar.goruns exactly those files together.- Functions in the same package can call each other across files.
- Uppercase names are for exported identifiers; lowercase names are package-private.
Correct setup
foo/
├── main.go
└── bar.go
Both files:
package main
Run with:
go run .
Common error
undefined: Bar
Usually means the file containing Bar() was not included in the build.
Good default habit
For a multi-file Go app, use:
go run .
If you want to build instead of run
FAQ
Why does go run main.go not see functions in other files?
Because that command only compiles main.go. It does not automatically include other files in the package.
What is the correct command for a multi-file Go program?
Usually:
go run .
This tells Go to run the whole package in the current directory.
Can I keep multiple files in package main?
Yes. This is normal for small and medium applications.
Do I need to create a separate package just to split code into files?
No. You can split code into multiple files within the same package.
Should the function be Bar or bar?
If it is only used inside the same package, either can work. Use bar for package-private helpers and Bar only when exporting is needed.
Can I run specific files instead of the whole package?
Yes:
go run main.go bar.go
But in real projects, go run . is usually simpler.
What if my files are in different folders?
Mini Project
Description
Build a small command-line Go program split across multiple files in the same main package. This demonstrates the exact pattern used when a program becomes too large for a single main.go file, but is still simple enough that creating separate packages is unnecessary.
Goal
Create a multi-file Go program that prints a greeting and a status message by calling functions defined in different files of the same package.
Requirements
- Create at least three Go files in the same directory.
- Put all files in
package main. - Define
main()inmain.go. - Move helper functions into other files.
- Run the program using the whole package, not just one file.
Keep learning
Related questions
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.
Blank Identifier Imports in Go: What `_` Means in an Import Statement
Learn what `_` means in a Go import, why blank identifier imports run package init code, and when to use them safely.
Calling Functions Across Files in the Same Go Package
Learn how Go uses packages across multiple files, why functions may appear undefined, and how to organize code correctly.