Go's `internal` Directory: Private by Convention
Go's `internal` directory creates private packages within your module, making them inaccessible to external projects. Use it for helper logic you don't want to support as a public API.
WHY IT EXISTS As a Go project grows, you often need to split code into multiple packages for better organization. However, not all of these packages are meant for public consumption. Exposing internal helper packages creates an implicit API contract that you must maintain, restricting your ability to refactor implementation details.
THE MENTAL MODEL The internal directory is a special folder name recognized by the Go toolchain that acts as a visibility barrier. Think of it as a fence around parts of your module. Any package inside an internal directory can only be imported by code living under the same module root. It is Go's way of enforcing "for internal use only" at the compiler level.
HOW IT WORKS You create a directory named internal in your project root. Inside, you can place packages like my-module/internal/auth. Code within my-module can import this package using its full path, github.com/user/my-module/internal/auth. If another module tries to import that same package, the Go compiler will report an error, preventing the build. This rule applies to any directory named internal, no matter how deeply it is nested.
WHEN TO USE IT Use the internal directory for any code that is not part of your module's public, stable API. This is perfect for helper functions, data structures for internal processing, or experimental features you are not ready to expose. It gives you the freedom to change, refactor, or delete this code without it being a breaking change for your module's users.
WHEN NOT TO USE IT Do not place packages in internal if you intend for them to be used by other modules. If a package contains reusable logic that could be valuable to others, it should be a regular, public package. Moving a package out of internal later is a breaking change for your own module's structure and signals that the code was not initially designed for public use.
ONE CANONICAL EXAMPLE A command-line tool, my-cli, needs custom authentication logic. You don't want other programs depending on this specific implementation. You can structure it as my-cli/internal/auth/auth.go. The main.go file in the project root can import it with import "github.com/user/my-cli/internal/auth". Any other project attempting the same import will get a compile-time error, protecting your implementation details.
Read the original → go.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.