Skip to content

Commit

Permalink
docs(core): dependency management guide (#14782)
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacplmann committed Feb 2, 2023
1 parent 2d56659 commit 0f5d2c3
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 2 deletions.
16 changes: 16 additions & 0 deletions docs/generated/manifests/menus.json
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,14 @@
"children": [],
"disableCollapsible": false
},
{
"name": "Dependency Management",
"path": "/more-concepts/dependency-management",
"id": "dependency-management",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Using Nx at Enterprises",
"path": "/more-concepts/monorepo-nx-enterprise",
Expand Down Expand Up @@ -1157,6 +1165,14 @@
"children": [],
"disableCollapsible": false
},
{
"name": "Dependency Management",
"path": "/more-concepts/dependency-management",
"id": "dependency-management",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Using Nx at Enterprises",
"path": "/more-concepts/monorepo-nx-enterprise",
Expand Down
20 changes: 20 additions & 0 deletions docs/generated/manifests/nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,16 @@
"path": "/more-concepts/why-monorepos",
"tags": []
},
{
"id": "dependency-management",
"name": "Dependency Management",
"description": "",
"file": "shared/concepts/dependency-management",
"itemList": [],
"isExternal": false,
"path": "/more-concepts/dependency-management",
"tags": []
},
{
"id": "monorepo-nx-enterprise",
"name": "Using Nx at Enterprises",
Expand Down Expand Up @@ -1440,6 +1450,16 @@
"path": "/more-concepts/why-monorepos",
"tags": []
},
"/more-concepts/dependency-management": {
"id": "dependency-management",
"name": "Dependency Management",
"description": "",
"file": "shared/concepts/dependency-management",
"itemList": [],
"isExternal": false,
"path": "/more-concepts/dependency-management",
"tags": []
},
"/more-concepts/monorepo-nx-enterprise": {
"id": "monorepo-nx-enterprise",
"name": "Using Nx at Enterprises",
Expand Down
5 changes: 5 additions & 0 deletions docs/map.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,11 @@
"id": "why-monorepos",
"file": "shared/guides/why-monorepos"
},
{
"name": "Dependency Management",
"id": "dependency-management",
"file": "shared/concepts/dependency-management"
},
{
"name": "Using Nx at Enterprises",
"id": "monorepo-nx-enterprise",
Expand Down
21 changes: 21 additions & 0 deletions docs/shared/concepts/dependency-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Dependency Management Strategies

Where do you define the dependencies of your projects? Do you have a single `package.json` file at the root or a separate `package.json` for each project? What are the pros and cons of each approach?

## Independently Maintained Dependencies

Without tools like Nx, this is the only dependency maintenance strategy possible. Each project is responsible for defining which dependencies to use during development and which dependencies to bundle with the deployed artifact. For javascript, these dependencies are specified in a separate `package.json` file for each project. Each project's build command is responsible for bundling the packages in the `dependencies` section into the final build artifact. Typically packages are installed across the repo using yarn/npm workspaces.

This strategy makes it very easy to have different versions of the same dependency used on different projects. This seems helpful because the code for each project exists in the same folder structure, but that code can't really be shared any more. If project1 and project2 use two different versions of React, what version of React will their shared code be written against? No matter how you answer, there will be bugs introduced in the system and often these bugs occur at runtime and are very difficult to diagnose.

Another potential problem with this approach is that sometimes a developer may have one version of a dependency installed in the root `node_modules` and a different version specified in the project's `package.json`. This can lead to a scenario where the app works correctly on the developer's machine but fails in production with the bundled version of the dependency. This is once again a bug that is difficult to diagnose.

## Single Version Policy

With this strategy, developers define all dependencies in the root `package.json` file. This enforces a single version for all dependencies across the codebase, which avoids the problems listed above. Individual projects may still have `package.json` files, but they are used only for the metadata defined there, not as a way of defining the dependencies of that project.

If there are React and Angular apps in the same repo, we don't want both frameworks bundled in the build artifacts of the individual apps. That's why the plugins Nx provides come with executors that use Nx's graph of dependencies to automatically populate the `dependencies` section of the individual `package.json` files in the build output and pre-populate a lock file for you. This enables your build artifacts to only contain the dependencies that are actually used by that app. As soon as a developer removes the last usage of a particular dependency, that dependency will be removed from the bundle.

The primary concern people have with this approach is that of coordinating updates. If two different teams are working on React apps in the same repo, they'll need to agree about when to upgrade React across the repo. This is a valid concern and beyond the scope of Nx to solve. If the developers can't cooperate, they should probably work in separate repos. On the other hand, if the teams can agree, it becomes much less work to upgrade the whole repo at the same time rather than performing that same upgrade process multiple times spread out over months or years. Performing the same code manipulation 100 places all at once is much easier than remembering how to perform that code manipulation 100 different times spread out over a year.

Consult the Nx executor's reference page to find options for populating dependencies and a lock file. If you would like to use Nx's graph to populate the dependencies of a project in your own scripts or custom executor, read about how to [prepare applications for deployment via CI](/recipes/ci/ci-deployment).
4 changes: 2 additions & 2 deletions docs/shared/concepts/integrated-vs-package-based.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ There are two styles of monorepos that you can build with Nx: integrated repos a

## Package-Based Repos

A package-based repo is a collection of packages that depend on each other via `package.json` files and nested `node_modules`. With this setup, you typically have different dependencies for each project. Build tools like Jest and Webpack work as usual, since everything is resolved as if each package was in a separate repo and all of its dependencies were published to npm. Moving an existing package into a package-based repo is very easy since you generally leave that package's existing build tooling untouched. Creating a new package inside the repo is just as difficult as spinning up a new repo since you have to create all the build tooling from scratch.
A package-based repo is a collection of packages that depend on each other via `package.json` files and nested `node_modules`. With this setup, you typically have [different dependencies for each project](/more-concepts/dependency-management). Build tools like Jest and Webpack work as usual, since everything is resolved as if each package was in a separate repo and all of its dependencies were published to npm. Moving an existing package into a package-based repo is very easy since you generally leave that package's existing build tooling untouched. Creating a new package inside the repo is just as difficult as spinning up a new repo since you have to create all the build tooling from scratch.

Lerna, Yarn, Lage, [Turborepo](/more-concepts/turbo-and-nx) and Nx (without plugins) support this style.

Expand All @@ -23,7 +23,7 @@ Lerna, Yarn, Lage, [Turborepo](/more-concepts/turbo-and-nx) and Nx (without plug

## Integrated Repos

An integrated repo contains projects that depend on each other through standard import statements. There is typically a single version of every dependency defined at the root. Sometimes build tools like Jest and Webpack need to be wrapped to work correctly. It's harder to add an existing package to this repo style because the build tooling for that package may need to be modified. It's straightforward to add a brand-new project to the repo because all the tooling decisions have already been made.
An integrated repo contains projects that depend on each other through standard import statements. There is typically a [single version of every dependency](/more-concepts/dependency-management) defined at the root. Sometimes build tools like Jest and Webpack need to be wrapped to work correctly. It's harder to add an existing package to this repo style because the build tooling for that package may need to be modified. It's straightforward to add a brand-new project to the repo because all the tooling decisions have already been made.

Bazel and Nx (with plugins) support this style.

Expand Down

1 comment on commit 0f5d2c3

@vercel
Copy link

@vercel vercel bot commented on 0f5d2c3 Feb 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-nrwl.vercel.app
nx-five.vercel.app
nx-dev-git-master-nrwl.vercel.app
nx.dev

Please sign in to comment.