Public API Surface

When you write modules, packages, and classes, you’re implicitly designing a public API, which consists of names that other code should use and rely on. Everything else is internal implementation details and shouldn’t be exposed to users.

Python doesn’t enforce public and private names. Again, it relies on a naming convention to signal which names are public and which are non-public:

  • Public names have no leading underscore. For example, counter, convert(), API_KEY.
  • Non-public names start with a single leading underscore (_). For example, _items, _remove_spaces() or _CACHE_DIR.

When designing a module or package’s public surface, these best practices help you use public and non-public names effectively:

  • Design a well-thought-out and future-proof public API. Decide which packages, modules, constants, functions, classes, methods, and objects will be part of your project’s public interface. Find descriptive names for them and treat them as a contract for users. This practice will help you avoid breaking your users’ code when they upgrade to a new version of your code.
  • Use non-public names for implementation details. Use non-public names for implementation details that may change in future versions of your code, such as internal helpers, temporary attributes, or any other objects you don’t want users to access in their code. This practice will help you prevent unintended uses of your code.
  • Don’t use non-public names from other developers’ code. Follow the convention and avoid using non-public names from third-party code in your own code. This will prevent your code from breaking if the third-party code changes its internal implementation.
  • Avoid overusing double leading underscores. For internal names, a single underscore is the right choice. In class bodies, names like __value can be used to avoid attribute clashes in inheritance hierarchies because they trigger name mangling. Name mangling only applies to identifiers defined in class bodies.
  • Keep the public surface small and stable. Expose only what callers really need. A small, well-documented public API is easier to maintain and version than a large, leaky one.

PEP 8 also recommends using the __all__ attribute in a module to explicitly declare its public interface, while still prefixing internal names with an underscore. It primarily affects from module import * and documentation tools, not access control. Following these conventions makes it clear which names you intend to keep stable.

To see these ideas in action, consider a module that mixes public objects and internal helpers:

🔴 Avoid this:

Python taxes.py
TAX_RATE = 0.20

def calculate_tax(amount):
    """Return the tax for the given amount."""
    return round_amount(amount * TAX_RATE)

def round_amount(amount, decimals=2):
    return round(amount, decimals)

All names in this module seem to be part of its public API. However, your real intention is that only TAX_RATE and calculate_tax() would be used outside the module. With no visual clue, your users may start importing and using round_amount() directly in their code. If you later decide to rename or remove this function, you risk breaking your users’ code.

Favor this:

Python taxes.py
__all__ = ["TAX_RATE", "calculate_tax"]  # Optional

TAX_RATE = 0.20

def calculate_tax(amount):
    """Return the tax for the given amount."""
    return _round_amount(amount * TAX_RATE)

def _round_amount(amount: float, decimals=2):
    return round(amount, decimals)

In this version, TAX_RATE and calculate_tax() are explicitly part of the public API and also listed in __all__. In contrast, _round_amount() is now marked as a non-public helper function. This way, your users know which names they can rely on, and you retain the freedom to change the internal helper without breaking their code.

Tutorial

Single and Double Underscores in Python Names

Learn Python naming conventions with single and double underscores to design APIs, create safe classes, and prevent name clashes.

intermediate best-practices python

For additional information on related topics, take a look at the following resources:


By Leodanis Pozo Ramos • Updated Jan. 8, 2026 • Reviewed by Martin Breuss