Is there a widely accepted standard directory structure for medium-sized Go projects?

I’m researching whether I should follow a standard directory structure for my new open source application. I want to do this in order to allow people to easily understand the code and to easily contribute.

It’s a medium-sized web app, which consists of a JSON API backend written in Go, together with a Next.js frontend written in Typescript.

According to Go Project Structure Best Practices, the most common options are:

  1. For small apps: flat structure. Putting all source files in the toplevel.
  2. For medium/large apps: splitting files into semantic groups. Each group directory is located in the toplevel.
  3. For large or “older” apps: the “traditional” structure, I think it’s referring to go-standards/project-layout. The toplevel consists of subdirectories by technical group (“pkg” for publicly-consumable sources, “internal” for only internally-consumable sources) as opposed to by semantical group. Within the pkg or internal subdirectory, one can still have semantical groups.

Question 1: Which one should I pick for my medium-sized app? Does the Go community have widespread agreement on which option to go for?

A friend is a big advocate of go-standards/project-layout. According to him, this is the Go standard, and allows every Go developers to immediately get started and easily understand the structure.

Question 2: Is it true that go-standards/project-layout is the golden standard with widespread acceptance?

When I look at actual Go repositories, I don’t actually see this as the most accepted layout. For example Hugo, HashiCorp Vault and Terraform all seem to use layout #2. Outside Kubernetes, I have a hard time finding go-standards/project-layout. Have I missed something?

Question 3: Go Project Structure Best Practices says that layout #3 is for “older” apps, and that s/he expects that the Go community will gradually drop this layout as Go modules start becoming more prevalent. What is your opinion on this?

Hello Hong,

I hope I can answer this, according to your requirements I think 2nd option would be good I have an example if you want to take a look

Go maintainers and google open source community always goes with 3rd option but it is considered highly opinion-based. Just like the concise vs verbose debate, I often see people take concise talk to next level and try to rename everything with 1 single letter everywhere which in my opinion is bad. If you can follow 3rd option, it could be good for people inside the go community since there are some benefits of following internal, example, pkg, cmd folder structures

However, if you are building a backend web API, I fail to see those benefits since you are the only end-user of that API, your endpoint documentation will actually become much more important than how you design your folder structure because we need to know API specs rather than your folder directories. 2nd option you mentioned also gives more clarity to people outside of go community which can be convenient

Obviously, many project maintainers in go are polyglots so they may prefer to choose 2nd option over 3rd to make things similar to their taste but if you are going to blindly follow java structure for folders, just don’t do it and go with 3rd option as you said.

I don’t agree with 3rd option is an old way of doing things and it will not be used in the future. It is just going with the specific way of organizing structures, not everyone likes it but it will depend on what you are actually building as a program and how you are comfortable with vendor, example, internal, pkg stuff

Do the 3rd if you think, you are building something reusable and big enterprise. Do the 2nd if you think you will be the only person who utilizes that in a single project or similar projects. Do the 3rd if it is something like a very small utility library

Both option 2 and option 3 are fine depending on your repo purpose. Here are some of the simplified cases I been through:

  1. Library packages only - option 2 because it simplifies import statement
  2. App but has importable library packages - option 3 with pkg/ for hosting library packages and app/ for program.
  3. App only - option 2 or option 3 depending on how big it is and whether do I need to standardize and automate the build process.

The point is to focus on how easy for your customers (Go developer) to import your packages.

Also, for purpose No.2, if you have library package repo like purpose No.1, it’s easier to upstream a package to there as well.

It’s general practice, not a standard / rule. It is written in its own documentations. Since most Go developers are aware of that layout so it saves time and resources to understand your repo (as in, I don’t need to analyze how your customized layout structure). Also, some parts are compliant to the Go compiler like internal/ directory.

Go and go-standards/project-layout do not restrict you or set golden rules on how you layout your project. They are learnt practices consolidated from various hiccups bumped by previous developers. My advice is use it as a reference or source of idea . Then, you continuously improve your repository over time.

Absurd. Go module has ZERO influence over layout structure pattern. What Go module did is to liberate a Go package away from the GOPATH pathing restriction and also solving some dependencies management problems at the same time. You still have to plan and do your project layout from the start.

The only ONE thing you have to be strict, go slow, and be careful with are those public API packages for other Go developer to import. As for why:

  1. You don’t want to alter a sub-package’s directory path that will cause the import statement to fail at customer end.
  2. If you want to delete a published API package, you need to roll out deprecation notice, take down notice and etc. Most of the resources are spent on communicating with your customers.
  3. Usually you need to roll out major version (those v2, v3, etc) releases. That has its own challenge.

Otherwise, any internal layouts is solely upto your choice as a maintainer.

Usually, having all the following will be good enough:

  1. a clear documentation on how you want others to contribute into your repository.
  2. source codes complying to Effective Go.
  3. [Good to have] makes your development process / automated build tools seamless (as in, I don’t need to mess with your build tools or tries to get a lot of dependencies installed in my local machine).

A couple of resources that go into some theory: