Last time I wrote about how JavaScript doesn’t have a proper concept of package . Yet, you can still use packaging principles to increase the cohesion of its files and directories.

While cohesion principles refer to the degree in which the files inside a package belong together, coupling refers to the degree of interdependence between files in relation to their package.

Let's say you're a solo plumber using an application built with NodeJS called The Plumber's Logbook. The purpose of the application is for you to see additional information about your jobs in order to improve your solo business.

Important Note 1: Similar to what I mentioned earlier, “files” in this context is a generic term that can also represent a “module”.

Important Note 2: You'll see in the examples that there's one file per package. That was done for the sake of brevity and to reduce the noise. Imagine that each package can have many files related to the same functionality, like mapping functions, helper classes, filter functions… even HTML. templates and bash scripts.

A file structure showing the path of the files for The Plumber’s Logbook and their dependencies represented in square brackets.

The /list-plumbing-work-events package lists all the “events” for the jobs you provided. For the purpose of this example, an “event” can be anything that happens while you’re at work.

package lists all the “events” for the jobs you provided. For the purpose of this example, an “event” can be anything that happens while you’re at work. The /admin/list-plumbing-work-complaints.js file lists all the events related to the negative feedback the customers have forwarded to you after a job.

file lists all the events related to the negative feedback the customers have forwarded to you after a job. The /admin package is a self-deployable server meant to be managed by yourself as an administrator. It shows a list of complaints and job events.

The Acyclic Dependencies Principle (A.D.P.) states that:

The dependency structure between packages must be a Directed Acyclic Graph (DAG). That is, there must be no cycles in the dependency structure. — The “Granularity” paper by Bob Martin (Uncle Bob)

That means if you start to trace all dependencies starting from a single file, you should never be able to come back to the same file again.

In The Plumber’s Logbook, there's a clear violation of this principle.

The /admin package contains index.html , which has a dependency on /list-plumbing-work-events/list-plumbing-work-events.js . The /list-plumbing-work-events package contains the list-work-events-with-complaints.js file, which has a dependency on /admin/list-plumbing-work-complaints.js .

A diagram showing how the “ /admin” package has a dependency on the “ /list-plumbing-work-events” package . The “ /list-plumbing-work-events ” package has also a dependency on “ /admin" .

If you start from the /admin package and follow the dependencies of any of its files, you'll eventually find yourself in the /admin package again ad infinitum.

What you can do is to organize the dependencies in a way it doesn’t contain cycles:

A file structure showing the “list-plumbing-work-complaints.js” file moved from the “/admin” package to a new one called “/list-plumbing-work-complaints”.

If the packages contain cyclic dependencies, it will be hard to move them around as a whole and understand how they connect to each other. When making a change, it will increase the risk of having unintended side-effects due to the dependency complexity.

The Stable Dependencies Principle (S.D.P.) states that:

The dependencies between packages in a design should be in the direction of the stability of the packages. A package should only depend upon packages that are more stable that it is. — The “Granularity” paper by Bob Martin (Uncle Bob)

Stable packages should not depend on packages that change a lot. If they do, changes in their dependencies will carry more risks of breaking the system.

In The Plumber’s Logbook, the /list-plumbing-work-events package has a lot of technical debt. It also has very low test coverage. It’s very hard to change that file without breaking the system.

In this case, instead of being depended upon less stable and volatile packages, it’s depending on /list-plumbing-work-complaints , which is new and changes often.

That violates the Stable Dependencies Principle.

One way to fix this is to create a new package for the list-work-events-with-complaints.js functionality and move the related dependencies there instead:

A file structure showing the “list-work-events-with-complaints.js” file moved to a package called “list-work-events-with-complaints”.

If you don’t do this, changes in the list-plumbing-work-complaints.js file may require changes in the list-work-events-with-complaints.js file that is inside the list-plumbing-work-events package, which is riskier to touch.

The Stable Abstractions Principle (S.A.P.) states that:

Packages that are maximally stable should be maximally abstract. Instable packages should be concrete. The abstraction of a package should be in proportion to its stability. — The “Stability” paper by Bob Martin (Uncle Bob)

The more stable a package is, the more its interface should be generalized. If you do that, they can be easily depended upon by many different parts of the system, as mentioned by S.D.P.

In The Plumber’s Logbook, the /list-plumbing-work-events package is stable (hard to change) and yet it’s too specific for plumbing work events:

A file structure showing a new “list-events” package with the “list-all-events.js” file created inside it. The “list-all-events.js” file is being used as a dependency for the work complaints and work events packages.

If you make /list-plumbing-work-events more generic by renaming it to /list-events , another package called /list-plumbing-work-events will be responsible for dealing only with plumbing work. The same goes for /list-plumbing-work-complaints , which is another volatile package that can depend upon the files from /list-events .

However, there’s no silver bullet.

The system can also function with a legacy stable package that is concrete and specific. The tradeoff is that you may not be able to easily reuse it’s functionality somewhere else. If you do, you’ll have to duplicate the code or make frequent changes to it, which effectively ignores the stability attributes of the package.