OEP-67: Standard Tools and Technologies#

OEP

OEP-67

Title

Tools and Technology Standards

Last Modified

2024-07-25

Authors

Feanil Patel <feanil@axim.org>

Arbiter

Braden MacDonald <braden@opencraft.com>

Status

Accepted

Type

Best Practice

Created

2023-09-05

Review Period

2023-09-05 - 2023-10-10

Resolution

References

OEP-11: Front End Technology Standards

Abstract#

The Open edX Platform is built using many first- and third-party tools and technologies. This document covers the current recommendations for which tools and technologies should be used.

Motivation#

The Open edX platform has a lot of first and third party tools and technology that it uses. The problems we’re trying to solve are:

  • It is difficult to know the current best practice for a given area.

  • It is unclear how to go about the discussion and decision-making process to change a best practice.

OEP-11 serves as a great example for the usefulness of this kind of OEP but limits itself to be relevant only to frontend technologies. This OEP would supersede OEP-11 and encompass not only front-end technology but any technology that:

  • Is considered a best practice for the Open edX Platform

  • Is relevant to a large subset of the Open edX Platform

Specification#

Note

We’ll fill this out before the OEP lands but leaving mostly blank for initial feedback.

General Technology Selection#

This technology is not specific to frontend or backend code.

  1. Codecov should be used to report code coverage by unit tests

    Rationale: It is important to measure the amount of code covered by our automated test suites. By striving for a high level of test coverage, we can reduce the number of bugs that can only be found via manual testing, and by using codecov to run coverage in CI, contributors are automatically reminded to include tests for any new code.

    The Open edX community has standardized around Codecov as a coverage reporting service.

  2. Use Github actions for CI Testing

    Rationale: As a healthy open source project we need good continuous integration testing to prevent the introduction of obvious bugs into the platform code. The Github Actions tool should be used to perform Continuous Integration (CI) testing for all Open edX repositories that perform any sort of software testing.

Frontend Technology Selection#

Note

