# exile.watch engineering blog

\~55 min total read time

<table><thead><tr><th width="131" align="center">Read time</th><th>Post</th></tr></thead><tbody><tr><td align="center">1 min</td><td><a data-mention href="/april-2024/why-doesnt-dependabot-update-package-json">Why doesn't Dependabot update package.json?</a></td></tr><tr><td align="center">4 min</td><td><a data-mention href="/march-2024/the-savior-amidst-the-chaos-of-dependency-updates-dependabot">The savior amidst the chaos of dependency updates - Dependabot</a></td></tr><tr><td align="center">4 min</td><td><a data-mention href="/march-2024/why-projects-local-setup-instructions-are-not-part-of-readme">Why project's local setup instructions are not part of README?</a></td></tr><tr><td align="center">15 min</td><td><a data-mention href="/march-2024/leveraging-lefthook-to-enforce-commit-guidelines-at-exile.watch">Leveraging Lefthook to enforce commit guidelines at exile.watch</a></td></tr><tr><td align="center">5 min</td><td><a data-mention href="/march-2024/the-package-manager-of-exile.watch">The package manager of exile.watch</a></td></tr><tr><td align="center">15 min</td><td><a data-mention href="/march-2024/lerna-the-hidden-powerhouse-of-exile.watch">Lerna - the hidden powerhouse of exile.watch</a></td></tr><tr><td align="center">3 min</td><td><a data-mention href="/march-2024/to-open-source-or-to-not-open-source">To open source or to not open source</a></td></tr><tr><td align="center">10 min</td><td><a data-mention href="/march-2024/exile.watch-architecture">exile.watch architecture</a></td></tr></tbody></table>

