Open-source project
golang-standards/project-layout avatar
golang-standards/project-layout

The Go project layout guide opens by telling you it is not a standard

Standard Go Project Layout

56,654 stars5,424 forksMakefileNOASSERTION

At a glance

What is it?
A community document that proposes around twenty directories for a Go repository, then tells you to clone it, keep what you need and delete the rest. Nineteen of its forty-five top-level entries are translations of the same file, its Makefile has no targets, and only the internal directory is enforced by the compiler.
Who is it for?
Read this document as a menu of conventions rather than a specification, which is exactly what it asks to be read as, and take the /cmd, /pkg and /internal split as the part with actual force behind it. Do not adopt the tree wholesale, because the document explicitly rules that out and names vendor as not universal, and do not apply it to a small project, where it says a single main.go file and go.mod is enough.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 155 days ago.
What is it written in?
Mainly Makefile, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The first substantive line is a disclaimer against its own authority

The document spends its opening section arguing against its own standing, and that is the first thing to understand about it. It states in bold that this is not an official standard defined by the core Go development team, and that it is instead a set of common historical and emerging project layout patterns in the Go ecosystem, adding that some of these patterns are more popular than others. It then redirects readers to first-party material instead, naming the official page on organizing a Go module, and observing that the core team publishes its own general guidelines there, including the internal and cmd directory patterns that this repository also describes. The consequence for anyone treating it as authority is direct. Only a subset of what follows is Go's own guidance, and the remainder is convention with nothing behind it, so a repository can break nearly every directory convention in this document and still compile cleanly. The document is candid about the limit the other way as well, saying it is intentionally generic, does not impose a specific Go package structure, and does not attempt to cover a structure like Clean Architecture.

The tree lists twenty directories, and the text tells you to delete most of them

The top level of the repository is where the layout becomes concrete, and it is a long menu: api, assets, build, cmd, configs, deployments, docs, examples, githooks, init, internal, pkg, scripts, test, third_party, tools, vendor, web and website, alongside go.mod, a Makefile, an .editorconfig and a LICENSE file. Then the document tells you what to do with that list, in unusually direct language: clone the repository, keep what you need and delete everything else, and just because a directory is there it does not mean you have to use it all. It adds that none of these patterns appear in every single project, and singles out vendor as not universal. That is a strong claim about the project's own contents, and it matters because the usual way people adopt a layout guide is to copy the tree wholesale. The consequence is that wholesale copying is the one approach the document rules out, and that several of those twenty directories, vendor among them, are optional in a way no tooling will ever tell you about.

Only /internal is enforced by the compiler, and /cmd is convention

Two directories get real treatment and they are not equally supported. The internal directory holds private application and library code, described as the code you do not want other projects importing, and the document is explicit that this pattern is enforced by the Go compiler itself, linking the Go 1.4 release notes for the mechanism. It also notes you are not limited to a single top-level internal directory, that more than one can exist at any level of the tree, and that you can optionally split internal packages to separate shared from non-shared code, which it marks as not required and in particular not necessary for smaller projects. The cmd directory has no comparable backing. Its guidance is that each application directory should match the executable you want, that you should keep little code there, that reusable code belongs in pkg, that non-reusable code belongs in internal, and that a small main function importing from those two and nothing else is common. The consequence is that an internal boundary is a compile error when crossed, while a misplaced cmd or pkg package is invisible to the toolchain and surfaces only in review.

The module path assumes GitHub, and the dot rule depends on your Go version

The go.mod at the root is a two-line placeholder: a module path reading github.com/YOUR-USER-OR-ORG-NAME/YOUR-REPO-NAME, and a go directive of 1.19. The surrounding text handles both with caveats rather than instructions. On the path, it says the file assumes your project is hosted on GitHub but that this is not a requirement, and that the module path can be anything, provided the first component contains a dot. It adds that the current version of Go no longer enforces that, that on slightly older versions your builds will fail without it, and links two upstream issues for the detail. On the version, the prose is talking about modules becoming ready for production back at Go 1.14, a considerably older milestone than the 1.19 written into the manifest. The consequence is that the file and the text around it sit on different timelines, and a self-hosted project has to correct the path before anything else will build.

