The internal folder in Go is one of the few "magic" features in the Go language where if you use it, you get a bunch of benefit without having to do too much.

The magic of the internal folder


The internal folder in Go is one of the few “magic” features in the Go language where if you use it, you get a bunch of benefit without having to do too much.

In organising a Go Module in the official Go documentation, they have this to say:

Larger packages or commands may benefit from splitting off some functionality into supporting packages. Initially, it’s recommended placing such packages into a directory named internal; this prevents other modules from depending on packages we don’t necessarily want to expose and support for external uses. Since other projects cannot import code from our internal directory, we’re free to refactor its API and generally move things around without breaking external users.

The project structure for a package is thus:

project-root-directory/
  internal/
    auth/
      auth.go
      auth_test.go
    hash/
      hash.go
      hash_test.go
  go.mod
  modname.go
  modname_test.go

Let’s talk about this practically.

If you work at a larger company where you have a few different microservices, you’ll often find that some of these services need to share common code or utilities. However, you don’t want every piece of code to be accessible to all services or external packages. By using the internal package you are doing two things:

  • Signalling that the code within is “private” - you don’t intend for any other team or service to import it.
  • Actually prevent them from doing so - even if they want to break this rule, the Go tooling will not allow it.

You’ll see internal folders used in lots of large open source projects. Kubernetes has a bunch of them - here is one example.

What is the downside of using the internal folder?

It’s basically the same as the benefits - it prevents you from importing code from within it. If you’re new to Go and don’t know about this “trick”, it can be quite hard to figure out as IDEs do not give you great guidance on this as part of autocomplete in my experience. It also means that if you ever decide to “promote” something to be importable, it can require a refactor.

My general advice is that if you are not sure - start with it in an internal folder. Allowing others to import your code should be a conscious choice, and you do not want it to happen by accident.