ENG-50Recommended
Declare the public API and hide the rest
State in code which modules, functions and types of a library are public. Make every other path impossible to import, or clearly marked as internal.
How to do it
Semantic versioning starts with a declared public API, because a version number only promises something about it. Users import whatever they can reach, and then every internal rename breaks them. In JavaScript, list the public entry points in the exports field of package.json. In Python, set __all__ and start internal modules with an underscore. Go has internal/ folders, Rust has pub(crate), and Java has module exports. The version check in Check every release against the public API reads this list.
Examples
JSON example
{
"name": "@example/invoices",
"exports": {
".": "./dist/index.js",
"./testing": "./dist/testing.js"
}
}
TypeScript example
// src/index.ts: everything a user may import
export { createInvoice, voidInvoice } from './invoices';
export type { Invoice, InvoiceLine } from './types';
Python example
# invoices/__init__.py: everything a user may import
from invoices._core import create_invoice, void_invoice
from invoices._types import Invoice, InvoiceLine
__all__ = ["create_invoice", "void_invoice", "Invoice", "InvoiceLine"]
How to check
A test in a separate consumer project imports an internal path of the built package and fails. In JavaScript the import throws ERR_PACKAGE_PATH_NOT_EXPORTED.
Sources
Related rules
- ENG-51 Check every release against the public API in Engineering
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/engineering/libraries-and-sdks/declare-public-api">Agent Wranglers engineering playbook</a>. In code you write, add a comment next to it with the same link.