\ When I started learning Go, one of the first questions I had was:
“How do I actually structure my code?”
In languages like C, it’s common to throw everything into a single file, or maybe separate header and implementation files. But in Go, project structure is a big deal: it affects how easily you can scale, test, and share your code. In this article, I’ll walk through why structure matters, how to access functions from different files, and what best practices I’m learning as I move forward.
A Go program can live entirely in a single file:
package main import "fmt" func main() { fmt.Println("2 + 2 =", 2+2) }
This works fine for “hello world” or quick experiments.
But as soon as you add more functionality (subtraction, multiplication, division…), the file gets messy and hardly scalable.
That’s where Go’s packages and folders come in.
In Go, every file belongs to a package.
By convention:
Here’s how I split the calculator project:
calculator/ │ ├── main.go └── calculator/ └── operations.go
\
package calculator func Add(a, b int) int { return a + b } func Subtract(a, b int) int { return a - b } func Multiply(a, b int) int { return a * b } func Divide(a, b int) (int, error) { if b == 0 { return 0, fmt.Errorf("cannot divide by zero") } return a / b, nil }
package main import ( "fmt" "GoLang-progress/calculator" ) func main() { fmt.Println("2 + 3 =", calculator.Add(2, 3)) fmt.Println("10 - 4 =", calculator.Subtract(10, 4)) fmt.Println("6 * 7 =", calculator.Multiply(6, 7)) result, err := calculator.Divide(8, 0) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("8 / 0 =", result) } }
Notice how main.go is now clean: it doesn’t worry about the math itself, just how to use it.
A common beginner question:
“How do I call a function from another file or folder?”
In my repo, I structured it like this:
calculator/ │ ├── main.go └── internal/ └── calc/ └── operations.go
Here, the math functions live under internal/calc.
\
package calc import "fmt" func Add(a, b int) int { return a + b } func Divide(a, b int) (int, error) { if b == 0 { return 0, fmt.Errorf("cannot divide by zero") } return a / b, nil }
\
package main import ( "fmt" "github.com/turman17/GoLang-progress/calculator/internal/calc" ) func main() { fmt.Println("2 + 3 =", calc.Add(2, 3)) result, err := calc.Divide(10, 0) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("10 / 2 =", result) } }
Your import must match your module path from go.mod plus the folder path.
In your repo, go.mod contains:
module github.com/turman17/GoLang-progress
The calculator code you want to use lives in the folder:
calculator/internal/calc
So the full import path is:
github.com/turman17/GoLang-progress/calculator/internal/calc
Common errors and fixes
❌ import "GoLang-progress/calculator/internal/calc"
→ Missing GitHub org/username. Must use full path.
❌ import "github.com/turman17/GoLang-progress/internal/calc"
→ Missing calculator directory in the path.
❌ go: module not found errors
→ Ensure go.mod has module github.com/turman17/GoLang-progress and run go mod tidy.
\
go run ./calculator
or
go build ./calculator
As projects grow, you’ll often see this pattern:
project-name/ │ ├── cmd/ → executables (main entrypoints) ├── internal/ → private code (not for external use) ├── pkg/ → reusable packages ├── api/ → API definitions (gRPC, OpenAPI, etc.) └── go.mod
For beginners, this might be overkill. But as you move into web apps, services, or MLOps tools, this layout becomes essential.
I’ll continue sharing what I learn as I explore Go for MLOps and backend development. Next up: error handling and testing in Go.
\ 👉 Check out my repo here: https://github.com/turman17/GoLang-progress
And stay tuned for the next article!


