Learn how to structure Go code the right way: from single-file projects to packages, internal modules, and proper imports, explained with a calculator example.Learn how to structure Go code the right way: from single-file projects to packages, internal modules, and proper imports, explained with a calculator example.

How to Organize Your Go Projects Like a Pro

2025/10/06 11:36
4분 읽기
이 콘텐츠에 대한 의견이나 우려 사항이 있으시면 crypto.news@mexc.com으로 연락주시기 바랍니다

\ 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.


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) } 

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.


Introducing Packages

In Go, every file belongs to a package.

By convention:

  • main → executable program
  • others (like calculator, utils, etc.) → reusable logic

Here’s how I split the calculator project:

calculator/ │ ├── main.go └── calculator/     └── operations.go 

\

  • main.go → handles input/output
  • operations.go → defines functions like Add, Subtract, etc.

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 } 

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)   } } 

Notice how main.go is now clean: it doesn’t worry about the math itself, just how to use it.


Accessing Functions from Different Files

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.

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 } 

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)   } } 

Why this import path is required

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 

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.

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

  • 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 

or

go build ./calculator 

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 

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

  • 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

  • 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.

\ 👉 Check out my repo here: https://github.com/turman17/GoLang-progress

And stay tuned for the next article!

면책 조항: 본 사이트에 재게시된 글들은 공개 플랫폼에서 가져온 것으로 정보 제공 목적으로만 제공됩니다. 이는 반드시 MEXC의 견해를 반영하는 것은 아닙니다. 모든 권리는 원저자에게 있습니다. 제3자의 권리를 침해하는 콘텐츠가 있다고 판단될 경우, crypto.news@mexc.com으로 연락하여 삭제 요청을 해주시기 바랍니다. MEXC는 콘텐츠의 정확성, 완전성 또는 시의적절성에 대해 어떠한 보증도 하지 않으며, 제공된 정보에 기반하여 취해진 어떠한 조치에 대해서도 책임을 지지 않습니다. 본 콘텐츠는 금융, 법률 또는 기타 전문적인 조언을 구성하지 않으며, MEXC의 추천이나 보증으로 간주되어서는 안 됩니다.

USD1 Genesis: 0 Fees + 12% APR

USD1 Genesis: 0 Fees + 12% APRUSD1 Genesis: 0 Fees + 12% APR

New users: stake for up to 600% APR. Limited time!