Public API
Problem
Consumers import implementation details directly from deep within modules, creating tight coupling that makes refactoring dangerous. Any internal reorganization or implementation change breaks dozens of imports across the codebase. There’s no clear boundary between public interfaces and private implementation, leading to unintended dependencies.
Solution
Explicitly designate which exports are intended for external use versus internal implementation. This communicates stability guarantees and helps consumers avoid depending on internals that might change.
Example
This example shows defining a public API through an index file that re-exports only the components intended for external use, keeping internals private.
// index.js - Public API surface
// Only these exports are part of the stable public API
export { Button } from './Button';
export { Input } from './Input';
// Internal components like ButtonBase, InputCore are NOT exported
// Consumers can only import what's explicitly exposed
// Usage - consumers import from the public API
import { Button, Input } from 'my-library';
Benefits
- Provides clear boundaries between public interfaces and private implementation.
- Enables safe refactoring by controlling what consumers can access.
- Communicates stability guarantees and versioning expectations.
- Makes it easier to evolve internals without breaking consumers.
Tradeoffs
- Requires discipline to maintain the public API boundary.
- Can limit flexibility if consumers need access to internal utilities.
- Needs careful API design to expose the right level of abstraction.
- May require versioning and deprecation strategies for API changes.