When I started learning Go, one of the first questions I had was: “How do I actually structure my code?” “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. The Simplest Go Program The Simplest Go Program A Go program can live entirely in a single file: package main import "fmt" func main() { fmt.Println("2 + 2 =", 2+2) } 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. packages folders Introducing Packages Introducing Packages In Go, every file belongs to a package. package By convention: main → executable program others (like calculator, utils, etc.) → reusable logic main → executable program others (like calculator, utils, etc.) → reusable logic Here’s how I split the calculator project: calculator/ │ ├── main.go └── calculator/ └── operations.go calculator/ │ ├── main.go └── calculator/ └── operations.go main.go → handles input/output operations.go → defines functions like Add, Subtract, etc. main.go → handles input/output main.go operations.go → defines functions like Add, Subtract, etc. operations.go Example: operations.go Example: 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 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 } Example: main.go Example: main.go 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) } } 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. main.go is now clean Accessing Functions from Different Files Accessing Functions from Different Files A common beginner question: “How do I call a function from another file or folder?” “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 calculator/ │ ├── main.go └── internal/ └── calc/ └── operations.go Here, the math functions live under internal/calc. operations.go (inside internal/calc) operations.go (inside 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 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 } main.go (importing internal/calc) main.go (importing internal/calc) 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) } } 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) } } Why this import path is required Why this import path is required Your import must match your module path from go.mod plus the folder path. module path In your repo, go.mod contains: module github.com/turman17/GoLang-progress module github.com/turman17/GoLang-progress The calculator code you want to use lives in the folder: calculator/internal/calc calculator/internal/calc So the full import path is: github.com/turman17/GoLang-progress/calculator/internal/calc github.com/turman17/GoLang-progress/calculator/internal/calc A few important notes A few important notes Folder name ≠ package name → The folder is internal/calc, but the package inside is declared as package calc. Imports use module path → Always start with github.com/... if that’s in your go.mod. Internal is special → Packages under /internal can only be imported by code inside the same module. Folder name ≠ package name → The folder is internal/calc, but the package inside is declared as package calc. Folder name ≠ package name Imports use module path → Always start with github.com/... if that’s in your go.mod. Imports use module path Internal is special → Packages under /internal can only be imported by code inside the same module. Internal is special Common errors and fixes 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. Quick checklist Quick checklist go.mod has the correct module line Directory is calculator/internal/calc with package calc inside main.go imports github.com/turman17/GoLang-progress/calculator/internal/calc Build from the module root: go.mod has the correct module line Directory is calculator/internal/calc with package calc inside main.go imports github.com/turman17/GoLang-progress/calculator/internal/calc Build from the module root: go run ./calculator go run ./calculator or go build ./calculator go build ./calculator Scaling the Structure Scaling the Structure 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 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. Best Practices I’m Learning Best Practices I’m Learning Keep packages small and focused Use meaningful names (calc, parser, storage) Don’t over-engineer — start simple, refactor later Avoid circular dependencies (Go enforces this) Keep packages small and focused Use meaningful names (calc, parser, storage) Don’t over-engineer — start simple, refactor later Avoid circular dependencies (Go enforces this) Lessons from the Calculator Project Lessons from the Calculator Project Separating logic (operations.go) from entrypoint (main.go) makes testing easier. Error handling (like divide by zero) should be explicit. Import paths really matter — especially when using internal. Separating logic (operations.go) from entrypoint (main.go) makes testing easier. Error handling (like divide by zero) should be explicit. Import paths really matter — especially when using internal. I’ll continue sharing what I learn as I explore Go for MLOps and backend development. Next up: error handling and testing in Go. MLOps and backend development 👉 Check out my repo here: https://github.com/turman17/GoLang-progress And stay tuned for the next article!