A shared library is defined less by what it does than by the shape it presents to everything that depends on it: its public surface, the contracts it implies, and the cost of changing either. Two bits of work from recently make that concrete. One was an export that was never declared. The other was a small change that quietly broke a numeric assumption.

Making implicit exports explicit

A couple of icon components from a shared plugin library were being used by a downstream service without being part of the library's declared public surface. The consumer reached in by path, something like import X from 'library/internal/path' rather than import X from 'library'.

That works, right up until it doesn't. Importing by internal path creates a hidden dependency on an implementation detail. Reorganise the library, move the internal path, and the consumer breaks. The library's authors have no way of knowing anyone relied on that path, because nothing declared the relationship.

The fix was to add the components to the library's declared exports. It's close to a one-line change, but it earns its keep: it makes the dependency visible to the maintainers, it brings the components under the same deprecation and versioning treatment as the rest of the public API, and it lets static analysis trace the import graph correctly. Libraries with undeclared consumers tend to become hard to refactor, because changes that look safe from the library's side turn out to break things elsewhere. Declaring the export brings the relationship into the open.

A breaking change disguised as a small one

The shared Node logging library is built on Winston, which ships a default set of severity levels (error, warn, info, and so on), each with a numeric priority where lower means more severe and error is level 0. The gap was that nothing sits above error. There's no way to say "this is worse than an error, this is on fire and needs a human now". critical plays that role in syslog and most structured logging conventions, but Winston doesn't include it by default.

Adding a custom critical level at priority 0 and shifting the existing levels up by one fixes that. The change is tiny in lines of code and deceptively large in blast radius. Levels are usually referenced by name, like logger.error(...), which is fine. But any code comparing levels numerically ("only process events with level <= 1") is now pointing at a different level than before, because error has moved from 0 to 1. Every numeric comparison that wasn't explicitly about critical is suddenly off by one.

That's why a change like this ships as a major version bump and asks consumers to update deliberately. The library exposes the new level and documents the shift; downstream services audit their numeric comparisons before upgrading. Writing the level was the easy part. The cost is rolling it through everything that depends on the library.

The shape is the contract

Both of these are the same lesson from different angles. A shared library's public surface is a contract whether or not you've written it down. An undeclared export honours that contract by accident. A numeric level shift changes it without telling anyone. Maintaining a library that lots of things depend on is mostly the work of keeping the contract explicit: declaring what's public, versioning what changes, and making the coordination visible instead of leaving consumers to find out when something breaks.