Nineteen translations, two of them Simplified Chinese, one marked with a question

Nineteen of the forty-five entries at the top level are translations of the same document, and that is where the maintenance shape of the repository shows. There are versions in Korean, Traditional Chinese, French, Japanese, Brazilian Portuguese, Spanish, Romanian, Russian, Turkish, Italian, Vietnamese, Ukrainian, Indonesian, Hindi, Persian, Belarusian and Bengali, plus two files both labelled Simplified Chinese. Those two are the detail worth naming, because they are separate files with different names and the second one carries a literal question mark marker beside it in the list, with nothing in the document explaining what that marker means. The consequence of nineteen parallel files is straightforward and unavoidable. The content can drift between languages, and a reader who lands on a translation that has fallen behind is reading a different document from the one the repository currently describes, with nothing available inside the repository to tell them which version is current.

The detected primary language is Makefile, and the Makefile has no targets

The repository's detected primary language is Makefile, which is worth explaining rather than repeating as a fact about the project. The Makefile at the root contains exactly one line, a comment instructing that scripts be called from the scripts directory. There are no targets in it, nothing to invoke, and no build of any kind. That is consistent with what the repository actually is, a document rather than a program, and it also means the language statistic is an artifact of having one Makefile-shaped file sitting among dozens of markdown files. The scripts directory that the comment points at is present in the tree, alongside githooks and a build directory, so the structure being described is laid out in the repository itself. The consequence is that anyone arriving expecting a tool to run finds a Makefile that does nothing, and anyone reading the language badge comes away with a wrong picture of the kind of project this is.

golint is called out as deprecated, and one style link is an archived copy

The tooling advice is aging in a different way from the layout advice, and it should be read as dated rather than current. The document points readers at gofmt for formatting and at a talk and an effective Go page for naming, then states that the previous standard linter, golint, is now deprecated and not maintained, recommending a maintained linter such as staticcheck instead. The style references that follow are a mix of live and dead: a conference talk on names, a blog post on package names, the code review comments wiki page, and a package style guideline that resolves to a web archive snapshot rather than the original site. Four conference talks are linked as background as well, covering industrial programming, general best practices, anti-patterns, and how to structure Go applications. The consequence is that a reader following the advice lands on a mixture of first-party guidance, third-party talks and one archived copy, which makes the repository a good record of how the community discussed layout rather than a maintained specification of it.

Editorial conclusion

Read this document as a menu of conventions rather than a specification, which is exactly what it asks to be read as, and take the /cmd, /pkg and /internal split as the part with actual force behind it. Do not adopt the tree wholesale, because the document explicitly rules that out and names vendor as not universal, and do not apply it to a small project, where it says a single main.go file and go.mod is enough. Three things to check before you follow it. Read the licence yourself, since the repository's licence field is unclassified even though a LICENSE file is present. Edit the module path, because the placeholder assumes GitHub and a self-hosted project will not build unchanged. And treat the tool advice as dated, since golint is called out as deprecated and one of the linked style guides already resolves to an archive snapshot.

Frequently asked questions

Is the Standard Go Project Layout an official standard?

No, and the document says so in bold near the top. It describes itself as a set of common historical and emerging project layout patterns in the Go ecosystem, notes that some are more popular than others, and points readers to the official Go documentation page on organizing a module instead, which covers the internal and cmd patterns.

Should I use this layout for a small Go project?

The document says not to. It states that if you are learning Go, or building a proof of concept or a simple project for yourself, this layout is an overkill, and that a single main.go file together with go.mod is more than enough. It also advises cloning the repository, keeping what you need and deleting everything else.

What does the /internal directory do in Go?

It holds private application and library code that you do not want other projects importing, and the boundary is enforced by the Go compiler rather than by convention. You can have more than one internal directory at any level of the tree, and splitting shared from non-shared internal code is described as optional rather than required.

Which linter should a Go project use?

The document names gofmt for formatting and says the previous standard linter, golint, is deprecated and not maintained, recommending a maintained linter such as staticcheck. Its linked style references are a mix, and one of them, the package style guideline, resolves to a web archive snapshot rather than the original page.

Official sources

  1. Official README
  2. Project repository