Best Practices for Coding and Architecture¶
This page provides in-depth discussion of how to code in various frameworks and libraries, and how to deploy that code, based on OpenCraft experience with past and current clients.
It augments the coding standards laid out in the OpenCraft handbook.
Reinventing the Wheel¶
If a free or inexpensive project exists with a compatible license which is regularly updated and it provides the functionality you need, use it!
Push back hard on requests for custom development of these existing tools, except for "plugin" style development which is expressly supported by the tool developers.
Before agreeing to do custom development, get solid, written answers to these questions:
- What will you do if the tool changes in the future? Who is going to pay for fixing your custom code?
- Who is responsible for the security of the custom code?
- What happens to the budget and/or whole project if a custom-built integration is more complex than the initial discovery estimated?
Upstreaming is always an option, but make sure the upstream developers welcome community contributions before offering it to clients.
React.js¶
See OEP-67: Frontend Technology Selection for advice and best practices related to React.
Accessibility¶
At minimum, all OpenCraft websites must conform to the accessibility standards laid out by WCAG 2.1 levels A and AA, and where practically possible, level AAA.
Please read the following two documents:
edX Accessibility Guidelines provides some general guidelines for writing accessible code.(we are waiting for a new link to this.)- Reactjs: Accessibility provides a good summary of a11y issues common to React applications, as well as a number of useful development tools for assessing maintaining a11y, including: inspectors for the a11y tree, and several screen readers which can be used by different OS/browser combinations.
Rob Dodson has some nice a11y videos on youtube. These two are especially recommended:
For a more comprehensive overview of a11y related topics for developers, the Web Accessibility course on Udacity is pretty good.
- WAI-ARIA Authoring Practices 1.1: useful advice
("No ARIA is better than Bad ARIA!") and accessibility development principles, including guides on how to use
aria-roleappropriately. - WCAG checklist: useful lists of what to look for when creating accessible websites.
- WCAG 2.1 AA Requirements: toggle "Level AAA" checkbox in the left sidebar to display AAA level requirements as well
- Paragon: edX repo providing accessible React UI components
As with all OpenCraft PR/MR reviews, every code review must include an accessibility check. To test accessibility (a11y) effectively, manual steps are required, but can be assisted by some automated tools.
- eslint-plugin-jsx=a11y
- Use
this.performAccessibilityAudit()inside aViewto manually trigger an accessibility audit. You should do that after performing actions which change the state of the page, for example after clicking a button that triggers a modal dialog.
- Use
- axe-core
- The axe plugin (chrome / firefox) helps find WCAG 2 and Section 508 accessibility defects on web applications.
Backend Architecture¶
There are two different architecture styles for the backend to a JavaScript web frontend.
In one version, the JavaScript calls APIs on a single backend. If data is needed from other APIs, that backend will fetch it and return the results.
In another version, the JavaScript calls each API from every service and assembles the results in the browser.
Neither is inherently superior, but consistency and planning will save you a lot of headache down the road.
Some components to keep in mind:
- An identity provider. Who handles login/registration and basic user account data?
- A source of content. Where does the data to be displayed come from?
- A source of metadata. How do you annotate the data in ways custom to your application?
- Auxiliary services. What other software does your application need to work with? How will that work?
- Asynchronous task workers. What work does your application do "in the background"?
Writing Good Tests¶
In general, keep tests located next to the code they cover. So if the model is defined in /apps/classes/models.py, the test should be in /apps/classes/models_test.py.
When a flaky test is discovered, create a ticket immediately for fixing it. Do not let them pile up!
REST API Standards¶
- The API should be RESTful and fully documented in an auto-generated API Spec file.
- Implementation details should be hidden from the frontend. This allows you to change implementation strategies, do data migrations, and do A/B tests without needing to modify the frontend.
- As much as possible, the frontend code should use models defined by the auto-generated API client rather than re-defining them.
- The API should be versioned, e.g.
/api/v1/endpoint/:idnot/api/endpoint/:id.
Testing APIs¶
REST API tests should guarantee API functionality and backwards compatibility. A test failure should represent a breaking change to the REST API contract. Internal refactoring should never result in breaking these tests.
To achieve that:
- Data setup should happen via REST API. No factories or models.
- Introspection should happen via REST API. No query counts or model queries.
- Tests should look for particular fields of interest but not assume they know every field, since additional ones can be added in a backwards compatible way. Testing one attribute at a time also makes it a lot easier to see when there is a regression, instead of trying to look at a large diff.
User IDs¶
These guidelines from edX require that "there must not be any APIs or pages that expose the association between the LMS user_id and any of the user's PII (e.g. username and full name) to unauthorized users." Since we allow unauthorized/unregistered users to view many pages on the site, including the discussion forums, which are full of usernames and display data about users retrieved from our APIs, that rules out using the LMS user ID in APIs. REST API should use usernames to identify users throughout. This is simple, URL-friendly, and has the nice advantage that the usernames are the same across all services (unlike the user IDs).
Developer Experience¶
When a model is added or modified, provide Django fixtures to go with it.
This helps with unit testing and speeds up manual tests by providing a way for the reviewer to add sample data.
Make sure any references to users or other database objects in your fixture use natural keys, since the user IDs may be different for different developers.
Makefiles for common developer commands are a great idea! Don't make developers remember which obscure incantation will load their test data or provision their devstack.