# When to Use /internal vs /pkg Directory in Go Projects

> Learn when to use /internal for private Go code and /pkg for public libraries. Understand Go's project layout conventions for secure and stable external imports.

- Repository: [golang-standards/project-layout](https://github.com/golang-standards/project-layout)
- Tags: best-practices
- Published: 2026-03-06

---

**Use `/internal` for private implementation details that the Go compiler actively prevents external modules from importing, and `/pkg` for stable public libraries intended for safe external consumption.**

The `golang-standards/project-layout` repository defines the canonical structure for Go applications, establishing two distinct directories that control API visibility and module boundaries. Choosing between `/internal` and `/pkg` determines whether your code remains a private implementation detail or becomes part of your module's public contract with other projects.

## Understanding the /internal Directory

According to [`internal/README.md`](https://github.com/golang-standards/project-layout/blob/main/internal/README.md) in the golang-standards repository, the `/internal` directory contains **private application and library code** that must not be imported by other modules. The Go compiler enforces this restriction at the language level: packages inside an `internal` folder can only be imported by code that shares the same ancestor directory.

This compiler enforcement means that moving code into `/internal` guarantees third-party projects cannot accidentally depend on your implementation details. The directory is ideal for database adapters tuned to your internal schema, private configuration structures, or HTTP handlers that rely on internal assumptions.

## Understanding the /pkg Directory

As documented in [`pkg/README.md`](https://github.com/golang-standards/project-layout/blob/main/pkg/README.md), the `/pkg` directory holds **library code that is safe for external applications** to use. Unlike `/internal`, packages in `/pkg` are importable by any external module and represent a commitment to maintaining a stable API.

Code placed here should have clear documentation and a commitment to backward compatibility. Common examples include generic loggers, configuration parsers, or client libraries for external services that other projects may profit from.

## When to Use /internal

Choose `/internal` when your code meets any of these criteria:

- **Implementation details subject to change**: The package contains logic that could change without notice and should not be part of a public contract.
- **Tight application coupling**: The code is specific to your application's internal architecture and isn't useful or safe for generic use.
- **Compiler-enforced privacy required**: You want the Go compiler to actively prevent accidental imports from third-party projects.

For example, a set of database adapters tuned for your service's internal schema belongs in `/internal` because external projects should not depend on your specific data access patterns.

## When to Use /pkg

Choose `/pkg` when your code meets these standards:

- **Stable, reusable API**: The package offers functionality that other projects may legitimately need, such as a generic JSON utility or a standard logger.
- **Public commitment**: You are prepared to maintain backward compatibility or provide clear migration paths for breaking changes.
- **Visual API boundary**: You want the directory structure to serve as a clear signal that the code is publicly supported and versioned.

## Practical Code Examples

### Private Helper in /internal

The following code lives in [`internal/auth/token.go`](https://github.com/golang-standards/project-layout/blob/main/internal/auth/token.go) and generates tokens that should never be used outside the module:

```go
// internal/auth/token.go
package auth // import path: example.com/project/internal/auth

import (
    "crypto/rand"
    "encoding/base64"
)

// NewToken generates a secret token used only inside the project.
func NewToken() (string, error) {
    b := make([]byte, 32)
    if _, err := rand.Read(b); err != nil {
        return "", err
    }
    return base64.StdEncoding.EncodeToString(b), nil
}

```

Usage within the same module is allowed:

```go
// cmd/server/main.go
package main

import (
    "log"
    "example.com/project/internal/auth"
)

func main() {
    t, err := auth.NewToken()
    if err != nil {
        log.Fatal(err)
    }
    log.Println("internal token:", t)
}

```

A different module attempting to import `example.com/project/internal/auth` will receive a compiler error.

### Public Library in /pkg

The following utility in [`pkg/jsonutil/pretty.go`](https://github.com/golang-standards/project-layout/blob/main/pkg/jsonutil/pretty.go) is safe for external consumption:

```go
// pkg/jsonutil/pretty.go
package jsonutil // import path: example.com/project/pkg/jsonutil

import (
    "bytes"
    "encoding/json"
)

// Pretty returns an indented JSON representation of v.
func Pretty(v any) (string, error) {
    b, err := json.MarshalIndent(v, "", "  ")
    if err != nil {
        return "", err
    }
    return string(b), nil
}

```

External modules can safely import and use this package:

```go
// another-module/main.go
package main

import (
    "log"
    "example.com/project/pkg/jsonutil"
)

func main() {
    s, _ := jsonutil.Pretty(map[string]string{"hello": "world"})
    log.Println(s)
}

```

## Summary

- **`/internal`** stores private code with compiler-enforced import restrictions, protecting implementation details from external dependencies.
- **`/pkg`** stores public libraries with stable APIs intended for external consumption, requiring commitment to backward compatibility.
- Both directories can coexist in a single project to separate public surface area from private logic.
- The `golang-standards/project-layout` documentation in [`internal/README.md`](https://github.com/golang-standards/project-layout/blob/main/internal/README.md) and [`pkg/README.md`](https://github.com/golang-standards/project-layout/blob/main/pkg/README.md) provides the authoritative definitions for these conventions.

## Frequently Asked Questions

### Can external packages import code from /internal?

No. The Go compiler strictly enforces that packages in `/internal` can only be imported by code within the same module tree that shares the same ancestor directory. Any external import attempt results in a compilation error, making this a stronger guarantee than documentation alone.

### Is /pkg required for all public APIs?

While not strictly required by the Go compiler, `/pkg` is the community-standard convention indicating code intended for external use. Public APIs can technically reside elsewhere, but placing them in `/pkg` provides a clear organizational signal to other developers about which code is considered stable and supported.

### Can a project use both /internal and /pkg together?

Yes. This is the recommended pattern: expose stable public libraries in `/pkg` while keeping all supporting logic, helper functions, and implementation details in `/internal`. This separation makes the architectural intent of each package explicit and leverages the compiler-enforced privacy of `internal`.

### What happens if I move code from /pkg to /internal?

Moving code from `/pkg` to `/internal` is a breaking change for any external importers. The code immediately becomes inaccessible to external modules due to the compiler's import restrictions, effectively forcing external projects to find alternative implementations or fork the code.