Working with External Dependencies and Compatibility
Supported Angular Versions
Every library must clearly define the range of Angular versions it supports via the peerDependencies field in package.json. This range is not merely a declaration – it represents a commitment that the library has been tested and works correctly with each major.minor Angular release within the range.
Testing strategy: CI pipelines must include builds and test runs against the lowest, highest, and latest patch of every supported Angular major version (LTS releases). Matrix builds (e.g., using a CI matrix) are recommended.
Deprecated APIs: The library code must not use any Angular API marked as deprecated in the minimum supported version. If a newer, preferable API exists, it should be adopted without breaking the support contract (e.g., by providing a conditional fallback).
Version range updates: When Angular releases a new major version, the library team must evaluate compatibility, update the
peerDependenciesrange, and publish a new library version (usually a major bump if the previous Angular version is dropped, or a minor bump if the range is widened). This process must be completed within a reasonable timeframe to prevent consumers from being blocked.
Handling Breaking Changes in External Dependencies
External dependencies (Angular, RxJS, @artstesh/postboy, etc.) occasionally introduce breaking changes. The library’s public API may be affected, requiring updates.
Impact analysis: For every dependency major update, the maintainers must review the upstream changelog and identify any impact on the library’s components, services, or models.
Migration guides: If the upstream breaking change forces a change in the library’s own public API (e.g., a renamed symbol, altered signature, or removed feature), the library’s own version must be bumped, and a migration guide must be added to the
versions/folder describing how consumers should adapt. The migration guide must reference the upstream change and provide explicit migration steps.Transitional compatibility: When possible, the library should support both the old and new external dependency versions concurrently, using peer dependency ranges (e.g.,
"rxjs": "^6.5.0 || ^7.5.0"). This eases adoption for consumers. Once the old version is dropped, a major library version bump is required.
Restrictions on External CSS Frameworks
Libraries are forbidden from shipping or depending on external CSS frameworks (Bootstrap, Tailwind, Foundation, etc.) or their base styles unless every imported style can be fully controlled through the global --grain-* theming variables. The rationale is to prevent style conflicts and to preserve the integrity of the two‑level cascade.
No un-customizable global styles: External CSS that applies global, un‑scoped rules (e.g., resetting margins, setting box‑sizing, or defining font families on
htmlor*) is strictly prohibited. Such styles leak into the host application and interfere with its own theming.Component‑scoped imports: If a small, focused CSS snippet from an external source is necessary (e.g., a dropdown positioning algorithm), it must be wrapped in a component’s selector, and all its property values must be expressed through
var()chains pointing to global or component variables. It must never override the cascade.Whitelisting of themes: If an external library provides its own theming system, the only acceptable integration is to map its variables to
--grain-*variables at the application level. The library itself must not assume any particular external theme.Stylelint enforcement: A Stylelint rule should flag any import of a known CSS framework file or any
@importthat brings un‑scoped reset styles.
Dependency Hygiene and Approval
Maintaining a lean, secure dependency tree is critical for library stability and consumer trust.
Adding new dependencies: Any new runtime dependency (listed in
dependencies) must be proposed and justified in a pull request. The justification must include:Why the functionality cannot be implemented in‑house with reasonable effort.
The dependency’s license compatibility (must be approved by legal).
Its maintenance status (recent commits, active community).
Its size impact (evaluated with tools like
bundlephobia).
TypeScript typings: Dependencies must have built‑in TypeScript typings or a DefinitelyTyped
@types/*package. Libraries must not rely on untyped external code in their public API.Dev dependencies: Testing, linting, and build tools belong in
devDependenciesonly. They are never shipped with the library.Regular audits:
npm audit(or the equivalent tool for the private registry) must run in CI for every pull request and on a scheduled basis. Critical and high vulnerabilities must be resolved before a new version is published. If a fix is unavailable, an exception must be documented and approved by the security team.Updating dependencies: When bumping a dependency version, the library’s tests must pass, and the dependency’s own changelog must be reviewed for potential side effects. Lockfile changes should be minimal and intentional.
All of these practices ensure that libraries remain compatible, secure, and easy to adopt for application teams across the organization.