Skip to content

Code Standards

Taylor Snead edited this page Aug 23, 2022 · 2 revisions
  • Add comments to all top-level items describing what it is in more detail, why it's separate from other things, why it was written like it is, etc.
    • You could also add a comment describing the whole file (e.g. Typescript, Rust). This can provide the scope of that file/module.
  • If you are considering adding a new element of change to your PR, the answer is probably to make a separate PR.

Front-end / Typescript / React

  • Code is formatted with prettier
  • Encapsulate all code that deals with a Context in one file. Never export the context itself, only export a Provider component and one or more hooks exposing context data.
  • Use components defined in website/src/components whenever possible
    • Otherwise use components from reakit if there is one
  • Add types wherever possible (objects, variables, function arguments, etc) and avoid the any type unless you really need it
  • Avoid the as operator for typecasting
  • Avoid unnecessary HTML elements. If you are nesting two or three div elements without any siblings, then reconsider your approach.
  • Always supply an aria-label to non-text elements (like icons or icon buttons)
  • Use the relevant semantic HTML element instead of div when possible
  • React components have PascalCase names, while other constants and functions get camelCase names
  • Use abbreviations in names sparingly -- most of the time the full name is easier for others to understand
  • Avoid brand/library names in our own code. For example, prefer graphqlClient over apolloClient or urqlClient.
  • For React hooks, always check the dependency list to ensure that all other input state is accounted for.
const [x, setX] = useState(0)
useEffect(() => {
  doSomething(x)
}, [x]) // <-- make sure x is here because it's used within the hook
  • Avoid exceptional returns in React components when possible because it can lead to mistakes with hooks.
// Do this!
if (condition) {
  return null
} else {
  return (...)
}

// ...instead of this
if (condition) {
  return null
}
// ...
return (...)
  • Be mindful of how much rendering your hooks require the component to do.
    • Use useMemo when creating expensive objects during render.
    • Use primary constants for the default value of useState because those values are recomputed every render. You can also do useState(() => initialValue) to provide a lazy initial state.
    • Combine multiple useEffect calls into one whenever possible

React Context

When creating a new React context to store global state, there are a few guidelines to consider.

  • Put the context object in a new file and call it XContext where X names the state, along with a provider component XProvider and one or more hooks that allow components to consume/affect the state.
  • Never export XContext itself, put all direct usage of it in the same file.
  • If the context interacts with an external system like cookies, local storage, or a 3rd party API then encapsulate as much direct usage of that system into the provider itself to avoid leaking implementation details.

Front-end styles

  • Styles are written in .css.ts files then imported into .tsx files
  • All styles are mobile-first, with desktop styles applied based on media queries. For example:
const myStyle = style({
  color: "blue", // blue on mobile
  "@media": {
    [mediaQueries.medium]: { color: "red" }, // red on tablets and smaller desktop screens
    [mediaQueries.large]: { color: "maroon" }, // maroon on larger screens
  }
})
  • Avoid vw and vh units, especially for desktop styles. Use fixed or percentage sizes instead.
  • Avoid magic numbers in styles, use values from src/style/constants when possible. You can also add reusable constants if one is missing. For example, use vspace for vertical spacing and hspace for horizontal spacing instead of values like 10px.
  • Never write pixel font sizes. Always use rem units for that

Back-end / Rust

  • Code is formatted with rustfmt/cargo fmt
  • Avoid using .unwrap() on Option<T> because on failure it causes the program to crash instead of allowing us to handle the error

SQL

  • Code is formatted with sqlfluff fix types/queries in the root folder
  • Always run dev-generate-types before committing SQL changes, and also commit the changes to sqlx-data.json
  • Table names are singular nouns, i.e. word, document, edited_collection
  • Use all lowercase for everything, including keywords like select, where, join, etc.
  • Always specify the type of join you're using. e.g. inner join instead of shortcut join
  • When you use a join, fully specify all column selections by table name. For example, if I use a join to access both document and word tables, never use plain index_in_document to refer to that column. Use word.index_in_document instead, even when there isn't a name conflict between the tables.
  • Use standard SQL by default and when possible, but dip into Postgres extensions when convenient
  • Never ever edit database migrations that are already merged to main and applied on live deployments. We always have to make new migration files for new changes, because postgres skips completed migrations based on the file name and our migration process will fail if an existing migration is edited
  • Almost always use an autouuid for primary keys. This is a "surrogate" key when we might also have a "natural" key in a separate unique column.
id autouuid primary key,
slug text unique,