Public, Private - Upper Case, Lower Case:
Categories:
Go's Visibility Rules: Public vs. Private with Case Sensitivity

Understand how Go's unique approach to identifier casing dictates public and private access, enabling clear package boundaries and robust code organization.
In Go, there are no explicit keywords like public
, private
, or protected
to manage the visibility of identifiers (variables, functions, types, methods, and fields). Instead, Go leverages a simple yet powerful convention based on the case of the identifier's first letter. This article will delve into how this mechanism works, its implications for package design, and best practices for structuring your Go projects.
The Casing Rule: Uppercase for Public, Lowercase for Private
Go's visibility rules are straightforward: an identifier is 'exported' (public) if its name begins with an uppercase letter, and 'unexported' (private) if it begins with a lowercase letter. This rule applies uniformly across all identifier types within a package. Exported identifiers are accessible from outside their declaring package, while unexported identifiers are only accessible from within the same package.
flowchart TD A[Identifier Declaration] --> B{First Letter Uppercase?} B -- Yes --> C[Exported (Public)] C --> D[Accessible from any package] B -- No --> E[Unexported (Private)] E --> F[Accessible only within its own package]
Go's Identifier Visibility Flowchart
package mypackage
import "fmt"
// PublicVariable is exported and accessible from other packages.
var PublicVariable string = "I am public"
// privateVariable is unexported and only accessible within 'mypackage'.
var privateVariable string = "I am private"
// PublicFunction is exported.
func PublicFunction() {
fmt.Println("This is a public function.")
privateFunction() // Can call private functions within the same package
}
// privateFunction is unexported.
func privateFunction() {
fmt.Println("This is a private function.")
}
// PublicStruct is an exported type.
type PublicStruct struct {
// ExportedField is an exported field.
ExportedField string
// unexportedField is an unexported field.
unexportedField string
}
// PublicMethod is an exported method for PublicStruct.
func (ps *PublicStruct) PublicMethod() {
fmt.Println("This is a public method.")
}
// privateMethod is an unexported method for PublicStruct.
func (ps *PublicStruct) privateMethod() {
fmt.Println("This is a private method.")
}
Examples of public and private identifiers in Go
Implications for Package Design and API Contracts
This simple rule has profound implications for how you design your Go packages. It forces developers to be explicit about what parts of their code are intended for external consumption and what parts are internal implementation details. This clarity helps in creating well-defined API contracts for packages, making them easier to use and maintain.
// main.go (in a different package)
package main
import (
"fmt"
"yourproject/mypackage" // Assuming 'mypackage' is in 'yourproject'
)
func main() {
fmt.Println(mypackage.PublicVariable) // OK: Accessing exported variable
// fmt.Println(mypackage.privateVariable) // ERROR: Cannot access unexported variable
mypackage.PublicFunction() // OK: Calling exported function
// mypackage.privateFunction() // ERROR: Cannot call unexported function
myStruct := mypackage.PublicStruct{
ExportedField: "Hello from main!",
// unexportedField: "Cannot set this directly" // ERROR: Cannot access unexported field
}
fmt.Println(myStruct.ExportedField)
myStruct.PublicMethod() // OK: Calling exported method
// myStruct.privateMethod() // ERROR: Cannot call unexported method
}
Accessing identifiers from an external package
Best Practices and Common Pitfalls
While the casing rule is simple, adhering to best practices ensures maintainable and idiomatic Go code. Always consider the scope and intended use of an identifier before naming it. Avoid exposing internal details unnecessarily, as this can lead to tight coupling and make refactoring difficult.
1. Define Package API
Before writing code, clearly define the public interface (API) of your package. What functions, types, and variables should users of your package interact with?
2. Use Uppercase for API Elements
Implement the defined API using uppercase initial letters for all exported identifiers. This includes functions, types, methods, and struct fields that are part of the public contract.
3. Use Lowercase for Internal Details
All helper functions, internal state variables, and implementation-specific types that are not meant for external consumption should start with a lowercase letter.
4. Document Exported Identifiers
Provide clear and concise documentation for all exported identifiers. This is crucial for users of your package to understand its functionality. Go's godoc
tool relies on these comments.