[RFC] Boundaries #9435
Replies: 4 comments 2 replies
-
This is really exciting! I've been using dependency-cruiser and eslint-plugin-monorepo-cop to painstakingly emulate something like this. Having something like this directly in turbo sounds awesome. Nice work! i agree that boundary checks should be opt in for the ci. |
Beta Was this translation helpful? Give feedback.
-
I want in. |
Beta Was this translation helpful? Give feedback.
-
I could see the value of this kind of linting step, and it being integrated with the rest of package relationship configs (I had missed the fact we could have multiple I'd be interested to give this a try in nuqs, particularly to test the --exclude-errors flag, which seems like a convenient way on its own to see if there are any undesirable cross-links between packages. I've recently split my e2e test environments into multiple frameworks, and making sure there isn't too much wild cross-pollination between them would be nice. I had been using a linter config in some client projects for this purpose, can't recall which one though. |
Beta Was this translation helpful? Give feedback.
-
Devil’s advocate: why not enforce the boundaries by default? Make the “unsafe” behavior opt-in for users that are already using this, and make them aware of the drawbacks. As someone who’s dealt with this issue, there are no downsides to forcing that all packages either import local code, or import via a valid npm specifier (to all the points stated in the RFC). Vite has enforced this since v2.7 (and by extension, Vitest), so a lot of people are already used to build systems that require this. This may be a “breaking” or annoying change, but on the flipside, enabling by default could just improve the perception that people have of Turbopack’s quality—it guides you into safer monorepo strategies that are easier to optimize and scale. Or people will just get annoyed; hard to say 😅 |
Beta Was this translation helpful? Give feedback.
-
We’d like to introduce a Boundaries feature in Turborepo.
The JavaScript ecosystem has grown into having great support for monorepos, letting developers scale JavaScript and TypeScript repositories like never before. However, as a repository progresses to greater scales, it becomes important to enforce boundaries within the repository to maintain order and predictability in the codebase.
Problems we want to solve
Without clear boundaries in a monorepo, it’s too easy to use code from internal packages in undesirable ways:
../../
out of the root of a package. This breaks out of the model of JavaScript monorepos, since code now depends on other code without respecting the package graph. This can result in unexpected caching behavior, since Turborepo can’t account for code outside of the package graph in hashing.@repo/shared-marketing
package that can accidentally be used in an application that is outside of the marketing domain.Proposed Solution
The
turbo boundaries
command will:Additionally, any non-CI invocation of
turbo run
orturbo watch
will include a synthetic "Boundaries checking" task once the Boundaries feature has been enabled. This ensures that we can notify you as soon as possible after writing code that breaches a Boundary.Getting started with zero-configuration
Add
boundaries: true
to your rootturbo.json
to start using the recommended checks provided by Turborepo. (This is not required to use Boundary Tags.)This will enforce our recommended set of Boundaries for your Workspace:
apps/web
is a package’s directory, the code below would result in an error.@repo/utils
is not listed inapps/web/package.json#dependencies
, the code below would result in an error. This helps with some package managers that allow dependencies to be available where they aren't installed.Once added, you may find existing issues that you’re not ready to address quite yet. See the Allowlisting existing code section for more on how to incrementally adopt Boundaries.
Incrementally adopting Boundary Tags
Additionally, you can create Tags to mark domains, concerns, contexts, and other useful sets that you want to make custom Boundaries for.
In a Package Configuration, add a
boundaries
key.The schema for
boundaries
above represents:tags
: The tags for the package. These will be used for comparisons against the Boundaries of other packages.allowDependencyOn
: List of tags that internal dependencies of the package must include at least one of in its own tag list, including indirect dependencies. Any package that does not have any of these tags will be considered an error. Cannot be used withdenyDependencyOn
.denyDependencyOn
: List of tags that internal dependencies of the package cannot include any of in its own tag list, including indirect dependencies. Any package with any of these tags will be considered an error. Cannot be used withallowDependencyOn
.allowDependencyFrom
: List of tags that internal dependents must include at least one of in their own tag list, including indirect dependencies. Any package that tries to use this package but is not tagged with any members of theallowDependencyFrom
array will be considered an error. Cannot be used withdenyDependencyOn
.denyDependencyFrom
: List of tags that internal dependents most not include any of in their own tag list, including indirect dependencies. Any package that tries to use this package that is tagged with any of the members ofdenyDependencyFrom
will be considered an error. Connot be used withallowDependencyFrom
.Creating exceptions for existing code
Introducing Boundaries to a large codebase can be difficult when you want to enforce a new Boundary where there wasn’t one before, so we want to make sure that Boundaries are incrementally adoptable.
After creating a Boundary, you can quickly create an exception list for existing violations by adding an
--exclude-errors
flag.This will create an allowlist file in packages that have violations. The exclusion list would look something like:
Exclusions will also be inspected whenever Boundaries checking occurs (
turbo boundaries
,turbo run
,turbo watch
) and errors will be thrown for exclusions that aren’t being used.turbo boundaries --exclude-errors
will automatically fix the erroneous exclusions.Open questions
turbo boundaries
as a CI check, and allow all otherturbo run
invocations to proceed without checking Boundaries so that developers can get feedback from their other tasks sooner.Beta Was this translation helpful? Give feedback.
All reactions