Death to the Angular Mono-Repo

Dolan Miu
7 min readApr 16, 2019

--

After having used the Angular mono-repo for over a year, I would like to share some of the criticisms and concerns I have for it.

All of us are guilty of this. We read this really cool article, or watched a cool video, or watched our elder cousins perform a cool trick. We want to mimic. This is what is happening with the Angular Mono-Repo trend.

Separation of Concerns

My main concern is the lack of separation of concerns.

“[Separation of Concerns], even if not perfectly possible, is yet the only available technique for effective ordering of one’s thoughts, that I know of.”

Edsger W. Dijkstra ( 1974)

Git history is useless

Git does not care about your individual apps. In a mono-repo, the history is a massive list of all changes… ever. Say you are working on “App A”. How does one revert to an older version of “App A”, when “App B”, “App C”, “App D”, “App F” and “App G” all pollute the history too?

The answer is you can’t, unless you pre-pend “[App-A]” on the front of every commit. But then that introduces more overhead, need for due-diligence and potential for human error.

Another way is to keep tagging your app, so you have “check-points” for your app, but then that reduces the effectiveness of the expressive git history we have.

Photo of a developer tagging a repository in Git, circa 1996 (colorized)

It is very difficult to find the commit which broke your app you’re working on. Not only can it be a commit related to your app, it can also be a commit related to the /libs, or any of the aforementioned apps.

Photo of me trying to find the commit which broke my app, circa 2014

My concern is the lack of separation of concerns.

Mono-repos are Leaky and Fragile

Making a change somewhere may affect the project you are on.

Common scenarios:

  • You are on your branch, and then you merge origin/master into your branch… compilation error. Someone changed an API on their branch
  • You take one week break from holiday, pull fresh from origin/master. It compiles fine and passes unit tests, but a shared component looks strange on your app, but it looks fine on other apps.
  • A team mate updates a dependency in package.json, it works fine for him, doesn’t for you.
  • A team mate makes a breaking change in a shared component, unaware its also heavily depended on by another app.
  • A battle tested component is used, but for one specific app, the look and feel needs to be different. You are left no choice but to write ugly override CSS in your app.
  • 3rd party dependency is updated and it breaks all apps. The only option is to big bang update.
An early black & white photo of my colleague trying his best not to break the repo, circa 1930

Tooling becomes slow

As the code base grows, the slower the tooling becomes. Running 500+ tests over the 30 apps in the mono-repo every-time you make a check-in becomes a bottle-neck in your work. The obvious answer is to “buy a better computer and move on”, but in some situations, it isn’t all that simple. My team switched from Karma to Jest because Karma was becoming slow after our ~10th app. Now Jest is starting to become slow.

We are left to only run tests of the specific app we are each working on. This is dangerous because a change in the /libs folder can affect all apps, so ideally all apps should be tested. This removes some of the benefits of the mono-repo, and introduces the potential for human error.

Low vision

You have finally finished your work and decide to make a pull-request. How can you be sure it won’t affect other apps? A naive developer will say that if all the unit tests pass on all projects, then it’s safe to go. But from experience, passing unit tests could be a potential red herring.

You are left with no choice but to test every app. This is fine when they’re aren’t as many apps, but when the app list grows, it becomes tedious and naturally people will become lazy and miss a few. How are the people reviewing your PR supposed to know it won’t affect other projects without them themselves also testing?

Single package.json

The package.json file becomes a behemoth of scripts and dependencies. This is messy and hard to read and look at.

Dependency management is harder

Because there is only one package.json, there is only one definitive list of all dependencies. You are only allowed one version of each dependency. What if one app wants an older version of a dependency, but you really want the latest and greatest? It’s not possible to have a single dependency with two versions. What if you want to update Angular which has a breaking change? You would need to big-bang update.

A black & white photo of developers trying to update a dependency from 1.2.X to 1.5.X, circa 1942

I have raised an issue about this on the Nx repo: https://github.com/nrwl/nx/issues/1089

Shared libs are idealistic

The purpose of the shared /libs folder is so that every project can share components, increase re-usability and reduce repeated code. Hooray!

In theory this sounds like a no-brainer. But in practice, each app requires the DatePickerComponent, the BarChartComponent, DialogComponent or the ItemDisplayComponent to be slightly different.

Case Study: Dialog Component

Let’s say the DialogComponent now requires an “Are you sure?” popup to come up when you press cancel for one of the apps, but you were told this is not needed in the other apps. You do what you would normally do, and add special cases inside the specific shared component. Maybe add a flag:

if (this.showAreYouSure) {
// Show "Are you sure?"
}

Afterwards, your team-mate working on a separate app who is also using the shared DialogComponent was told by his boss to dim to background whenever a Dialog Opens. You don’t want to affect the other apps, so you again… add another hack/flag to make it working.

Then in another app, they want the close button to be a “X” the top-right rather than a “Close” button on the bottom right. Another hack/flag is added.

Another app may want a red border and drop-shadow on the modal. You then add ugly CSS overrides in your app including !important tags to force it to look the way you want.

Overtime, these seemingly minor hacks build up, and turns into a shared monster.

An artist’s impression of a shared monster, circa 2003

You become scared to contribute to the shared components because it is relied by too many apps. You become too scared to refactor the component because it is so coupled with everything in the repository. You become scared to change the .html and .css because other apps have style overrides. You fear the code-base. It is tangled like vines.

A photo of a shared component, circa 2019 (colorized)

The shared component gets too complex for anyone one single person to understand, which means even more hacks are added. It becomes un-readable, un-scalable and un-maintainable.

Shared /libs is unrealistic, and potentially dangerous.

The quote below hits the nail on the head of why shared /libs are flawed:

Separating concerns by files is as effective as separating school friendships by desks. Concerns are “separated” when there is no coupling: changing A wouldn’t break B. Increasing the distance without addressing the coupling only makes it easier to add bugs.

Dan Abramov

Increasing the distance (moving code from /apps to /libs), does indeed allow for re-usability and code sharing 👍, but I am not convinced if it warrants the added complexity and potential for bugs.

Summary

I believe the mono-repo definitely has it’s uses, but if over-used, it decreases productivity and is expensive.

Google made it work because they invested a lot of time into their tooling and workflow to prevent all of these issues. But for everyone else, it is simply not scalable; a house of cards waiting to collapse 💥.

The instantaneous moment when the mono-repo collapsed under it’s own weight, circa 2017

Introducing the mono-repo shouldn’t be a simple decision or be directed by trends. Just because someone you admire uses the mono-repo doesn’t mean you have to as well. It should be part of an ongoing evolution with a lot of thought and research.

Thanks for reading! ❤️

--

--