The phrase “frontend” is used to mean any part of the platform that is shown to users. This encompasses views rendered in Python on the server, interactive interfaces written using JavaScript, and CSS styling.

  1. Use React

    Rationale: React must be used for building new UIs, as it is widely adopted by the community and strikes a balance between flexibility and feature richness.

  2. Use React Query for data loading

    For loading data from the platform or other external sources (e.g. via REST APIs), React Query should be used.

    For client-side state management, regular React “state” and “context” should be used alongside React Query, following the best practices described in the official React docs: Managing State.

    Exception: Much of our frontend code is currently written using Redux for both data loading and state management, and that is also an accepted approach. However, for new code or major refactorings of existing code, it is recommended to use React Query and plain React state instead as described.

    Decision Record: For details, see OEP-0067-0010 React Query.

  3. Use Jest and React-Testing-Library to test React components

    We use Jest and React-Testing-Library to test React components. We are deprecating the Enzyme library. New tests must not use Enzyme, and any repositories planning to move to React 18 or newer need to replace Enzyme.

  4. Target the latest standardized JavaScript version (ECMA-262)

    Rationale: edX JavaScript should be written consistent with the latest ECMA-262 specification in order to ensure future support, the largest community and the availability of modern features. Currently, ES2022 is the latest version with full compatibility from the browsers we support. There is no need to use Babel, core-js, nor polyfills to provide backwards compatibility with unsupported browsers nor to transform ES2022 code into ES6/ES5 code.

    TypeScript: TypeScript should be utilized in frontend code wherever it seems to provide value (especially in any code that is reused: libraries, API wrappers, shared components, data type definitions, etc.). For details, see OEP-0067-0008 TypeScript.

    JSX: Babel or other tooling may be used to add JSX syntax support to our JavaScript/TypeScript code.

    Porting: Files written in ES5 and/or plain JS should be gradually converted to the newer standard (e.g. TypeScript with ES2022 syntax) as new development in those feature areas allows.

  5. JavaScript code should follow the edX ESLint configuration

    Rationale: In order to standardize and enforce Open edX’s JavaScript coding style across multiple codebases, edX has published an ESLint configuration that provides an enforceable specification. The edX ESLint Config is made available as an npm package that can be installed into any Open edX package.

    Exception: The edX ESLint Config for ES5 may be used only where ES5 is in use. Both configs may be used in the same codebase through the use of ESLint configuration cascading.

  6. JavaScript libraries should be installed via npm

    Rationale: It is important that JavaScript libraries are kept up-to-date, and one key component is to make it as simple as possible to perform upgrades. Projects that use npm maintain all of their library requirements in a single package.json file, which can be easily changed as the versions change.

  7. JavaScript projects should publish a package-lock file

    Rationale: To keep dependencies up-to-date and builds consistent, JavaScript projects should allow patch and minor upgrades in their package.json file and commit a package-lock.json file. The package-lock.json file will keep a full list of dependencies and their versions, ensuring when built for deployments the version of libraries are consistent. This follows the same pattern used in edX Python code - see OEP 18 for more information. For more information on package-lock files see Package Lock.

  8. Keep dependencies up to date by using Renovate

    Rationale: JavaScript dependencies are updated constantly and can be difficult to maintain over time. Renovate makes this easier by automatically updating dependencies in a package.json file and verifying the tests still work. When possible, leverage Renovate to ensure JavaScript software remains up to date. Documentation on how to configure Renovate automation on a repository is available in the Upgrade Automation How-to.

  9. JavaScript should be bundled using Webpack

    Rationale: Webpack is the tool of choice for code optimization and bundling, as it is widely used throughout the industry, and because it seamlessly handles modern code bases as well as all of the older technologies used by edX, such as AMD Modules. Webpack should be implemented to handle as much of the “asset pipeline” as possible, rather than passing this responsibility on to Django.

  10. JavaScript dependencies should be managed with ES Modules

    Rationale: JavaScript module systems allow front end code to specify its dependencies and be grouped into bundles that minimize the assets needed to provide page functionality. The most prevalent module syntax is currently ES2015 Modules, which should be adopted everywhere JavaScript/TypeScript code is used.

    Exception: Some of our existing JavaScript uses the older AMD Modules syntax for modules. AMD Modules are interoperable with ES2015 Modules if Webpack is used for bundling, so this is fine in legacy code, but should not be used in new or refactored code.

  11. CSS should be generated using SCSS

    Rationale: Sass’s SCSS syntax is an extension of CSS that adds power and elegance to the basic language. It makes the maintenance of large CSS files more manageable though the ability to use variables, mixins, imports and more. In particular, it makes theming possible with the ability to override variables that define colors, fonts etc.

    You can find out more about Sass in the official Sass documentation.

  12. API calls should be made with the edX Frontend Auth Client or Axios

    Rationale: The edX Frontend Auth Client simplifies the process of talking to edX APIs by using Axios interceptors and handling JWT Cookie authentication. It also provides React components to handle private routes and should be used when possible. When making calls to non-edX APIs Axios should be used to provide a consistent API.

    The fetch API was considered but Axios was chosen for its more intuitive API, particularly when handling HTTP errors with rejected promises.

  13. Server-side content should be rendered with Django Templates

    Rationale: Although it is advised to use client side templating with React, see Use React, in legacy code that generates HTML on the server, Django templates should be used. There are many template languages available for Django, but the simplest option is to use the built-in Django template engine.

    The Open edX codebase has a mixture of Django and Mako templates, but the former are easier to reason about because they don’t support arbitrary code evaluation.

    Exception: Mako templates can continue to be used within edx-platform for consistency with the existing code base. This is because the base templates and theming support are all provided via Mako, so it is too much to expect a new feature to be implemented with a different framework. There is much desire to replace Mako within edx-platform so this exception may eventually be removed.

  14. BundleWatch should be used to automatically monitor JavaScript bundle sizes

    Rationale: It is important for users of the Open edX platform that we deliver reasonably sized JavaScript bundles. This provides faster load times to all users, and is vital for users with low bandwidth and/or metered connections. To ensure we don’t unintentionally increase the size of our JavaScript bundles, we utilize BundleWatch for automated bundle size monitoring.

  15. Development dependencies should be separated into devDependencies

    Rationale: To keep installation and deployment of our MFEs as fast as possible, the “dependencies” field in package.json should reference only the packages needed to build the bundle of the MFE (this includes webpack via frontend-build, as well as any dependencies used in the actual MFE/React code). Any dependencies used only for testing, linting, formatting, or other development tasks should be put into “devDependencies” (e.g. Jest, eslint, TypeScript, @types/ packages, etc.).

Backend Technology Selection#

This section will cover any technology or tools that are not considered a part of the frontend.

Decisions#

This section lists the decision records for any time a standard technology has been change in the system along with the reasons why and the alternatives considered.

Consequences#

  • OEP-11 will be superseded.

  • All ADRs under OEP-11 will be moved to be under this OEP instead.

  • Future decisions for technology changes will require an ADR and an update to this OEP

  • The tech radar will serve as a snapshot of the current state of those decisions and to simplify making changes, we will co-locate it with the supporting documents for this OEP.

Change History#

2024-07-25#

  • Added “Development dependencies should be separated into devDependencies”

  • Pull request #615

  • Changed guidance on React state/data loading to recommend React Query instead of Redux

  • Updated JavaScript/TypeScript guidance

  • Pull request #616

2023-09-05#

2024-05-23#

  • Updated “Keep dependencies up to date” to recommend Renovate instead of Greenkeeper