Question
I am writing a basic Go program that calls a function defined in a different file but within the same package. However, I get an error saying that NewEmployee is undefined.
Here is the code:
// main.go
package main
func main() {
emp := NewEmployee()
_ = emp
}
// employee.go
package main
type Employee struct {
name string
age int
}
func NewEmployee() *Employee {
p := &Employee{}
return p
}
func PrintEmployee(p *Employee) string {
return "Hello world!"
}
Why would a function declared in another file of the same package be reported as undefined, and how should this be set up correctly in Go?
Short Answer
By the end of this page, you will understand how Go treats multiple files in the same package, why a function can still appear as undefined, how to compile and run Go code correctly, and how exported and unexported names affect visibility.
Concept
In Go, files that belong to the same package are compiled together. That means a function defined in employee.go should be available in main.go as long as both files are in the same folder and are built together.
In your example, both files use:
package main
So NewEmployee() is visible to main.go without any import.
If Go says undefined: NewEmployee, the problem is usually not the function definition itself. The most common cause is that only one file is being run or built.
For example, if you run:
go run main.go
Go will compile only main.go, not employee.go. Since employee.go is excluded, NewEmployee() does not exist from the compiler's point of view.
Instead, you should run the whole package:
go run .
or:
go build
This tells Go to compile all .go files in the current package directory.
Another important idea is name visibility:
- Names starting with an uppercase letter are exported.
- Names starting with a lowercase letter are unexported.
Inside the same package, both exported and unexported names are accessible. Across different packages, only exported names are accessible.
So in your code:
NewEmployeeis exportedEmployeeis exportednameandageare unexported
That is valid, and main.go can still use them because it is in the same package.
One more note: your PrintEmployee function returns a string, so its signature should include string as the return type. Otherwise, the code will not compile.
Mental Model
Think of a Go package like a folder of recipe cards that are all used to make one meal.
- Each
.gofile is one recipe card. - The
packageline says which meal that card belongs to. - When Go builds the package, it gathers all recipe cards in the same folder and package.
If you only hand Go one card, like main.go, it cannot see the instructions written on employee.go.
So the issue is usually not "the function is in another file". The issue is "that file was not included in the build."
Syntax and Examples
In Go, functions in the same package can be used directly across files.
Example structure
// main.go
package main
import "fmt"
func main() {
emp := NewEmployee()
fmt.Println(emp)
fmt.Println(PrintEmployee(emp))
}
// employee.go
package main
type Employee struct {
name string
age int
}
func NewEmployee() *Employee {
return &Employee{name: "Alice", age: 30}
}
func PrintEmployee(p *Employee) string {
return "Hello world!"
}
How to run it
Run the package, not a single file:
go run .
Why this works
- Both files are in the same directory.
- Both files declare
package main. go run .includes all files in that package.
If files are in different packages
Then you must import the package and use exported names only.
package employee
type Employee struct{}
func NewEmployee() *Employee {
return &Employee{}
}
package main
import "myapp/employee"
func main() {
emp := employee.NewEmployee()
_ = emp
}
That is a different setup from your original question.
Step by Step Execution
Consider this code:
// main.go
package main
import "fmt"
func main() {
emp := NewEmployee()
fmt.Println(PrintEmployee(emp))
}
// employee.go
package main
type Employee struct {
name string
age int
}
func NewEmployee() *Employee {
return &Employee{name: "Sam", age: 25}
}
func PrintEmployee(p *Employee) string {
return p.name
}
Step-by-step
- Go loads the current package.
- It sees both
main.goandemployee.gobecause both are in the same folder and package. main()starts running.NewEmployee()is called.NewEmployee()creates anEmployeevalue and returns a pointer to it.- That pointer is stored in
emp. PrintEmployee(emp)is called.PrintEmployeeaccessesp.nameand returns it.fmt.Println(...)prints the name.
What happens if you run only one file?
If you run:
go run main.go
then Go compiles only main.go.
At that point:
- Go reads
main.go - It finds
NewEmployee() - It cannot find a definition in that file
- It reports:
undefined: NewEmployee
That is why building the whole package matters.
Real World Use Cases
This concept is used constantly in real Go projects.
Splitting code by responsibility
A package may be divided into multiple files such as:
main.gofor program startupemployee.gofor employee-related types and functionsdb.gofor database accessconfig.gofor configuration loading
All of these can belong to the same package if they are closely related.
Keeping code readable
As a program grows, putting everything in one file becomes hard to manage. Multiple files let you:
- group related types and functions
- reduce clutter
- make code easier to navigate
Building command-line tools
In a CLI app, you might have:
main.goto parse flagscommands.goto define command handlershelpers.gofor utility functions
They can all work together in the same package.
Web applications
A small web server might use:
main.gofor startuproutes.gofor route setuphandlers.gofor HTTP handlersmodels.gofor data structures
Again, package-level visibility allows direct access across files.
Real Codebase Usage
In real projects, developers use package-level organization intentionally.
Common patterns
Grouping related code in one package
A package often contains several files:
user.gouser_service.gouser_validation.go
All can share unexported helpers if they are in the same package.
Constructors
Functions like NewEmployee() are common constructor-style functions:
func NewEmployee(name string, age int) *Employee {
return &Employee{name: name, age: age}
}
This helps centralize setup logic.
Validation before creation
func NewEmployee(name string, age int) (*Employee, error) {
if name == "" {
return nil, errors.New("name is required")
}
if age < 0 {
return nil, errors.New("age cannot be negative")
}
return &Employee{name: name, age: age}, nil
}
This is common in production code.
Unexported helpers
A package may hide internal logic in lowercase functions:
func normalizeName(name string) string {
return strings.TrimSpace(name)
}
These helpers are visible across files in the same package, but not outside it.
Early returns and guard clauses
Go code often uses simple early exits:
func PrintEmployee(p *Employee) string {
if p == nil {
return "no employee"
}
return p.name
}
This keeps logic readable and safe.
Common Mistakes
Here are common mistakes beginners make with this topic.
1. Running only one file
Broken
go run main.go
If NewEmployee() is in another file, this can cause:
undefined: NewEmployee
Fix
go run .
or:
go build
2. Using different package names in the same folder
Broken
// main.go
package main
// employee.go
package employee
Files in one folder generally need to belong to the same package for this kind of direct access.
Fix
Use the same package name if they are meant to compile together:
package main
3. Forgetting to export names across packages
If files are moved into different packages, lowercase names are not accessible.
Broken
package employee
func newEmployee() *Employee {
return &Employee{}
}
package main
import "myapp/employee"
func main() {
employee.newEmployee()
}
Fix
Use an uppercase first letter:
func NewEmployee() *Employee {
return &Employee{}
}
4. Incorrect function signature
Your original PrintEmployee returned a string but did not declare it correctly.
Broken
func PrintEmployee(p *Employee) {
return "Hello world!"
}
Fix
func PrintEmployee(p *Employee) string {
return "Hello world!"
}
5. Declaring a variable and not using it
In Go, unused local variables cause compile errors.
Broken
func main() {
emp := NewEmployee()
}
Fix
Use it:
func main() {
emp := NewEmployee()
_ = emp
}
or print it.
6. Putting files in different directories without imports
If files are in different folders, they are usually different packages. You cannot call functions directly without importing the package.
Comparisons
| Situation | Can you call the function directly? | What is required? |
|---|---|---|
| Same package, same folder, built together | Yes | Same package name |
| Same package, but only one file is run | No | Run/build the whole package |
| Different packages | No | Import the package and use exported names |
| Same folder, different package names | No | Reorganize package structure |
Same package vs different package
| Feature | Same package | Different package |
|---|---|---|
| Direct access to functions | Yes | No |
Need import | No | Yes |
| Can use lowercase names | Yes | No |
| Can use uppercase names | Yes | Yes, if exported |
go run main.go vs go run .
| Command | What it does |
|---|---|
go run main.go | Runs only main.go |
go run . | Runs all Go files in the current package |
For multi-file programs in one package, go run . is usually the correct choice.
Cheat Sheet
// File 1
package main
func Hello() string {
return "hi"
}
// File 2
package main
func main() {
println(Hello())
}
Rules
- Files in the same package can use each other's functions directly.
- Files must usually be in the same directory and use the same
packagename. - Run the whole package with:
go run .go build
- If you run only one file, other files are not included.
- Uppercase names are exported.
- Lowercase names are package-private.
- Across different packages, you must:
importthe package- use exported names only
Common fix for undefined
If your code is split across files in one package:
go run .
Correct function return syntax
func PrintEmployee(p *Employee) string {
return "Hello world!"
}
Constructor-style function
func NewEmployee() *Employee {
return &Employee{}
}
FAQ
Why does Go say a function is undefined when it is in another file?
Usually because you ran only one file, such as go run main.go, instead of building the whole package.
How do I run all Go files in the same package?
Use:
go run .
Do files in the same Go package need imports between them?
No. Functions, types, and variables in the same package are available directly.
Does the function name need to start with an uppercase letter?
Only if you want to use it from another package. Inside the same package, lowercase names also work.
Can two files in the same folder have different package names?
Not for normal package usage like this. If they do, they are treated as different packages and cannot access each other directly.
Why does my PrintEmployee function fail to compile?
Because it returns a string but the function signature did not declare a return type.
What is the difference between go build and go run .?
go build compiles the package. go run . compiles and runs it immediately.
Can I split a Go package into many files?
Yes. That is a common and recommended way to organize related code.
Mini Project
Description
Create a small multi-file Go program that manages employees. This project demonstrates how functions, structs, and constructor-style functions work across multiple files in the same package.
Goal
Build and run a two-file Go program where main.go creates an employee using a function from employee.go and prints employee information.
Requirements
Requirement 1 Requirement 2 Requirement 3
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.