Why I use the internal folder for a Go-project

5 min read Original article ↗

Andreas

This short text is another view to the internal folder discussion. In the article Don’t Put All Your Code In Internal Ido Perlmuter writes, why it is a bad idea to use the internal folder in Go. After that was linked inside the Golang Weekly Newsletter I want to write about that topic from another perspective. At all I think that article is a great start for discussions. I think Ido did not think about all aspects, when he argued that it should be possible to use packages from other projects.

But let’s start with my perspective:

Versioning and modules

In Go Everything starts inside a module. That module lives normally inside of a repository and that repository has a version. For Go Modules semantic versioning has to be used. Here it is important, that inside a major version the API should not have incompatible changes. That means that you can add features, but not change something existing.

Exported and Unexported Elements

When creating a package it is important to think, which elements should be visible to other packages. Because everything public can be used and should not change inside the major version.

So it is a good start to have just unexportet elements. When there will be the need for an external call just change the name of that element and make it exported. Then just run go test or go build and the compiler will help you and tell you where that element is called inside your project.

For structures the visibility to other packages is also important. If I want the json package to encode or decode a User that type needs to be exported and all the relevant fields.

type User struct {
name string
Age int
}

In that example above, name will be not encoded or decoded from or into json, because it is not exported. So sometimes it is important to export things because your package calls another package.

But keep in mind everything exported will be part of the API of that package.

Packages vs. Programs

It is important to get the difference between a package and a program. Because that is a complete different approach of developing.

By creating a program the target is to create a runnable binary. When starting with a program you should not think about a big amount of different packages. Start with the main package. If things are getting bigger than refactor your code into smaller logical pieces.

The bigger your program and your project gets, the need for an additional abstraction like other packages could be necessary. But that comes not for free. It will make the codebase more complex and it will be harder to maintain. But that packages are developed not for external use. There are part of a refactoring process and the API of that package has just the use cases of the original project in mind. If now third party programs start to import that package, the maintaining of that packages will be more complicated.

So what can I do, when I don’t want other projects to use it?

Yes! Use the internal folder.

But it is total different, when a package is created for external use. That package will be inside an own repository and own module. In that case the versioning is used just for that package. Also the scope of that package is different. One package should do one thing. The rule of thumb is, when it is easy to find a name, the scope is right. You can find more information at Effective Go about naming a package.

Is it safe to use other packages?

Another question is security. When I use another package I have to trust the maintainer. Because that package could always change. Or what is about security issues? Is that project or package still maintained?

When my program needs to use UUIDs and I don’t want to implement that by my own, I can search for third party packages. At pkg.go.dev I found github.com/google/uuid. Before I use that package I check:

  • Do I trust the maintainer? Yes, because it is google.
  • Could the API have breaking changes? That would be for major versions 0. No, that package has Version 1 or higher.

So in that case I would say: yes, I could use that package. If a package has not version 1 it is always a risk, because a lot of things can change, which will break your build.

But whats about a package, which is part of another project? Here is the risk much higher that something will break. But there is another solution. If a big project has some cool packages, which should be open source, they can publish them as an own repository.

A good example about a project, where you could use packages of is Gobufallo. The project has a lot of repositories inside, which could be used by other projects. In that case the maintainers had decided to provide that functionality of some packages to third party users.

I would recommend something little different. Use own repositories for packages you like to share with the community. The rest should be inside the internal folder.