
JS Code Sharing Tactics
Developers constantly seek to avoid repeating themselves. Here are three practical strategies for sharing JavaScript code across multiple projects, with honest trade-offs for each approach.
Developers constantly seek to follow the DRY (Don't Repeat Yourself) principle by extracting utility functions and creating reusable UI components. While refactoring code within a single project is straightforward, sharing code between separate projects presents distinct challenges that require different approaches.
Tactic 1: Via File System
Approach: Store shared code in a common directory accessible to multiple projects.
A typical directory structure looks like this:
server/
…
src/
common/
…
… other client folders or files
Advantages
- Minimal setup requirements
- Direct visibility of changes enables quick developer feedback
- Straightforward debugging
Disadvantages
- No versioning — breaking changes affect all dependent projects simultaneously
- Poor scalability beyond a few projects
- Shared code is limited to language features common across all projects
- Works only with mono-repo structures
Tactic 2: Git Submodules
Approach: Leverage Git's submodule feature to compose multiple repositories, mimicking mono-repo structure while allowing different branches for version control.
Advantages
- Enables independent versioning across dependent projects
- Improves scalability compared to the file system approach
Disadvantages
- Error-prone for developers; requires special commands for cloning and updates
- Managing multiple branches adds complexity
Tactic 3: Npm Packages
Approach: Use npm as a standard distribution mechanism for shared JavaScript code, supporting both internal team sharing and external distribution.
Package design considerations: Group code by semantic cohesion rather than line count. Utility collections benefit from tree-shaking, which prevents bundle bloat when importing individual functions.
Advantages
- Explicit semantic versioning simplifies dependency management
- Enables heterogeneity through transpilation during publishing
- Functions effectively in both mono-repo and multi-repo environments
Disadvantages
- Requires more complex setup, including potential private registry configuration
- Creates separation between the package and dependent code, complicating simultaneous changes
- Debugging across package boundaries becomes cumbersome with transpilation
Mitigation Strategies
- Use Storybook for isolated package development
- Employ
yarn link/npm linkor Lerna for concurrent refactoring across packages
Conclusion
For most scenarios, the decision comes down to the file system approach versus npm packages. If you are unsure which to choose, start with file system sharing — use absolute imports and central re-exports to keep imports tidy. This foundation makes it easy to migrate to npm packages when the need becomes clear, requiring only minimal import modifications.