Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions oeps/best-practices/oep-0067-bp-tools-and-technology.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,21 @@ Frontend Technology Selection
Documentation on how to configure Renovate automation on a repository is available
in the `Upgrade Automation How-to`_.

#. **JavaScript projects should use caret ranges for npm dependencies**

**Rationale**: Using caret ranges (e.g., ``"^1.2.3"``) in our ``package.json`` more clearly
communicates that our projects are designed to accept semver-compatible (minor and patch)
updates. This intent is important because merely pinning top-level dependencies does not
extend to their transitive dependencies—potentially leading to duplicative or outdated
packages in our bundles. Moreover, specifying dependencies with caret ranges streamlines
our `Renovate`_ workflow. With caret ranges, non-breaking updates are typically resolved
automatically during installation, so Renovate will primarily flag major version bumps
that require manual intervention. In contrast, pinned dependencies can trigger frequent
Renovate updates for every minor or patch change, increasing maintenance overhead.

**Decision Record**: For details, see
:ref:`Use Caret Ranges for npm Dependency Versions`.

#. **JavaScript should be bundled using Webpack**

**Rationale**: `Webpack`_ is the tool of choice for code optimization and
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _Use Renovate to update dependencies:

Use Renovate to update dependencies
###################################

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
.. _Use Caret Ranges for npm Dependency Versions:

Use Caret Ranges for npm Dependency Versions
############################################

Status
******

Proposed (February 2025)

Context
*******

Managing dependencies in JavaScript projects requires balancing stability, maintainability, and performance. Historically, most projects
have opted to pin exact dependency versions to ensure consistency. However, this approach has notable downsides, particularly when
considering transitive dependencies, package duplication, and update workflows.

Decision
********

JavaScript projects in the Open edX community should define dependencies using caret ranges (e.g., ``^1.2.3``) instead of pinning exact
versions (e.g., ``1.2.3``).

Why?

#. **Pinned Dependencies Do Not Fully Lock Transitive Dependencies**: Even if a top-level dependency is pinned, its transitive dependencies
are not necessarily locked. This means updates can still introduce changes, making pinning less effective than intended.

#. **Reduces Webpack Bundle Size and Dependency Fragmentation**: When different packages pin slightly different versions of the same dependency,
multiple versions of that dependency may be included in the final bundle. Using caret ranges allows different packages to share a single version,
reducing duplication and improving performance.

#. **More Clearly Communicates Engineering Intent**: Specifying caret ranges explicitly indicates that a project intends to accept non-breaking
updates in accordance with semantic versioning. This improves clarity for maintainers and contributors.

#. **Improves Renovate Workflow and Reduces Maintenance Overhead**: The Open edX platform to automate and manage dependency updates (see
:ref:`Use Renovate to update dependencies` for more details). With caret ranges, Renovate still detects and opens PRs for minor and patch updates,
but these updates typically require only a ``package-lock.json`` change rather than modifying ``package.json``. This allows Renovate to group updates
more efficiently and reduces unnecessary PR churn. In contrast, pinned dependencies require Renovate to open a PR for every minor or patch update to
explicitly modify ``package.json``, increasing maintenance overhead.

Consequence
***********

#. Projects will automatically receive non-breaking dependency updates within the specified range, reducing the need for manual intervention.
#. Webpack bundle sizes may decrease due to fewer redundant versions of dependencies.
#. Renovate will generate fewer update PRs, focusing primarily on major version changes that require attention.
#. Developers should continue using lockfiles (``package-lock.json``) to ensure consistent installations across environments.

Rejected Alternatives
*********************

#. **Continuing to pin dependencies in JavaScript projects**

* Pinning dependencies does not effectively prevent updates to transitive dependencies.
* Leads to unnecessary duplication of packages in Webpack bundles, increasing size and complexity.
* Results in excessive Renovate PRs for minor and patch updates, increasing maintenance burden.