We, coders, try to reuse code as much as possible. We extract utility functions, introduce new UI components. Motivation is clear – follow the DRY principle with all the benefits like less code, less maintenance or testing.
Refactoring code for reuse is easy when working within one project. However there are situations where we want to share code between different projects. One may want to share validation logic between client and server, to share model or API clients between multiple microservices or to enforce consistent design with a common UI library.
Code sharing can also be done between projects but there are multiple options how to do it – each with its own benefits and drawbacks. This blog should help you to choose the right one.
The simplest option to share code between projects is to share common files via file system directory and simply include them from multiple projects. Typical use-case for this tactic is to share models, validations and other utils between client and server code. Directory structure could be the following
1 2 3 4 5 6 |
server/ … src/ common/ … … other client folders or files |
Especially when you are using Create React App, you are forced to put common code “inside” client’s source folder. Luckily the server (usually) does not have restrictions on how to include files.
Pros:
Cons:
Using git submodules feature, it is possible to compose multiple git repositories in order to mimic mono-repo structure. In essence it is almost identical to the previous approach but git allows you to use different branches to emulate different versions of common code.
Benefits and disadvantages are also similar (minimal setup, no indirection, subset of language features) with the following differences.
Pros:
Cons:
Npm packages are a standard way to share JS code. Code can be shared both internally inside the team or externally with the whole world. Using npm packages also makes collaboration between different teams possible, e.g. platform team preparing reusable packages and product teams working on final products.
With npm packages one must decide what is the correct size of a package. It is the same thing as with microservices – hard to define the right size in terms of line of codes. I suggest deciding it based on the semantics – code with high cohesion belongs together, loosely coupled things should be separated.
There might be a slight exception to this rule. It is okay to put a collection of different utility functions together into one package. Thanks to tree-shaking (if properly applied), we do not have to worry about the final bundle size if we want to use only one function out of hundreds. However if there are too many things together and some breaking change happens, it may be hard to figure out if the part I’m using is affected too.
This is the most versatile solution but it also comes with its cost.
Pros:
Cons:
Which tactic to choose? Unless you are familiar with git submodules (even then it is quite exotic option which might have its benefits on the other hand), we would recommend choosing between the first and the last option.
If you are not sure that you need a full-blown solution with npm packages, you can start with simply sharing via file system. You might consider reexporting common code from common “root” and/or use absolute imports if you feel that you might switch to npm package soon. Then it should be quite easy to replace all imports with the new ones.