Go > Packages and Modules > Creating Packages > Internal packages

Go Internal Packages: Controlling Visibility

This snippet demonstrates how to create and use internal packages in Go to control the visibility of code within a larger project. Internal packages are a powerful tool for encapsulating implementation details and preventing unintended external dependencies.

What are Internal Packages?

In Go, a package named 'internal' within your module's source code directory has special visibility rules. Specifically, a package can only be imported by code within the directory tree rooted at the parent of the 'internal' directory. This provides a mechanism for restricting access to certain packages, making them private to specific parts of your project.

Creating an Internal Package

To create an internal package, create a directory named 'internal' within your Go module. Place your Go files containing the internal code within this directory. Note that the package name in your go file is still only 'internal', and not like 'parentDir/internal'.

package internal

// HelperFunction is accessible only from packages within the parent directory.
func HelperFunction() string {
    return "This is an internal helper function."
}

Example Usage

This code shows the correct usage of the internal package. The package 'mypackage' calls a function in the 'internal' package. The main package, residing in the directory tree above 'internal', can also call the 'internal' package.

// main.go
package main

import (
	"fmt"
	"myproject/mypackage"
	"myproject/internal"
)

func main() {
	fmt.Println(mypackage.PublicFunction())
	fmt.Println(internal.HelperFunction())
}

mypackage definition

This mypackage shows how another package inside your module's directory tree can import the internal package.

// mypackage/mypackage.go
package mypackage

import "myproject/internal"

// PublicFunction is accessible from outside the mypackage directory.
func PublicFunction() string {
	return "Public function calling internal: " + internal.HelperFunction()
}

Project Directory Structure

For this example to work, the directory structure should look like this: myproject/ ├── go.mod ├── main.go ├── mypackage/ │ └── mypackage.go └── internal/ └── internal.go Create a go.mod file with `go mod init myproject`

Concepts Behind the Snippet

Internal packages provide a mechanism for encapsulation within a Go module. They restrict the visibility of certain packages to only the modules within the same directory tree. This helps in creating well-defined APIs and prevents accidental dependencies on internal implementation details from external packages. It ensures that only designated parts of your project use these specific components.

Real-Life Use Case Section

Imagine you are building a web application. You might have a package responsible for handling database connections and queries. You want to ensure that only your data access layer uses this package, and not the presentation layer or other parts of the application. By making the database connection package an internal package, you can enforce this restriction, preventing accidental direct database access from other parts of the system. Another use case is within a framework where core components should only be accessible by the framework itself, not by users building applications on top of the framework. Internal packages help maintain the integrity and intended usage of the framework's core functionalities.

Best Practices

  • Clearly Define Boundaries: Identify which parts of your code should be considered internal implementation details and mark them as internal packages.
  • Avoid Overuse: Don't make everything internal. Use it judiciously to protect specific parts of your code that should not be directly accessed by other modules.
  • Document Intent: Clearly document why a package is internal to avoid confusion and ensure developers understand the intended usage.
  • Consider Alternatives: Evaluate if other mechanisms like unexported identifiers (lowercase) are sufficient before resorting to internal packages, as internal packages add a directory layer.

Interview Tip

When discussing internal packages in an interview, emphasize their role in encapsulation and controlling visibility within a Go module. Be prepared to explain the directory structure requirements and the implications for import paths. Also, discuss the trade-offs between using internal packages versus other visibility mechanisms like unexported identifiers.

When to Use Them

Use internal packages when you want to enforce a clear separation between the public API of your module and its internal implementation details. This is especially useful in large projects with multiple developers where it's important to prevent accidental dependencies on internal code. Use internal when you need to explicitly block access to a group of functions or types from outside your project's defined boundary.

Alternatives

Instead of using internal packages, you could rely solely on unexported identifiers (lowercase names) within a single package to control visibility. However, this only provides visibility within the same package. Another alternative is to break your project into smaller, independent modules. This provides stronger isolation but adds complexity to dependency management. Consider using code review processes to enforce API boundaries.

Pros

  • Encapsulation: Enforces clear boundaries between public API and internal implementation.
  • Reduced Dependencies: Prevents accidental dependencies on internal code.
  • Improved Maintainability: Makes it easier to refactor internal code without breaking external clients.
  • Increased Code Clarity: Signals intent and helps developers understand which parts of the code are considered stable and public.

Cons

  • Added Directory Structure: Requires an extra directory level, which can add to the project's complexity.
  • Potential Overuse: Can be overused, leading to unnecessary complexity and reduced flexibility.
  • Limited Scope: Only applies within the same module. Does not provide visibility control across different modules.

FAQ

  • Can I have multiple 'internal' directories in a single project?

    Yes, you can have multiple 'internal' directories. Each 'internal' directory restricts access to the packages within it based on the directory tree above it. Each 'internal' directory acts as an individual scope for controlling visibility.
  • What happens if I try to import an internal package from outside its allowed scope?

    The Go compiler will report an error, preventing you from importing the internal package. The error message will indicate that the import is not allowed.
  • Does using internal packages affect performance?

    No, using internal packages does not have a significant impact on performance. The visibility restriction is enforced at compile time and does not add any runtime overhead.
  • How do I test internal packages?

    You can test internal packages by creating test files within the same directory or within a subdirectory of the parent directory. The tests will have access to the internal package's members, allowing you to test its functionality.