ProgrammingBackend Developer

How is the work with packages and visibility implemented in Go? What are the key rules for code packaging, exporting, public and private components?

Pass interviews with Hintsage AI assistant

Answer.

In the Go language, code organization through packages and the visibility system plays a key role in modularity, reusability, and encapsulation of logic.

Background

Unlike many classic object-oriented languages with inheritance, Go was designed from the ground up around simple modules — packages with clear rules for export and imports. This is a legacy of the philosophy of minimalism, simplicity, and strict dependency control.

Problem

If components do not have clear visibility, chaos in naming arises, making it difficult to track dependencies between files, and increasing the risk of accidentally using private implementation details. Errors in export organization can lead to hard-to-debug bugs and violations of encapsulation.

Solution

In Go, every file must belong to some package (package name). The entire scope of visibility in Go is defined as follows:

  • Starts with a capital letter (exported/public)
  • Starts with a lowercase letter (private)

All functions, types, variables, constants, methods whose names start with a capital letter are exported from the package and available to other packages after import. The rest are only accessible within the package.

Example structure:

project/
│
├── main.go          // package main
└── mathutil/
    └── mathutil.go  // package mathutil

Example code:

// mathutil/mathutil.go package mathutil // Public - exported function func Sum(a, b int) int { return a + b } // private - unexported function func subtract(a, b int) int { return a - b }
// main.go package main import ( "fmt" "project/mathutil" ) func main() { fmt.Println(mathutil.Sum(2, 3)) // 5 // fmt.Println(mathutil.subtract(3,2)) // Compilation error! subtract is unexported }

Key features:

  • Simple and transparent export/import model
  • Export is defined solely by the first letter of the name — no keywords are required
  • Each directory implements exactly one package

Tricky questions.

Can a variable or function declared with a lowercase letter be exported by other means?

No. In Go, there are no keywords like public, private, everything is defined only by the first letter of the name (unicode category — uppercase letter).

Can functions from one file in a package use private functions from another file of the same package?

Yes, the visibility is limited at the package level, not at the file level. All private elements are accessible in all files of the same package.

Example code:

// mathutil/helpers.go package mathutil func hiddenHelper() int { return 42 } // mathutil/mathutil.go package mathutil func UseHelper() int { return hiddenHelper() // available! }

Can you create the same function or type with the same name within one package in different files?

No, there will be a compilation error — names must be unique within a package, even if declared in different files.

Typical mistakes and anti-patterns

  • Accidentally exporting implementation details by starting a name with a capital letter
  • Splitting packages into files with the same type names
  • Placing too much logic in the main package (violating modularity)
  • Using export solely for testing (it's better to use a xxx_test.go file in the same package)

Real-life example

Negative case

A large team places all utility functions and types in a utility package with everything exported:

Pros:

  • Quickly use any functions without scrutiny

Cons:

  • Loss of encapsulation (implementation details easily become dependencies of other packages)
  • Difficulty in refactoring and testing

Positive case

The team carefully divides the code into modules by business areas, exporting only the API:

Pros:

  • Clear boundary of package interfaces
  • Convenient to scale the project

Cons:

  • Discipline is required to maintain the project's structure