Author: [Sebastian Krzyżanowski](https://github.com/sbsrnt)\
About *exile.watch*: <https://docs.exile.watch/>\
Github: <https://github.com/exile-watch>\
\
Visit <https://exile.watch/> to experience it first hand


# Why doesn't Dependabot update package.json?

1 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

If you're unfamiliar with Dependabot or want to learn more about it, head over to my [previous article](/march-2024/the-savior-amidst-the-chaos-of-dependency-updates-dependabot).

## TL;DR

Add `versioning-strategy: increase` to the updates key

{% code title="dependabot.yml" %}

```diff
version: 2
registries:
  github:
    type: npm-registry
    url: https://npm.pkg.github.com
    token: ${{ secrets.GH_TOKEN }}
updates:
  - package-ecosystem: "npm"
+   versioning-strategy: increase
    directory: "/"
    registries: [github]
    schedule:
      interval: "weekly"
      day: "saturday"
      time: "05:00"
      timezone: "Europe/Warsaw"

```

{% endcode %}

## Dependabot versioning strategies

You can view all available and up-to-date versioning strategies on the official documentation in [this section](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#versioning-strategy).

***

To save you a click, here is the gist of it as of April 2024:

<table><thead><tr><th width="264">Option</th><th>Action</th></tr></thead><tbody><tr><td><code>auto</code></td><td>Try to differentiate between apps and libraries. Use <code>increase</code> for apps and <code>widen</code> for libraries.</td></tr><tr><td><code>increase</code></td><td>Always increase the minimum version requirement to match the new version. If a range already exists, typically this only increases the lower bound.</td></tr><tr><td><code>increase-if-necessary</code></td><td>Leave the constraint if the original constraint allows the new version, otherwise, bump the constraint.</td></tr><tr><td><code>lockfile-only</code></td><td>Only create pull requests to update lockfiles. Ignore any new versions that would require package manifest changes.</td></tr><tr><td><code>widen</code></td><td>Widen the allowed version requirements to include both the new and old versions, when possible. Typically, this only increases the maximum allowed version requirement.</td></tr><tr><td>N/A</td><td>Some package managers do not yet support configuring the <code>versioning-strategy</code> parameter.</td></tr></tbody></table>

## Dependabot's default versioning strategy in Lerna monorepos

By default, Dependabot's versioning strategy is set to `auto`.&#x20;

However in practice, in Lerna monorepos, it appears that Dependabot ends up with the `lockfile-only` option.

***

Author: [Sebastian Krzyżanowski](https://github.com/sbsrnt)\
About *exile.watch*: <https://docs.exile.watch/>\
Github: <https://github.com/exile-watch>\
\
Visit <https://exile.watch/> to experience it first hand


# The savior amidst the chaos of dependency updates - Dependabot

4 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

I briefly touched on Dependabot in [this section](/march-2024/exile.watch-architecture#with-great-modularity-comes-great-responsibility), but now it's time to dive deeper into the topic.

***

So, you have a list of [dependencies](https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file). Sometimes it's short, sometimes it's a mile long.&#x20;

But the big question is, how do you keep those dependencies up-to-date?

[`npm audit fix`](https://docs.npmjs.com/cli/v9/commands/npm-audit)?&#x20;

Sure, that's one approach, reacting to warnings that pop up post-install.&#x20;

But this mainly applies to security updates.&#x20;

What about regular dependency updates, like bumping from a patch version to a minor version, or from a minor version to a major version, without security concerns?

## Enter Dependabot

[Dependabot](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/about-dependabot-version-updates) is GitHub's solution for automated dependency updates, covering a wide range of programming languages including Ruby, JavaScript, Python, [and many more](https://github.com/dependabot/dependabot-core#:~:text=Use%20it%20to%20generate%20automated%20pull%20requests%20updating%20dependencies%20for%20projects%20written%20in%20Ruby%2C%20JavaScript%2C%20Python%2C%20PHP%2C%20Dart%2C%20Elixir%2C%20Elm%2C%20Go%2C%20Rust%2C%20Java%20and%20.NET.).

## Enabling the budget [Skynet](https://en.wikipedia.org/wiki/Skynet_\(Terminator\))

All it takes is adding a [`dependabot.yml`](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file) config file to your `.github` repo directory.&#x20;

Here's what that might look like:

<pre class="language-yaml"><code class="lang-yaml"><strong># {root}/.github/dependabot.yml
</strong><strong>version: 2
</strong>updates:
- package-ecosystem: "npm"
  directory: "/"
  schedule:
    interval: "weekly"
</code></pre>

Pretty straightforward, right?

***

Now, you might wonder: "Okay, automated pull requests sound great, but what if we have tons of dependencies? Will it flood our repo with individual pull requests for each one?"

Yes, that's exactly what would happen. And at [exile.watch](https://exile.watch/), with our multitude of dependencies spread across projects, that could easily become overwhelming.

So, am I suggesting that every project gets bombarded with a barrage of pull requests on a weekly (or whatever interval you set) basis for every single dependency update?

[By default, yes, Dependabot opens a separate pull request for each dependency update](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#groups).&#x20;

But here's the game-changer:

## Group updates &#x20;

[Group updates](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#groups) are a lifesaver, especially for those managing extensive dependency lists.&#x20;

This [relatively new](https://github.blog/changelog/2023-08-10-group-dependabot-version-updates-by-development-or-production-dependencies/) feature was long requested and for good reason.

It allows you to group sets of dependencies (by package manager) so that Dependabot can open a single pull request to update multiple dependencies simultaneously.

{% hint style="info" %}
Even with grouped updates, if there's a security concern with one of the packages, [Dependabot will still prioritize a separate pull request](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#groups:~:text=Note%3A%20If,into%20package%20vulnerabilities.) to address the vulnerability promptly, ensuring you're always informed of potential risks.
{% endhint %}

Here's an example `dependabot.yml` that [exile.watch](https://exile.watch/) uses:

```yaml
# {root}/.github/dependabot.yml
version: 2
registries:
  github:
    type: npm-registry
    url: https://npm.pkg.github.com
    token: ${{ secrets.GH_TOKEN }}
updates:
  - package-ecosystem: "npm"
    directory: "/"
    registries: [github]
    schedule:
      interval: "weekly"
      day: "saturday"
      time: "10:35"
      timezone: "Europe/Warsaw"
    groups:
      exile-watch-build-tools:
        patterns:
          - "@exile-watch/biome-config"
          - "@exile-watch/conventional-changelog-config"
          - "@exile-watch/lefthook-config"
          - "@exile-watch/postcss-config"
          - "@exile-watch/rollup-config"
          - "@exile-watch/typescript-config"
      exile-watch-design-system:
        patterns:
          - "@exile-watch/writ-icons"
          - "@exile-watch/writ-react"
      exile-watch-data:
        patterns:
          - "@exile-watch/encounter-data"
```

{% hint style="info" %}
You might notice the `registries` field:&#x20;

```yaml
registries:
  github:
    type: npm-registry
    url: https://npm.pkg.github.com
    token: ${{ secrets.GH_TOKEN }}
```

By default, when using the npm registry, there's no need to specify the registry.&#x20;

However, [since exile.watch has chosen to host packages on GitHub's NPM registry](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch/3.-module-registry-a-place-where-packages-get-to-chill), we needed to include this field.
{% endhint %}

In the case of the [crucible](https://docs.exile.watch/projects/crucible) project, I've opted for grouping all internal dependencies. Meanwhile, for the [splinters](https://docs.exile.watch/projects/splinters) project, the strategy is to group updates by config dependency:

```yaml
# {root}/.github/dependabot.yml
version: 2
registries:
  github:
    type: npm-registry
    url: https://npm.pkg.github.com
    token: ${{ secrets.GH_TOKEN }}
updates:
  - package-ecosystem: "npm"
    directory: "/"
    registries: [github]
    schedule:
      interval: "weekly"
      day: "saturday"
      time: "10:35"
      timezone: "Europe/Warsaw"
    groups:
      exile-watch:
        patterns:
          - "@exile-watch*"
      rollup-config-deps:
        patterns:
          - "@rollup*"
      lefthook-config-deps:
        patterns:
          - "@commitlint*"
          - "commitizen"
          - "lefthook"
          - "cz-conventional-changelog"
      unit-testing-config-deps:
        patterns:
          - "@testing-library*"
          - "vite-tsconfig-paths"
          - "vitest"
```

And here's the result—[grouped pull requests in action](https://github.com/exile-watch/crucible/pull/9):

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2Ff0t8QQCi7LK5upRxmbjo%2Fimage.png?alt=media&amp;token=8cd7e645-952c-488a-9d89-ab4669382301" alt=""><figcaption><p>List of group pull requests opened by Dependabot</p></figcaption></figure>

***

With this approach, managing our dependencies suddenly feels like a walk in the park, and the once daunting architecture, brimming with countless dependencies, doesn't seem so scary anymore :).

***

Author: [Sebastian Krzyżanowski](https://github.com/sbsrnt)\
About *exile.watch*: <https://docs.exile.watch/>\
Github: <https://github.com/exile-watch>\
\
Visit <https://exile.watch/> to experience it first hand


# Why project's local setup instructions are not part of README?

4 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

Whenever you hop into any [project](https://docs.exile.watch/projects/hideout#links-to-projects) under [exile.watch's GitHub profile](https://github.com/exile-watch), you'll spot the same pattern in the [README.md file](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes), resembling something like this:

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FB8A8l0qmNCI5Mca10Hco%2Fimage.png?alt=media&amp;token=8575c2ff-ffd8-42a7-a452-2899699af8a7" alt="" width="563"><figcaption><p>exile.watch README.md</p></figcaption></figure>

Rather than stuffing the heading and contribution links directly into README.md / [`CONTRIBUTION.md`](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors), you'll find them on GitBook, our platform of choice.

## This is intentional

Adopting this approach comes with its mix of pros and cons, but I'm inclined to argue the scale tips more towards the positives.

## Let's start with the bad first

### Single point of failure (SPoF)

Documentation [SPoF  (single point of failure)](https://en.wikipedia.org/wiki/Single_point_of_failure) is now part of 3rd party provider which is GitBook. \
If GitBook goes down, so does exile.watch documentation and local setup instructions.

GitBook offers a syncing functionality with GitHub and you can check how hideout repo could look like at <https://github.com/exile-watch/hideout/tree/0330ad9b5392650b96ec2ca8f233cac0ce3fda59>.

GitBook does provide a GitHub syncing feature, offering a glimpse into what the hideout repo might look like [here](https://github.com/exile-watch/hideout/tree/0330ad9b5392650b96ec2ca8f233cac0ce3fda59). Honestly, while placing SPoF with a third-party isn't ideal, alternative communication channels like [GitHub's discussions](https://github.com/exile-watch/hideout/discussions) and [Discord](https://discord.gg/UUquE4uv7k) make this concern less daunting.

Plus, GitBook runs on [Cloudflare](https://www.cloudflare.com/), so any potential outages should be swiftly handled.

### Unintuitive and potentially annoying at the beginning

Typically, diving into a new company or open-source project, README.md and CONTRIBUTING.md are your go-tos. This is standard for most projects.

However, needing an extra click to access this information can be irksome, despite not being a deal-breaker given the benefits you'll soon discover.

### But what about GitHub's wiki? It's been built for this exact reason!

No. Full stop.&#x20;

GitHub's markdown support and wiki functionality are too basic and frankly, more trouble than they're worth. On top of that, [there are no plans to add more features anytime soon](https://github.com/orgs/community/discussions/23914#discussioncomment-3242152).

GitBook, in contrast, excels in user-friendliness and functionality.

## The good

### Centralized documentation

In the realm of Open Source, having everything in one spot is a game-changer. Enough said.

### Plenty of integrations

Though exile.watch docs mainly use [Lucid.app](https://lucid.app/) integration, the sheer ease of embedding various blocks with a simple slash command is a clear win.&#x20;

Plus, [GitBook's hint system](https://docs.gitbook.com/content-editor/blocks/hint) outdoes [GitHub's alert feature](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts) by miles.

### Easily accessible drafts with diff changes

Yes, GitHub offers similar functionality, but GitBook makes accessing drafts and seeing changes as easy as a few clicks—no terminal commands necessary.

### Page / book flow

GitBook surpasses GitHub wiki in creating organized categories and subpages, enhancing the overall reading and navigation experience.

### **Limited README Exploitation for Hacktoberfest**

You heard that right. While it may seem counterintuitive for an open-source project, limiting README contributions helps sidestep meaningless [Hacktoberfest](https://hacktoberfest.com/participation/) spam.&#x20;

For more context, check out [this video](https://www.youtube.com/watch?v=5nY_cy8zcO4).

## Conclusion

To wrap it up, shifting to centralized documentation via GitBook is primarily about streamlining information access and safeguarding against low-effort Hacktoberfest contributions.&#x20;

It's a strategic choice that, while not without its downsides, offers significant benefits in the grand scheme.

***

Author: [Sebastian Krzyżanowski](https://github.com/sbsrnt)\
About *exile.watch*: <https://docs.exile.watch/>\
Github: <https://github.com/exile-watch>\
\
Visit <https://exile.watch/> to experience it first hand


# Leveraging Lefthook to enforce commit guidelines at exile.watch

15 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

When it comes to collaboration with others (whether from the same organization, team, or in open source), having clear rules is essential. No one really enjoys chaos, and if you do, then godspeed.

Focusing on architecture from the start may bring some pain and frustration, but as time flies, a well-set architecture will save you far more headaches and money.

One of the key rules and architectural aspects are [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks).

## What are git hooks?

[Git](https://git-scm.com/) has a way to fire off custom scripts when certain important actions occur.&#x20;

There are two groups of these hooks: client-side and server-side.&#x20;

Client-side hooks are triggered by operations such as [committing](https://en.wikipedia.org/wiki/Commit_\(version_control\)) and [merging](https://en.wikipedia.org/wiki/Merge_\(version_control\)), while server-side hooks run on network operations like receiving pushed commits.

In this post, we will solely focus on client-side hooks, setting the stage for an efficient management system.

## Git hooks managers

Understanding git hooks sets the stage, but managing them efficiently is where git hooks managers come into play.&#x20;

The first thing you'll likely find about git hooks is the official [Git Hooks documentation](https://git-scm.com/docs/githooks).&#x20;

However, there are already git hooks managers available that are fast, well-maintained, and battle-tested, so I didn't see the need to create a custom git hook manager.&#x20;

To name a few: [Husky](https://github.com/typicode/husky), [pre-commit](https://github.com/pre-commit/pre-commit), [overcommit](https://github.com/sds/overcommit), [and more](https://github.com/aitemr/awesome-git-hooks)!

## What about Lefthook?

While there are several git hooks managers out there, my googling led me to a particular one that stood out—[Lefthook](https://github.com/evilmartians/lefthook).&#x20;

I've always worked with Husky, but ever since the [v5 fiasco](https://github.com/typicode/husky/issues/857), it became clear that, at the time, this was an unstable project that breached the trust.&#x20;

When I gave [Husky v9](https://github.com/typicode/husky/releases/tag/v9.0.1) a shot a few weeks ago, I'll be honest: I had no clue how to create a shareable configuration and was somewhat wary.

Add [lint-staged](https://github.com/lint-staged/lint-staged), commonly used with Husky, to the mix, and you get a sense that things could have been simpler in 2024.

## Before we dive into Lefthook, let's quickly mention commit guidelines

If you visit [commit message guidelines](https://docs.exile.watch/development/commit-message-guidelines) at <https://docs.exile.watch/>  you will notice following banner at the very top:

{% hint style="danger" %}
Your contributions will be rejected if you won't follow this guideline.
{% endhint %}

With a clear framework for commit messages established, integrating Lefthook to enforce these standards was our next step, particularly within our Lerna environment.&#x20;

### A word about commitizen

[Commitizen](https://commitizen.github.io/cz-cli/) is a [CLI](https://en.wikipedia.org/wiki/Command-line_interface) tool designed to help developers create more consistent and structured commit messages following conventional commit guidelines.

### A word about commitlint

[commitlint](https://commitlint.js.org/) checks if commit messages meet specified guidelines, playing a crucial role in our commit process.

## Cooking up Lefthook in Lerna environment

If this is your first encounter with [Lerna](https://lerna.js.org/) and how it's applied at exile.watch, I suggest giving [Lerna - the hidden powerhouse of exile.watch](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch) a read.&#x20;

If you're short on time, here's the gist: exile.watch relies heavily on dependencies structured as boilerplates for consumer applications to use.&#x20;

And guess what? Lefthook is one of these crucial "boilerplates."

***

Lefthook finds its place within the [@exile-watch/splinters](https://github.com/exile-watch/splinters) build tools [monorepo](https://monorepo.tools/), specifically under the [@exile-watch/lefthook-config](https://github.com/exile-watch/splinters/tree/main/packages/lefthook-config) package.&#x20;

To give you an idea, here's how the `@exile-watch/splinters` repo is organized, focusing just on the Lefthook part:

```
// Simplified @exile-watch/splinters repo structure
.
├── .lefthook
│   └── commit-msg
│       └── commitlint.sh
├── packages
│   └── lefthook-config
│       ├── scripts
│       │   └── commitlint.sh
│       ├── README.md
│       ├── lefthook.yml
│       └── package.json
├── lefthook.yml
├── lerna.json 
├── commitlint.config.js
└── package.json  // @exile-watch/lefthook-config as a devDependency
```

Right off the bat, 4 key components stand out:

1. `.lefthook/commit-msg` with`commitlint.sh` bash script at the root of the repository
2. `lefthook-config` having `scripts` directory with `commitlint.sh`
3. `lefthook.yml` in both - the root of the repository *and* `lefthook-config`
4. `commitlint.config.js`

Let's delve into the roles each plays.

## The dual role of commitlint.sh

Diving into the specifics, let's tackle an essential caveat first:

[If you find yourself with more than one Lefthook file in the project, be warned—only one will be actively used, and it's anyone's guess which one that might be.](https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md#config-file)

This situation could mess up our setup.&#x20;

To avoid this problem, we make sure the root `.lefthook/commit-msg/commitlint.sh` script calls the script inside the package directly:

```bash
# .lefthook/commit-msg/commitlint.sh - our root repo .lefthook config

#!/bin/bash
source node_modules/@exile-watch/lefthook-config/scripts/commitlint.sh

run_commitlint
```

```bash
# lefthook-config/scripts/commitlint.sh - our lefthook-config package
#!/bin/bash
function run_commitlint {
  echo $(head -n1 $1) | npx commitlint --edit --color --help-url='https://docs.exile.watch/development/commit-message-guidelines'
}
```

This setup essentially ensures that our root `.lefthook`'s `commitlint.sh` script activates the script defined in the package itself, thereby enforcing our commit guidelines directly from the package.

By doing so, we maintain control and clarity, using the root script to trigger the package-defined script, thereby unifying our commit linting process under one predictable mechanism.

One more note: [The `/scripts` directory ](https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md#source_dir)is also [required](https://github.com/evilmartians/lefthook/blob/master/docs/configuration.md#remotes) to make remotes work—more on that later.

## Why Lefthook and commitlint are essential

Our primary aim is to uphold commit standards, necessitated by `commitlint`, which avails the `commitlint` command.&#x20;

The question then becomes: how best to activate our `commitlint` package?&#x20;

Through the `commitlint.sh` bash script, of course.

This crucial detail explains why having the `@exile-watch/lefthook-config` as a [devDependency](https://stackoverflow.com/questions/18875674/whats-the-difference-between-dependencies-devdependencies-and-peerdependencie/22004559#22004559) is vital—it empowers us to execute Lefthook within the same repository where the package resides:

```bash
# Enabling the use of Lefthook's commitlint within the same repo
source node_modules/@exile-watch/lefthook-config/scripts/commitlint.sh
```

And by invoking `run_commitlint`, we kickstart the [linting process](https://en.wikipedia.org/wiki/Lint_\(software\)) as defined within our package's `commitlint.sh` script.&#x20;

***

The naming conventions here are flexible; I opted for simplicity and consistency across the board, hence the recurrent use of "commitlint."

### Deciphering the commitlint.sh script

Let's break down this script bit by bit:

```bash
echo $(head -n1 $1) | npx commitlint --edit --color --help-url='https://docs.exile.watch/development/commit-message-guidelines'
```

* **$1** and **head -n1 $1**: Fetches and echoes the first line of the commit message, preparing it for linting.
* **npx commitlint**: Executes commitlint, applying our project's rules to the commit message in question.
* **--edit, --color, --help-url**: [Options](https://commitlint.js.org/reference/cli.html) enhancing usability, like colored output and providing a URL for guideline assistance.

## Integrating Lefthook with commitlint.sh

But how do we ensure Lefthook autonomously triggers this script at the[ pre-commit stage](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#_committing_workflow_hooks)? That's where `lefthook.yml` within `lefthook-config` comes into play

<pre class="language-yaml"><code class="lang-yaml"><strong># {root}/packages/lefthook-config/lefthook.yml
</strong><strong>pre-commit:
</strong>  parallel: true
  commands:
    lint:
      glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc,yml,yaml}"
      run: npx biome check --no-errors-on-unmatched --files-ignore-unknown=true {staged_files}

    "lint:apply":
      glob: "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc,yml,yaml}"
      run: npx biome check --apply --no-errors-on-unmatched --files-ignore-unknown=true {staged_files} &#x26;&#x26; git update-index --again

    "typecheck":
      glob: "*.{ts,tsx}"
      run: npx tsc

commit-msg:
  scripts:
    "commitlint.sh":
      runner: bash
</code></pre>

Notice this part:

```bash
commit-msg:
  scripts:
    "commitlint.sh":
      runner: bash
```

This configuration informs Lefthook about the `commitlint.sh` script, ensuring it's triggered appropriately.

## Eating the cooked lefthook-config package in other repositories

The brilliance and simplicity of extending this setup to other repositories lie in the `lefthook.yml` file at the root, pointing to our configured `lefthook.yml` within the `lefthook-config` package:

```yaml
# {root}/lefthook.yml
remotes:
  - git_url: https://github.com/exile-watch/splinters
    configs:
      - packages/lefthook-config/lefthook.yml
```

You're essentially instructing Lefthook to fetch and merge the specified configurations, making project-wide guideline enforcement a breeze.

Check how [nucleus](https://github.com/exile-watch/nucleus) and [crucible](https://github.com/exile-watch/crucible) repos are consuming our [lefthook-config](https://github.com/exile-watch/splinters/tree/main/packages/lefthook-config).

## Understanding `commitlint.config.js`

Last component in our highlighted setup is [`commitlint.config.js`](https://commitlint.js.org/concepts/shareable-config.html) file.&#x20;

<pre class="language-javascript"><code class="lang-javascript"><strong>// {root}/commitlint.config.js
</strong><strong>module.exports = {
</strong>  extends: ["@commitlint/config-conventional"],
};
</code></pre>

```jsonp
// package.json
{
  // ... 
  "config": {
    "commitizen": {
      "path": "cz-conventional-changelog"
    }
  }
}
```

This [config](https://commitlint.js.org/concepts/shareable-config.html) sets our commit message rules, ensuring every message meets our project standards. It's key for keeping our git history clean and meaningful.

## Lastly, synchronizing our git hooks with a postinstall step

Before we evaluate our setup's complexity, there is one last step that we have to follow up with.&#x20;

After installing dependencies, we run Lefthook script:

```
// package.json
{
  // ... 
  "scripts": {
    // ...
    "postinstall": "npx lefthook install"
  }
}
```

By [post installing ](https://docs.npmjs.com/cli/v10/using-npm/scripts#life-cycle-operation-order)Lefthook, we are synchronizing our git hooks with latest changes that are provided by our lefthook-config package.

```flow
> postinstall
> npx lefthook install

sync hooks: ✔️ (pre-commit, commit-msg)
```

## Is this approach... Overcooked?

However, there's a bit of uncertainty on my end about whether this setup is the way it's supposed to work.&#x20;

The idea of a consumer package pulling its configuration from the `lefthook-config` package, which then executes a script from the root of its own repository, works like a charm in practice.&#x20;

Yet, it leaves me wondering if we've taken the simplicity of Lefthook's intended use and stretched it a bit too far.

[I've sparked a conversation on the Lefthook repository to clarify this very point](https://github.com/evilmartians/lefthook/discussions/639#discussioncomment-8591400).&#x20;

Ultimately, our approach works seamlessly for now, but it raises the question:&#x20;

Is this how it was meant to be used?&#x20;

Only time will tell if adjustments or a nod of approval await us down the line.

***

Author: [Sebastian Krzyżanowski](https://github.com/sbsrnt)\
About *exile.watch*: <https://docs.exile.watch/>\
Github: <https://github.com/exile-watch>\
\
Visit <https://exile.watch/> to experience it first hand


# The package manager of exile.watch

5 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

TL;DR: `exile.watch` is using NPM as it's package manager.&#x20;

NPM was causing the fewest issues, without really deteriorating the developer experience.

***

## What is a package manager?

Package manager is a tool that automates the process of installing, updating, configuring, and removing software packages or libraries from a project.&#x20;

These packages are typically distributed through a registry, with [npm](https://www.npmjs.com/) (Node Package Manager) being the most widely used registry in the JavaScript community.&#x20;

Package managers help developers manage dependencies (external code or libraries that a project relies on) efficiently and ensure consistency across development environments.

The most commonly used package managers in the JavaScript ecosystem are:

1. **npm**: The default package manager for Node.js, allowing users to install and manage dependencies from the npm registry.
2. **Yarn**: Developed by Facebook, Yarn is a faster, more secure alternative to npm, offering improvements in speed, reliability, and security for managing dependencies.
3. **pnpm**: Standing for "performant npm," pnpm offers efficiency by symlinking packages from a single content-addressable storage, reducing disk space usage and increasing installation speed compared to npm and Yarn.

## The good, the bad and everything around package managers regarding exile.watch development experience

### [npm](https://docs.npmjs.com/cli/v10/commands/npm)

In my experience with exile.watch, npm has been a reliable package manager, particularly appreciating its support for a multi-package approach through monorepo workspaces and the way it handles package dependencies in the root `node_modules`, aligning seamlessly with our architecture.&#x20;

However

* &#x20;the slow speed of `npm install` (well only few seconds, but comparing to other package managers - yeah, it's slow as fuck),
* rarely there is a need to run `npm run build` when using `npm link` to keep dependencies in sync flavored with general issues ([#2828](https://github.com/npm/cli/issues/2828), [#7151](https://github.com/npm/cli/issues/7151), [#4285](https://github.com/npm/cli/issues/4285), [#2076](https://github.com/npm/cli/issues/2076)),&#x20;
* coupled with the lack of hot reloading for `.next` dependencies (probably because of the aforementioned [#2828](https://github.com/npm/cli/issues/2828) issue since I'm on windows)

&#x20;are notable drawbacks that could (and did in some way at few points) impact productivity.

### [yarn](https://yarnpkg.com/)

The [exile.watch architecture](/march-2024/exile.watch-architecture) aligns with the first three[ limitations and caveats](https://classic.yarnpkg.com/lang/en/docs/workspaces/#toc-limitations-caveats) of Yarn, leading me to initially dismiss Yarn due to its different package management philosophy.

Looking back, I realize this decision might have been premature. The documentation I referenced pertains to Yarn 1.x, which is quite outdated.&#x20;

Further research revealed that Yarn has progressed to version [4.x](https://yarnpkg.com/blog/release/4.0), far beyond the 2.x release I was vaguely aware of.&#x20;

This oversight is particularly regrettable because, throughout my 7-year career, I never professionally used Yarn, thus locking my understanding of this package manager in a medieval times.&#x20;

Which sucks, as I recall how effectively `yarn link` worked comparing to `npm link`.

### [pnpm](https://pnpm.io/)

When I first gave `pnpm` a go, the speed at which it handled installs was the first thing that made me take notice. It was impressively quick. And when I got into using `pnpm link` for hot reloading, it worked like a charm—no hiccups at all.

However, I soon hit a couple of bumps.&#x20;

One was with linking packages. It turned out that the `node_modules/.bin/` directory wasn’t getting the bin links from all the nested packages, which was a bit of a letdown because it messed with my continuous integration setup. (see [#96](https://github.com/pnpm/pnpm/issues/96) and [#899](https://github.com/pnpm/pnpm/issues/899)).

Then, there was `pnpm dedupe` step needed after installation. Not a deal-breaker, but definitely an extra hoop to jump through.

The binaries thing was a bit of a curveball too. Since the necessary bin executables from the nested packages weren’t automatically linked, `pnpm` tried to step in and fix the gap by installing them (and this was causing weird inconsistencies as well).&#x20;

This move kind of threw a wrench into my neat architecture plans, making things look more complex than I wanted. To get around that, I found myself resorting to the [`--shamefully-hoist`](https://pnpm.io/npmrc#shamefully-hoist).

But then I started experiencing inconsistencies with linking the packages locally akin to npm issues mentioned above.&#x20;

If I'm forcing pnpm to act as npm, then what's the point of using pnpm?

***

Author: [Sebastian Krzyżanowski](https://github.com/sbsrnt)\
About *exile.watch*: <https://docs.exile.watch/>\
Github: <https://github.com/exile-watch>\
\
Visit <https://exile.watch/> to experience it first hand


# Lerna - the hidden powerhouse of exile.watch

15 min read (including sub-pages)

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

I briefly touched on why I picked [Lerna](https://lerna.js.org/) and what it's all about in an [exile.watch architecture](/march-2024/exile.watch-architecture#where-did-the-idea-of-modularity-come-from) post before.

However, [exile.watch](https://exile.watch/)'s approach to Lerna deserves its own post with a total deep dive.

This isn't exactly a step-by-step guide on using Lerna.&#x20;

Instead, think of it as a behind-the-scenes look at how exile.watch harnesses Lerna to our advantage.

## To reiterate - what is Lerna?

Lerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository.

[Monorepos](https://monorepo.tools/) let you develop multiple packages in the same repository, which is fantastic for productivity. You can share code, establish clear ownership, and have fast unified CI.

To use monorepos well, you need a good monorepo tool, and Lerna is exactly that. Lerna is [fast](https://github.com/vsavkin/large-monorepo), widely used, and battle tested.

## So... What was the problem?

There was one real problem, and one imaginary problem:&#x20;

1. *The real deal*: a dependency mess, which I've already mentioned about in [exile.watch architecture](/march-2024/exile.watch-architecture)post.
2. *The not-so-real deal*: just my own itch to dive into Lerna, especially since I left myself a nice, messy sandbox to play in with that dependency chaos.

## Tackling two issues at once: The Dependency Mess and Mastering Lerna

[Remember when I talked about slicing up dependencies into neat little categories by packing them off into their own packages](/march-2024/exile.watch-architecture#sweet-i-have-the-concept-mapped-in-mind-but-where-do-i-begin)?

I brushed over that idea before, but now, let's dive deeper into how that worked out.

### The toolkit I needed to make it all happen

{% hint style="info" %}
Because each component plays a crucial role in the grand scheme, I've detailed them on their own subpages. Feel free to click through for a deep dive into each part of my setup. Here’s a quick rundown of what I needed:
{% endhint %}

To pull this off, I needed a few key things in my arsenal:

*(2 min read)* [1. Separate repository and monorepo tool](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch/1.-separate-repository-and-monorepo-tool)

*(1 min read)* [2. A module bundler - the fun begins](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch/2.-a-module-bundler-the-fun-begins)

*(1 min read)* [3. Module registry (a place where packages get to chill)](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch/3.-module-registry-a-place-where-packages-get-to-chill)

*(2 min read)* [4. Versioning, Publishing, and Configuring lerna.json for Commit Conventions](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch/4.-versioning-publishing-and-configuring-lerna.json-for-commit-conventions)

*(2 min read)* [5. Some automation magic using the (Lerna) CLI](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch/5.-some-automation-magic-using-the-lerna-cli)

*(5 min read)* [6. How does one test lerna packages at exile.watch: locally and in the real world](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch/6.-how-does-one-test-lerna-packages-at-exile.watch-locally-and-in-the-real-world)

## Lerna impact on exile.watch

Regardless of how deep you've dived into the sections above, it's worth highlighting the significant impact Lerna has had on exile.watch.&#x20;

Spinning up a new package or project now takes mere seconds.&#x20;

And yes, while one might argue "But doesn't setting up a repository template do the same?", there's a catch—over time, disparate dependency versions inevitably lead to incompatibility nightmares.

So back to square one.&#x20;

Thanks to Lerna and the "boilerplate" strategy I've detailed earlier, we dodge such dilemmas, scaling effortlessly.&#x20;

That’s the real beauty of it. Admittedly, this approach demands some upkeep of our internal ecosystem, but it's a small price for coherent progress.

***

To wrap up, I'll leave you with a question that might stir some thought:

If you were in charge of steering your company's tech direction, which path would you choose?

a) Accepting rapidly accumulating tech debt and inconsistent dependencies across projects, leading to:\
\- headaches and reduced engineering productivity, which ultimately costs more money

— or —

b) Keeping projects up-to-date with a person or a small team dedicated to this task, ensuring:\
\- smooth transitions between teams\
\- straightforward onboarding for new hires and immediate productivity from day one

Consider this: The smaller the team, the more pronounced the diminishing returns from implementing Lerna might seem.

If the focus is solely on short-term gains, aiming to quickly deliver features to meet specific goals, then option a) might seem more appealing, and it's not inherently wrong to think that way.

Focusing just on the short-term is like building a house of cards: it might stand up for a while, but eventually, it’s going to come tumbling down.

***

Author: [Sebastian Krzyżanowski](https://github.com/sbsrnt)\
About *exile.watch*: <https://docs.exile.watch/>\
Github: <https://github.com/exile-watch>\
\
Visit <https://exile.watch/> to experience it first hand


# 1. Separate repository and monorepo tool

2 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

*This page is part of* [Lerna - the hidden powerhouse of exile.watch](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch#the-toolkit-i-needed-to-make-it-all-happen) *list - 1/7*

*Following up on our deep dive into exile.watch's use of Lerna, this page tackles the first crucial steps: setting up a separate repository and introducing Lerna as our monorepo tool.*&#x20;

***

## Separate repository

It was fairly straightforward—I created an empty repository through GitHub's UI

## Monorepo tool (Lerna)

Starting with an empty repository, the best approach was to follow the "[starting from scratch](https://lerna.js.org/docs/getting-started#starting-from-scratch)" instructions from the Lerna setup guide, utilizing the `npx lerna init` [CLI](https://en.wikipedia.org/wiki/Command-line_interface) command.

Running `lerna init` got everything prepped for a true *Lerna workspace*.&#x20;

Although I don’t remember the exact output, I initially ended up with the following `lerna.json`:

```json5
// lerna.json
{
  "$schema": "node_modules/lerna/schemas/lerna-schema.json",
  "version": "independent",
  "packages": [
    "packages/*"
  ]
}
```

*As of March 2024*, let’s break down the options and definitions in the above JSON:

### $schema

This refers to [JSON schemas](https://json-schema.org/understanding-json-schema/about), which help to describe your JSON data structure.

### version ("fixed" mode)

{% hint style="info" %}
When using the fixed mode, all the affected packages will be published using the same version. The last published version is recorded in `lerna.json`
{% endhint %}

```
.
├ package.json     # "version": 0.0.0
├ lerna.json       # "version": 1.2.3
└ packages     
  ├ package_A
  │ └ package.json # "version": 1.2.3
  └ package_B      
    └ package.json # "version": 1.2.3
```

### version ("independent" mode)

{% hint style="info" %}
If you decide to use `independent`, then every package has it's own version.&#x20;
{% endhint %}

I went with `independent` option.&#x20;

I opted for this approach because some packages will change more often than others.&#x20;

Also it wasn’t necessary to version the repository's `package.json`, and doing so is [generally not recommended:](https://github.com/lerna/lerna/issues/2879#issuecomment-1471606448)

```
.
├ package.json     # "version": deleted key, "private": true
├ lerna.json       # "version": "independent"
└ packages     
  ├ package_A
  │ └ package.json # "version": 1.2.3
  └ package_B      
    └ package.json # "version": 3.2.1
```

### packages

This is an array of [globs](https://docs.python.org/3/library/glob.html) indicating where packages can be found. By default, it’s set to `["packages/*"]`, which is how configurations for [splinters](https://github.com/exile-watch/splinters/blob/main/lerna.json#L16-L18), [nucleus](https://github.com/exile-watch/nucleus/blob/main/lerna.json#L14-L16) and [writ](https://github.com/exile-watch/writ/blob/main/lerna.json#L14-L16) are structured.

## Updating `package.json` to leverage NPM Workspaces

The last change involved updating `package.json` to make the most of [npm workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces):

```jsonp
// package.json
{
  // ...
  {
    "workspaces": [
    "packages/*"
  ],
}
```

***

*With our repository and Lerna setup ready, let's now focus on the second key piece: the module bundler, crucial for efficiently packaging our code.*


# 2. A module bundler - the fun begins

1 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

*This page is part of* [Lerna - the hidden powerhouse of exile.watch](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch#the-toolkit-i-needed-to-make-it-all-happen) *list - 2/7*

***

{% hint style="info" %}
While you technically don't need a module bundler to get your project off the ground (you could just transpile each file from source to output directory and adjust your `package.json` accordingly), using one can streamline your process significantly.
{% endhint %}

I went with [Rollup](https://rollupjs.org/), thanks to my previous experience with it. You can read more about bundlers [here](https://snipcart.com/blog/javascript-module-bundler).

{% hint style="info" %}
*To keep it clear: Rollup itself is **not** opinionated - just look at this* [*list*](https://rollupjs.org/configuration-options/)
{% endhint %}

You can make Rollup's config somewhat opinionated easily which means that if you find a setup that works for one package, it's likely to work across the board with minimal fuss.

This consistency is a big win when managing multiple packages, ensuring they're all bundled efficiently in the same manner.

So, what's the deal with the packages in [exile.watch](https://exile.watch/)?

Each package serves its unique purpose, acting as a piece of the larger puzzle. They're essentially [boilerplates](https://en.wikipedia.org/wiki/Boilerplate_code) for different areas of functionality:

* [`@exile-watch/typescript-config`](https://github.com/exile-watch/splinters/pkgs/npm/typescript-config) bundles [TypeScript](https://www.typescriptlang.org/) and related dependencies. Adding it as a dev dependency means you’re indirectly installing TypeScript itself.
* [`@exile-watch/unit-testing-config`](https://github.com/exile-watch/splinters/pkgs/npm/unit-testing-config) includes everything you need for [React](https://react.dev/)/JavaScript unit testing, like [Vitest](https://vitest.dev/) and [@testing-library/react](https://testing-library.com/docs/react-testing-library/intro/), along with a few other relevant plugins.

And it doesn’t stop there.

***

Each package adopts a specific stance, making sure that whether I’m whipping up a new consumer app, a browser extension, or even a desktop application, the development experience remains consistent.&#x20;

***Everything is expected to work, act, and behave the same, regardless of where it’s implemented.***

***

*Having established our bundling strategy with Rollup, the next logical step is to consider where our packages will live.*&#x20;


# 3. Module registry (a place where packages get to chill)

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

*This page is part of* [Lerna - the hidden powerhouse of exile.watch](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch#the-toolkit-i-needed-to-make-it-all-happen) *list - 3/7*

***

The [NPM registry](https://docs.npmjs.com/about-npm) is typically the go-to spot for uploading packages.&#x20;

But, with [exile.watch](https://exile.watch/) being an [open-source](/march-2024/to-open-source-or-to-not-open-source) project, I got curious about leveraging GitHub's package navigation section:&#x20;

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FO5ewAIFbVrTXWQd78djw%2Fimage.png?alt=media&amp;token=d5e89ea9-9002-47b0-b951-00b98e3cfee9" alt="" width="170"><figcaption><p>GitHub's repository navigation</p></figcaption></figure>

Turns out, to get your packages listed there, you’ve got to use [GitHub Packages](https://github.com/features/packages).&#x20;

So, I decided to dive into the GitHub Packages NPM registry.&#x20;

This move wasn’t just about getting our packages neatly listed; it was also a chance to really see what it's like to work with GitHub Packages.&#x20;

And honestly, this experience could fill another blog post, which I might just write up someday.

If you are not up to date - there is a new kid on the block when it comes to registries: [JSR](https://deno.com/blog/jsr_open_beta) (JavaScript Registry by Deno)

***

*Moving forward, we'll explore how Lerna aids in module versioning and publishing, and how tweaking `lerna.json` helps enforce commit conventions in our project.*


# 4. Versioning, Publishing, and Configuring lerna.json for Commit Conventions

2 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

*This page is part of* [Lerna - the hidden powerhouse of exile.watch](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch#the-toolkit-i-needed-to-make-it-all-happen) *list - 4/7*

***

[Lerna](https://lerna.js.org/) simplifies life with its [all-in-one versioning and publishing capabilities](https://lerna.js.org/docs/features/version-and-publish), perfect for streamlining our release process.

***

Initially, our `lerna.json` looked something like this:

```jsonp
// Initial lerna.json setup
{
  "$schema": "node_modules/lerna/schemas/lerna-schema.json",
  "version": "independent",
  "packages": [
    "packages/*"
  ]
}
```

Given that [exile.watch](https://exile.watch/) is [open source](/march-2024/to-open-source-or-to-not-open-source), it was crucial to implement certain standards.&#x20;

A key requirement was enforcing commit linting. More on commit message guidelines at exile.watch [here](https://docs.exile.watch/development/commit-message-guidelines).

Moreover, with [GitHub serving as our NPM registry](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch/3.-module-registry-a-place-where-packages-get-to-chill), it was essential for Lerna to recognize this setup. Thus, I enhanced our `lerna.json` to adhere to commit conventions and integrate with the GitHub NPM registry, as shown below:

```jsonp
// Enhanced lerna.json for commit conventions and GitHub NPM registry integration
{
  "$schema": "node_modules/lerna/schemas/lerna-schema.json",
  "version": "independent",
  "command": {
    "version": {
      "conventionalCommits": true,
      "createRelease": "github",
      "message": "chore(release): publish [skip ci]",
      "commitHooks": false
    }
  },
  "packages": [
    "packages/*"
  ]
}
```

This tailored setup ensures our project adheres to best practices in version management and commit hygiene, all while leveraging GitHub’s infrastructure for package distribution.

***

*Having set up versioning and publishing with Lerna, let’s dive into automating these processes with the Lerna CLI to streamline our workflow.*


# 5. Some automation magic using the (Lerna) CLI

2 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="exile.watch logo" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

*This page is part of* [Lerna - the hidden powerhouse of exile.watch](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch#the-toolkit-i-needed-to-make-it-all-happen) *list - 5/7*

***

To avoid the hassle of manually managing versioning and publishing, I set up a straightforward [GitHub Actions](https://docs.github.com/en/actions/using-workflows/about-workflows#about-workflows) workflow to automate these processes for me:

```yaml
# .github/workflows/release.yml
name: Release

on:
  push:
    paths:
      - 'packages/**'
    branches:
      - main

jobs:
  publish:
    name: Publish to NPM
    if: "!contains(github.event.head_commit.message, '[skip ci]')"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          token: ${{ secrets.GH_TOKEN }}
          fetch-depth: 0 # Fetch-depth 0 necessary to get full commit history for checking changes

      - uses: actions/setup-node@v4
        with:
          node-version-file: '.nvmrc'
          cache: 'npm'

      - name: Configure Git
        run: |
          git config --global user.name "${{ secrets.GIT_USER_NAME }}"
          git config --global user.email "${{ secrets.GIT_USER_EMAIL }}"

      - name: "GitHub Package Registry Identity"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          echo "//npm.pkg.github.com/:_authToken=$GITHUB_TOKEN" > .npmrc
          echo "@exile-watch:registry=https://npm.pkg.github.com/" >> .npmrc

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Lerna publish
        if: success()
        env:
          CI: true
          GH_TOKEN: ${{ secrets.GH_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: lerna publish --yes
```

This GitHub Actions workflow, defined in `.github/workflows/release.yml`, automates the process of versioning and publishing our packages to NPM whenever changes are pushed to the `main` branch affecting any files within the `packages/**` directory.&#x20;

Here's what each step accomplishes:

* **Checkout Repository** \
  Uses the `actions/checkout@v4` action to check out your repository code, making it accessible to the workflow. \
  The `fetch-depth: 0` option ensures the entire commit history is available, which is crucial for Lerna to accurately determine version changes.
* **Setup Node.js**\
  The `actions/setup-node@v4` action prepares the environment by setting up the Node.js version specified in the `.nvmrc` file. \
  It also caches NPM dependencies to speed up the build process in future runs.
* **Configure Git**\
  Configures Git with the user name and email stored in your GitHub secrets. \
  This information is used for commit operations performed by Lerna during the versioning process.
* **GitHub Package Registry Identity**\
  Sets up authentication for the GitHub Package Registry by writing the GitHub token to `.npmrc`. \
  This step ensures that the workflow can publish packages to the GitHub Package Registry under your account.
* **Install Dependencies**\
  Runs `npm ci` to install the project dependencies based on the `package-lock.json` file. \
  This command is preferred for continuous integration workflows as it installs dependencies faster and more reliably than `npm install`.
* **Build**\
  Executes the `npm run build` command to build the project. \
  This step compiles the source code into an executable format before publishing.
* **Lerna Publish**: \
  Finally, the `lerna publish --yes` command, run only if all previous steps have succeeded, publishes the updated packages to NPM. \
  The `--yes` flag automates the confirmation process, allowing Lerna to proceed without manual intervention. \
  The environment variables `CI`, `GH_TOKEN`, and `NPM_TOKEN` are set to ensure Lerna can operate in the CI environment and authenticate with NPM and GitHub Package Registry as needed.

***

*Moving beyond automation with Lerna's CLI, next up we explore the process of validating changes in our packages—how we ensure that updated packages integrate smoothly within exile.watch, whether through local bumps or prereleases.*


# 6. How does one test lerna packages at exile.watch: locally and in the real world

5 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

*This page is part of* [Lerna - the hidden powerhouse of exile.watch](/march-2024/lerna-the-hidden-powerhouse-of-exile.watch#the-toolkit-i-needed-to-make-it-all-happen) *list - 6/7*

***

The final part explores how to effectively operate within this environment, focusing not only on local development but also on verifying the correct publication of prereleased packages.

As a starting point, I recommend reading [The package manager of exile.watch](/march-2024/the-package-manager-of-exile.watch) first. In it, I discuss package linking across all the package managers I evaluated, ultimately choosing npm as my go-to option.

## 1. [npm link and npm link @exile-watch/\<package>](https://docs.npmjs.com/cli/v8/commands/npm-link)

Utilizing `npm link` and `npm link @exile-watch/<package>` is the straightforward approach that works well most of the time—despite the occasional hiccup when symlinks go out of sync, which isn't a frequent issue and can be managed.&#x20;

I've detailed the challenges with this method in the previously mentioned post about package managers for those interested in digging deeper.

## 2. [yalc](https://github.com/wclr/yalc)

Yalc offers a solution akin to `npm link`, presenting a viable option for linking packages, though its success rate varies (see [#217](https://github.com/wclr/yalc/issues/217)).&#x20;

When it comes to integrating Yalc with Next.js, the reliability dips even further, as evidenced by mixed results (see [#188](https://github.com/wclr/yalc/issues/188) and the mirrored issue [#35110](https://github.com/vercel/next.js/issues/35110) on the Vercel repository).

A significant limitation emerges with chain linking.&#x20;

Why does this matter?&#x20;

Yalc falls short in resolving dependencies of dependencies (see [#95](https://github.com/wclr/yalc/issues/95)).&#x20;

For exile.watch, where each package serves as a comprehensive boilerplate focusing on distinct functionalities and configurations, this limitation complicates the testing process in consumer projects, making it a tedious task to ensure package compatibility.

## 3. [Preview publish](https://docs.exile.watch/projects/doryani/actions/preview-publish-package)

So, outside of using `npm link`, how do we test a package in its consumer environment?&#x20;

Enter our homebuilt automated prerelease system, which versions each release based on the commit hash.&#x20;

This method is as close as it gets to real-world testing.&#x20;

Why?&#x20;

Because it evaluates the package post-bundling, ensuring the changes behave as expected.

Occasionally, local modifications might not carry over after bundling, leading to errors from unsupported syntax (this is a skill issue of mine by sometimes being a dummy :))

[You can see it in action here](https://github.com/exile-watch/splinters/pull/17).\
*(I won't elaborate how this action works under the hood here as it deserves it's own post - for now here is the* [*sub-actions section*](https://docs.exile.watch/projects/doryani/actions/preview-publish-package#sub-actions) *which highlights what each step is responsible for*).&#x20;

***

*(WIP) Next on the line: Dependabot*

***

The links below won't redirect you to the initial page so here you have it for the conclusion of these parts:[/pages/bQNDvFgFO328G9lChDFC#lerna-impact-on-exile.watch](https://engineering.exile.watch/march-2024/lerna-the-hidden-powerhouse-of-exile.watch/pages/bQNDvFgFO328G9lChDFC#lerna-impact-on-exile.watch "mention")


# To open source or to not open source

3 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch logo</p></figcaption></figure>

TL;DR [exile.watch](https://github.com/exile-watch) is fully (excluding docs & engineering blog for now) open source.&#x20;

Always was, is, and will be.

***

## What is Open Source?

Open source refers to a type of software license that allows the source code to be freely available for anyone to view, modify, and distribute.&#x20;

This concept is rooted in the principles of collaboration, transparency, and community-driven development.&#x20;

Open source software is developed in a decentralized and collaborative manner, often by communities of individual programmers and organizations who share a common interest in the software's development and success.

## Path of Exile and it's tooling history

You'd be surprised at how many community tools have been developed in the 10 years since [Path of Exile](https://www.pathofexile.com/) hit the scene. There are so many, in fact, that there's a [separate website](https://grinding.zone/) just to list them all!

This isn't because [Grinding Gear Games](https://www.grindinggear.com/) (GGG), the creators of Path of Exile, are neglecting their game. Far from it.

Most community concerns have been addressed one way or another, either through official Q\&A sessions after league content reveals or in community podcasts with GGG's directors.

The Path of Exile game is insanely complex. There's an internal joke in the community that you basically need a PhD and to have taken dedicated MIT classes to tackle certain systems the game offers – and that's not even an exaggeration.

Every league, introduced every 3 to 4 months, adds layers to this complexity. Given this pace, it's physically impossible for Grinding Gear Games to anticipate and provide solutions for issues they don't even know exist until the community gets their hands on the new content.

For programmers, Path of Exile is a dream come true as it encourages problem-solving.

By the time GGG could come up with a solution, you can bet the community will have already devised something that works well enough for everyone.

***

## Status of closed/open source tools in PoE

Everyone loves knowing that the tool they're planning to use (or are already using) is open source.

The full transparency that open source offers goes a long way, especially in communities where tooling isn’t just nice to have—it’s practically essential. And let's be clear: we're talking essential for quality of life, not pay-to-win. That's a huge distinction and it’d be unfair to mix them up.

We've got browser extensions and desktop apps developed by community members. My advice? Always double-check what you’re downloading, especially if it’s a [closed source browser extension](https://www.howtogeek.com/188346/why-browser-extensions-can-be-dangerous-and-how-to-protect-yourself/) or [desktop app](https://www.mend.io/blog/theres-a-new-stealer-variant-in-town-and-its-using-electron-to-stay-fully-undetected/). This isn't just about PoE—it's a good rule of thumb in general.

Here’s my hot take: It only takes *one* bad actor in the tooling community to shake a decade’s worth of trust to its core.&#x20;

*If* that ever happens (and let's hope it doesn't), closed source tools are going to face a serious trust crisis. And that? That'd be a rough time for anyone using community tools in PoE, not to mention the devs behind those tools (unless, of course, you’re fully open source).

As much as we’re grateful for GGG’s take on QoL tooling, something like this could prompt an official response from them that might limit the freedom the community currently enjoys.

***

## exile.watch creator credibility

I've been part of [Exilence Next](https://github.com/viktorgullmark/exilence-next) team since late 2020, all the way until its [official servers shut down](https://www.reddit.com/r/pathofexile/comments/13jx5ue/rip_exilence_thanks_for_all_your_hard_work_over/) in mid 2023. In short this was a desktop application tracking Path of Exile gamer's wealth.

Exilence Next was an open source project. It was... big. Not the application itself. But the amount of users we had and most importantly the bandwidth usage.&#x20;

Out of respect for the original creators of Exilence (and GGG, considering bandwidth issues), I won't disclose exact numbers.&#x20;

Exilence was one of the handful of apps—literally, you could count them on one hand—that likely played a big part in the strict rate limiting every PoE app developer faces today.

***

Author: [Sebastian Krzyżanowski](https://github.com/sbsrnt)\
About *exile.watch*: <https://docs.exile.watch/>\
Github: <https://github.com/exile-watch>\
\
Visit <https://exile.watch/> to experience it first hand


# exile.watch architecture

10 min read

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2FzXjtfpnPjXaN7gAZDm9L%2Fexilewatch.webp?alt=media&amp;token=74fc8000-b114-47be-bc13-cda9b75cd22f" alt="" width="256"><figcaption><p>exile.watch.logo</p></figcaption></figure>

[Seven repositories, over ten packages, one Chrome extension, and one user-facing website](https://docs.exile.watch/architecture/dependencies)... all managed by [one person](https://github.com/sbsrnt).

Wait, wait, wait... How many repositories and packages are there again?

For... *one* user-facing website? Surely there must be some micro-frontends because otherwise, this seems like a completely overengineered solution.

No. There are no micro-frontends. In fact, the [user-facing website doesn't even have ten pages](https://github.com/exile-watch/crucible/tree/develop/pages).

You read it correctly. This modularity is by design. But is this approach beneficial?

Let's dive in.

***

## Why [React](https://react.dev/)? Why [Next.js](https://nextjs.org/)? Why not [Vue](https://vuejs.org/) or [Angular](https://angular.io/) or x, y, z?

I’ve been sticking with React for a good while now (7 years and counting), and honestly, it just fits like a glove for what I need to do. Picking it was pretty much a no-brainer for me.

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2F0D9UI77aropG8xRMuBG4%2Fimage.png?alt=media&amp;token=1bc57486-46fd-4f5e-8253-aba5a959f41e" alt=""><figcaption><p>Comparison chart of React, Vue.js and Angular</p></figcaption></figure>

Does React have [flaws](https://www.google.com/search?q=reactjs+cons\&ie=UTF-8)? Sure, as do other tools. However, I won't be delving into those here.&#x20;

Given the project's needs, [SEO (Search Engine Optimization)](https://en.wikipedia.org/wiki/Search_engine_optimization) was a significant consideration. exile.watch was initiated in 2021, and while I don't recall if SEO was a strong selling point for React at that time, I do remember that Next.js was particularly effective at satisfying SEO crawlers.

At the time, Next.js offered the server-side rendering capabilities with the least developer experience friction.

***

## I have the framework, now what's the structure?

I'm a big fan of the [Bulletproof React repository](https://github.com/alan2207/bulletproof-react) for its clean and concise approach to project structure. This is my goal whenever I start a new React/Next.js project, and that's exactly what I aimed for with exile.watch in 2021.

<pre><code><strong>// simplified structure of exile.watch in 2021
</strong><strong>.
</strong>├ components
│ ├ ComponentA
│ │ └ ComponentA.tsx
│ ├ ComponentB
│ │ └ ComponentB.tsx
│ └ index.ts
├ hooks
│ └ useHook.ts
├ pages
│ ├ encounters.tsx
│ └ index.tsx
├ tsconfig.json
└ package.json
</code></pre>

The development of exile.watch was on full throttle.

However, with the introduction of the [Expedition league](https://www.pathofexile.com/expedition) in the [Path of Exile game](https://www.pathofexile.com/)...

I lost all interest in the game, and consequently, my motivation to continue with exile.watch diminished.

***

## Where did the idea of modularity come from?

End of January 2024 enters the chat. Being let go from the company [due to bankruptcy](https://www.fiercebiotech.com/medtech/invitae-prepares-sale-it-files-chapter-11-bankruptcy) I developed an itch to learn Lerna.&#x20;

During my time at the company, we extensively levereged Lerna. I was part of the [Engineering Productivity](https://jellyfish.co/library/engineering-productivity/) team, and over time we created a stellar frontend architecture that I will always aim to achieve, as long as resources (people and time) will allow.

*This* is what sparked my comeback to exile.watch after a three-years hiatus, given the "dependency mess" I had left behind and the breakdown to smaller projects and packages.

I decided to use exile.watch as my own sandbox for learning.

### [Lerna](https://lerna.js.org/)?

Lerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository.

[Monorepos](https://monorepo.tools/) let you develop multiple packages in the same repository, which is fantastic for productivity. You can share code, establish clear ownership, and have fast unified CI.

To use monorepos well, you need a good monorepo tool, and Lerna is exactly that. Lerna is [fast](https://github.com/vsavkin/large-monorepo), widely used, and battle tested.

***

## Sweet - I have the concept mapped in mind, but where do I begin?

So, I've got React, Next.js, Lerna... components, hooks, some dependencies... *Some* dependencies.

*29* dependencies, *49* devDependencies. That's... *A LOT*.

```
{
    "dependencies": {
    "@reduxjs/toolkit": "^1.5.1",
    "@sentry/browser": "^6.4.1",
    "@sentry/integrations": "^6.4.1",
    "@sentry/node": "^6.4.1",
    "@sentry/react": "^6.4.1",
    "@sentry/tracing": "^6.4.1",
    "@sentry/webpack-plugin": "^1.15.1",
    "@svgr/webpack": "^5.5.0",
    "classnames": "^2.3.1",
    "cookie-parser": "^1.4.5",
    "downshift": "^6.1.3",
    "express": "^4.17.1",
    "framer-motion": "^4.1.17",
    "is-hotkey": "^0.2.0",
    "lodash": "^4.17.21",
    "next": "^10.2.2",
    "pako": "^2.0.3",
    "react": "17.0.2",
    "react-cookie": "^4.0.3",
    "react-dom": "17.0.2",
    "react-hook-form": "^7.6.5",
    "react-redux": "^7.2.4",
    "react-xml-parser": "^1.1.8",
    "sass": "^1.34.0",
    "slate": "^0.63.0",
    "slate-history": "^0.62.0",
    "slate-react": "^0.63.0",
    "xml2ts": "^0.6.1",
    "xmltojson": "^1.3.5"
  },
  "devDependencies": {
    "@babel/core": "^7.14.3",
    "@commitlint/cli": "^12.1.4",
    "@commitlint/config-conventional": "^12.1.4",
    "@mdx-js/react": "^1.6.22",
    "@storybook/addon-actions": "^6.2.9",
    "@storybook/addon-docs": "^6.2.9",
    "@storybook/addon-essentials": "^6.2.9",
    "@storybook/addon-links": "^6.2.9",
    "@storybook/preset-scss": "^1.0.3",
    "@storybook/react": "^6.2.9",
    "@storybook/theming": "^6.2.9",
    "@svgr/cli": "^5.5.0",
    "@types/is-hotkey": "^0.1.2",
    "@types/js-yaml": "^4.0.1",
    "@types/lodash": "^4.14.168",
    "@types/node": "^14.17.0",
    "@types/pako": "^1.0.1",
    "@types/react": "^17.0.6",
    "@types/react-redux": "^7.1.16",
    "@types/xmltojson": "^0.0.28",
    "@typescript-eslint/eslint-plugin": "^4.24.0",
    "@typescript-eslint/parser": "^4.24.0",
    "babel-eslint": "^10.1.0",
    "babel-loader": "^8.2.2",
    "commitizen": "^4.2.4",
    "css-loader": "^5.2.5",
    "cz-conventional-changelog": "^3.3.0",
    "date-fns": "^2.21.3",
    "eslint": "^7.27.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-config-react-app": "^6.0.0",
    "eslint-plugin-flowtype": "^5.7.2",
    "eslint-plugin-import": "^2.23.3",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-prettier": "^3.4.0",
    "eslint-plugin-react": "^7.23.2",
    "eslint-plugin-react-hooks": "^4.2.0",
    "eslint-plugin-simple-import-sort": "^7.0.0",
    "fork-ts-checker-webpack-plugin": "^6.2.10",
    "fs": "^0.0.1-security",
    "js-yaml": "^4.1.0",
    "lint-staged": "^11.0.0",
    "prettier": "^2.3.0",
    "sass-loader": "^10",
    "style-loader": "^2.0.0",
    "ts-loader": "^9.2.2",
    "ts-node": "^9.1.1",
    "tsconfig-paths-webpack-plugin": "^3.5.1",
    "typescript": "^4.2.4"
  }
}
```

I have *waaay* more devDependencies than dependencies, so let's address this first.&#x20;

Given what [devDependencies](https://stackoverflow.com/a/22004559) are all about, it made sense to kick things off by tackling the build tools. This meant setting up my first Lerna monorepo to manage just that.

First up, creating [@exile-watch/typescript-config](https://github.com/exile-watch/splinters/pkgs/npm/typescript-config) to ditch 10 TypeScript-related packages.

Next, swapping out individual code quality tools for [@exile-watch/biome-config](https://github.com/exile-watch/splinters/pkgs/npm/biome-config), which axed another 11 packages.

And planning to swap out Storybook for @exile-watch/storybook-config, shedding another 7 packages in the process.

Seeing the pattern yet?

Same story with the dependencies.&#x20;

Initially, I didn't lean on any UI library, which meant my themes and icons were kinda all over the place. That sparked the creation of [@exile-watch/writ](https://github.com/exile-watch/writ), our Lerna monorepo design system, housing [@exile-watch/writ-react](https://github.com/exile-watch/writ/pkgs/npm/writ-react) and [@exile-watch/writ-icons](https://github.com/exile-watch/writ/pkgs/npm/writ-icons) packages.

*PS:* [*Here is the current list of dependencies for comparison*](https://github.com/exile-watch/crucible/blob/develop/package.json)

## With great modularity comes great responsibility

And here I am, two weeks after diving back into the project.

7 repositories, 10+ packages, 1 chrome extension and 1 user facing website.

But... *how do you even keep track of all this*?

Simple answer: [Dependabot group updates](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#groups).

<figure><img src="https://1213438767-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4DXEoZIzUdpt18CW2qTD%2Fuploads%2Ff0t8QQCi7LK5upRxmbjo%2Fimage.png?alt=media&amp;token=8cd7e645-952c-488a-9d89-ab4669382301" alt=""><figcaption><p>List of group pull requests opened by Dependabot</p></figcaption></figure>

I wrote a separate post about Dependabot [here](/march-2024/the-savior-amidst-the-chaos-of-dependency-updates-dependabot).&#x20;

For now, just know Dependabot is like that reliable buddy doing all the heavy lifting for you—keeping everything updated without you breaking a sweat.&#x20;

&#x20;You can see it in action[ here](https://github.com/exile-watch/crucible/pull/9).

## So... is this approach beneficial?

Let's wrap this up with some pros and cons based on what I've been through.

### Pros

* *Everything's modularized*: Each Lerna monorepo, package, and consumer app knows its lane, focusing sharply on its defined scope
* Jump-starting new projects is a breeze since everything’s pre-configured, letting other team members hit the ground running with feature development
* Unified updates mean when something changes, it hits the whole org at once—keeping everyone on the same wavelength
* Frequent rotations? Easy. Moving between projects is smooth since you’re always familiar with the setup
* The development approach is opinionated, guiding the team with clear rules to follow

### Cons

* Thinking of kicking off a new project? This architecture might feel like swatting a fly with a sledgehammer—total overkill unless you’re in it for the learning curve.
  * Got a couple of frontend teams already? Great! This might be time to think about bringing things together with a centralized solution
* Still on the fence? Remember what I said about sticking to [Bulletproof React](https://github.com/alan2207/bulletproof-react) for simplicity’s sake
* Resource drain alert: Getting all this up and running eats up time and people—usually needing a dedicated person or a small squad focused on R\&D and pushing engineering productivity to new heights
* Update fatigue is real. Without Dependabot finely tuned, your team might get bogged down with constant updates, shifting focus from feature delivery to keeping up with the latest package versions

***

Author: [Sebastian Krzyżanowski](https://github.com/sbsrnt)\
About *exile.watch*: <https://docs.exile.watch/>\
Github: <https://github.com/exile-watch>\
\
Visit <https://exile.watch/> to experience it first hand


