## Emerald Coding Standards
###### Last Updated: Thursday, 13 October 2023
### Introduction
This document outlines the coding standards and best practices to be followed when writing and maintaing code in this repository.
There is always a certain amount of idealism when it comes to coding standards...
Here; we're are attempting to document idomatic learnings with the goal of bringing observability and best practices that serve as mutally benefitial ways of working, which have allowed and continue to allow us to write clean and maintainable code, enabling us to collaborate effectively and successfully with each other as a team.
It presents a realistic standard to which we are able to hold each other accountable to; aswell as enabling each of us to contribute, document and update with knowlege that presents itself through our collective learnings and experiences accordingly.
## Coding practices
1. [General](#1-General)
2. [Folder Structures](#2-Folder-Structure)
3. [Code Formatting](#3-Code-Formatting)
4. [Component Composition](#4-Component-Composition)
5. [Testing](#5-Testing)
6. [Documentaion](#6-Documentation)
7. [Code Review](#7-Code-Review)
8. [Error Handling](#8-Error-Handling)
9. [Styling](#9-Styling)
### 1. General
- _Prefer_ writing out words instead of using abbreviations.
- _Prefer_ _exact_ names over short names (within reason). E.g., `labelPosition` is better than`align` because the former much more exactly communicates what the property means.
- use `is` and `has` prefixes for boolean properties & methods.
#### Prefer small, focused modules
Keeping modules to a single responsibility makes the code easier to test, consume, and maintain. ES6 modules offer a straightforward way to organize code into logical, granular units.
Ideally, individual files should be 200 - 300 lines of code.
As a rule of thumb, once a file draws near 400 lines, start considering how this code might start to exist in a segmented and modular form.
#### Less is more
When writing code; think about it simply and minimalistic way, an [atomic way](https://atomicdesign.bradfrost.com/chapter-2/).
Firstly understanding what we're trying to achieve. Ask as many questions you need to make sure this is clear.
* What are the minimal amount of things needed to be done to satsify this criteria?
* How do I break down my task into the smallest piece of code that can be added to the code base as soon as possible?
Are some great question to be asking before starting a task.
Once code is added it rarely ever goes away. Code is expensive and in the sense that the price we pay is maintenance, complexity, and size which makes it hard to justify going back to something to "clean it up". All the code we write should offer a high value to either the user or business.
The best code is no-code, because nothing can go wrong with it.
When in doubt, leave it out. (or ask someone ;p)
#### Type safety
We use types to ensure confidence in the code we're writing and to help make our code more managable and understandable.
That being said; avoid `any` where possible. If we find ourselves using `any`, consider whether a generic may be appropriate in our case.
For methods and properties that are part of a component's public API, all types must be explicitly specified- we use typescript as a way to make us move faster!
### 2. Folder Structure<a name="folder-structure"></a>
###### Why:
- Code should be organised in a modular and component-based structure.
- Keep components, styles, and tests in the same directory.
- `todo:` There some descripancies in file naming currently, we should _Prefer_ `PascalCase` to remain consistent with component declarations.
**_Example:_**
```
src/
|-- components/
| |-- Button/
| |-- Button.js
| |-- Button.module.css
| |-- Button.test.js
|
|-- pages/
| |-- HomePage/
| |-- HomePage.js
| |-- HomePage.module.css
| |-- HomePage.test.js
|
|-- utils/
| |-- helpers.js
| |-- api.js
|
...`
```
### 3. Code Formatting<a name="code-formatting"></a>
###### Why:
In order to make our code as maintainable as possible, it makes sense to format the code to be as conistent as possible.
We use [Prettier](https://prettier.io/) for code formatting and [ESLint](https://eslint.org/) for static analysis.
This is configured to run on a `pre-commit` git hook via [Husky](https://typicode.github.io/husky/) and [lint-staged](https://typicode.github.io/husky/)
Relevant rules can be found here:
```
/.prettier.config.js
/.lintstagedrc.js
/.eslintrc.json
```
#### Method Conventions
- Methods should be as generic as possible.
- The name of a method should capture the action that is performed _by_ that method rather than describing when the method will be called.
_Avoid_
```ts
activateWhizzer() {
// ...
}
```
_Prefer_
```ts
handleClick() {
// ...
}
```
### 4. Component Composition<a name="state-management"></a>
###### Why:
A big part of out codebase is heavily focused around [Component-based architecture](https://en.wikipedia.org/wiki/Component-based_software_engineering) which involves building complex systems by combining smaller, reusable units of functionality, known as components.
By focusing on creating reusable objects, we accelerate the development and deployment of an application while providing additional functionalities.
In a component-based architecture, components are the building blocks that are highly flexible, modular, and reused across several projects. They follow repeatable conventions and can be used across other interfaces and modules without compromising the integrity of our code or UX.
#### Granular vs. complex components
When designing components it's best to think about composing via [Atomic Design Methodology](https://atomicdesign.bradfrost.com/chapter-2/)
Simplisitcally we can think of the component we're building as being one of two things:
_Granular Components:_ Use granular components when we want to maximize reusability and maintainability of individual UI elements. This is especially useful for components that are used in multiple parts of the application.
_Complex Components:_ Use a complex component when we have a specific section of the UI that involves multiple elements working together and may have its own internal state and logic.
#### Single Responsibility Principle
- Each component should have a single responsibility.
- If a component does too many things, it becomes harder to maintain and reuse.
**_Example:_**
_Avoid_
```ts
const UserProfile = () => {
// Handles user profile display, editing, and validation
};
```
_Prefer_
```ts
const UserDisplay = () => {
// Displays user profile
};
const UserEditor = () => {
// Allows editing user profile
};
```
#### Destructuring
- Use destructuring to extract props in the function parameter.
- Use destructuring to extract propbs atz beginning of a functional component.
***Example:***
_Avoid_
```ts
// 1. It makes things slightly harder to comprehend.
const MyComponent = (props) => {
return <div>{`Name: ${props.name}, Age: ${prop.age}`}</div>;
};
// 2. A counter intuitive approach
const MyComponent = (props) => {
const someLogicalThing = () => {
//....
};
const { name, age } = props;
return <div>{`Name: ${name}, Age: ${age}`}</div>;
};
// 3. A counter intuitive approach
const MyComponent = (props) => {
const someLogicalThing = () => {
//....
};
const { name, age } = props;
return
<div>
<div>{`Start date: ${props.date.start}`}</div>
<div>{`Name: ${name}, Age: ${age}`}</div>;
<div>
};
```
_Prefer_
```ts
// Option 1: Destructuring in parameter
const MyComponent = ({ name, age }) => {
return <div>{`Name: ${name}, Age: ${age}`}</div>;
};
// Option 2: Destructuring at the beginning
const MyComponent = (props) => {
const { name, age } = props;
const date = props?.date ? null;
return
<div>
<div>{date ? `Start date: ${date}` : null}</div>
<div>{`Name: ${name}, Age: ${age}`}</div>;
</div>
};
```
#### Descriptive Prop Names
- Use descriptive names for props to make component usage more intuitive.
**_Example:_**
_Avoid_
```ts
<MyComponent n="John" a={30} />
```
Good
```ts
<MyComponent name="John" age={30} />
```
#### Spread Props
- When passing down props to child components, use the spread operator for cleaner code.
**_Example:_**
```ts
const ParentComponent = () => {
const propsToPass = { name: 'John', age: 30 };
return <ChildComponent {...propsToPass} />;
};
```
#### Hooks & Context > Prop Drilling
- Prop drilling can become more complex as the component tree grows.
- In such cases, it's important to consider alternative approaches like using the Context API or state management libraries to manage shared data more efficiently.
**_Example:_**
_Avoid_
```ts
const Child = ({ sharedData }) => {
return <div>{sharedData}</div>;
};
// The parent doesn't use the props,
// instead only serves as passing to an element lower in the tree
const Parent = ({ sharedData }) => {
return <Child sharedData={sharedData} />;
};
const Grandparent = () => {
const sharedData = 'Shared Data';
return <Parent sharedData={sharedData} />;
};
export default Grandparent;
```
_Prefer_
```ts
const Child = ({ sharedData }) => {
const { data } = useData();
return <div>{data}</div>;
};
const Parent = () => {
return <Child />;
};
const Grandparent = () => {
const { setData } = useData();
setData('OK');
return <Parent />;
};
export default Grandparent;
```
#### No Boolean arguments
- _Avoid_ adding boolean arguments to a method in cases where that argument means "do something extra".
- In these cases, _Prefer_ breaking the behavior up into different functions.
**_Example:_**
_Avoid_
```ts
function getTargetElement(createIfNotFound = false) {
// ...
}
```
_Prefer_
```ts
function getExistingTargetElement() {
// ...
}
function createTargetElement() {
// ...
}
```
#### Avoid Excessive Nesting
- _Avoid_ nesting components too deeply.
- If a component has too many children, consider breaking it down.
**_Example:_**
_Avoid_
```ts
<Header>
<Logo>
<Image />
</Logo>
</Header>
```
_Prefer_
```ts
<Header />
<Logo />
<Image />
```
#### Component Naming
- Use descriptive and meaningful names for components.
- Utilize PascalCase for component names.
**_Example:_**
_Avoid_
```ts
function user_profile() { ... }`
```
_Prefer_
```ts
function UserProfile() { ... }
```
### 5. Testing<a name="testing"></a>
###### Why:
We use [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/) and [Storybook](https://storybook.js.org/) for testing our code.
**_We aim to write meaningful tests that give us confidence that our code works reliably._**
Good tests encode the engineer's _intention_, they don't only lock in the test's behavior without editorialization of what's important and why.
#### Avoid Snapshot Testing
_Avoid_ using snapshot testing for React components. Snapshot tests can be brittle and may not provide meaningful coverage. This means they can quite quickly degrade in intended utility of integrated or functional tests which is: as the code changes make; sure nothing is broken.
Snapshots are tests we don't fully understand, so when they fail, we don't usually understand why or how to fix them. That means we have to do true/false negative analysis & then suffer indirection as we debug how to resolve the issues.
In their essence they are manually generated files. It's very easy to be undisciplined about scrutinizing generated files before committing them. If not at first, then definitely over time. Most engineers, upon seeing a snapshot test fail, will sooner just nuke the snapshot and record a fresh passing one instead of agonizing over what broke it.
False negatives quickly erode the team's trust in a test to actually find bugs and instead come to be seen as a chore on a checklist they need to satisfy before they can move on to the next thing.
As a result, if a snapshot test fails because some intended behavior disappeared, then there's little stated intention describing it and we'd much rather regenerate the file than spend a lot of time agonizing over how to get the same test green again.
#### Prefer Mocking External Dependencies
We use [MSW](https://mswjs.io/) to mock API calls. This is the most accurate way of simulating how the system works in production so it's best to focus our testing efforts in utlising this.
When testing components that rely on external dependencies (e.g., API calls), use mocking to control the behavior of those dependencies.
External dependencies may have unpredictable behavior, such as network latency or availability issues. By using mocks, we can control the behavior of these dependencies, making our tests more deterministic and reliable.
Mocks allow us to easily simulate edge cases or error scenarios that might be challenging or risky to trigger with real external services. This helps ensure that our code handles exceptional situations correctly.
In situations where the real external service is temporarily unavailable (e.g., maintenance, downtime, or network issues), we can continue to work using mocked data.
#### Declare main functionalities with testId
Focus on testing how users interact with our components, rather than specific implementation details or internal state.
**_Example:_**
_Avoid_
```ts
expect(getByText('Submit').tagName)...
```
_Prefer_
```ts
expect(screen.getByTestId('submit-button'))...
```
#### Test Behavior, Not Implementation Details
Focus on testing how users interact with our components, rather than specific implementation details or internal state.
**_Example:_**
_Avoid_
```ts
expect(getByText('Submit').tagName).toBe('BUTTON');
```
_Prefer_
```ts
expect(screen.getByTestId('submit-button')).toBeInTheDocument();
```
#### Clear and Descriptive Test Cases
Write test case names that clearly describe what is being tested. This makes it easier for others (and our future selves) to understand the purpose of the test.
***Example:***
_Avoid_
```ts
test('it works', () => {
// ...
});
```
_Prefer_
```ts
test('it renders a submit button', () => {
// ...
});
```
#### Integration Tests for Complex Components
For complex components, consider writing integration tests that cover the interaction between multiple components. This ensures that they work together as expected.
* [Integration Testing](https://css-tricks.com/react-integration-testing-greater-coverage-fewer-tests/#aa-option-2-integration-tests)
* [Testing React](https://www.appsierra.com/blog/react-integration-testing)
* [Interaction tests with Storybook](https://storybook.js.org/docs/react/writing-tests/interaction-testing)
#### Component Stories
Stories exist to demonstrate the component in various states and configurations to ensure the component behaves as expected under different conditions.
- Each component should have a corresponding story.
- Every component in the codebase should have at least one story to showcase its functionality.
### 6. Documentation<a name="documentation"></a>
###### Why:
It's useful that all our code is covered with documentation. We use the [TS Doc](https://tsdoc.org/) format to document our typescript code.
Writing documentation helps us to share knowlege with each other about why a specific piece of code exists, why it's been added and insight to thought process and dicusssions that happened while creating or using it.
Sometimes it's useful to write the documentation before we write any code to try and iron out any inconsistencies of our thought proces. It can also act as a place to sketch potential API design and ensure we are on the right track in completing our task.
- Provide clear and concise comments for complex code sections.
- Document API endpoints, function signatures, and component props.
**_Example:_**
```ts
export class Statistics {
/**
* Returns the average of two numbers.
*
* @remarks
* This method is part of the {@link core-library#Statistics | Statistics subsystem}.
*
* @param x - The first input number
* @param y - The second input number
* @returns The arithmetic mean of `x` and `y`
*
* @beta
*/
public static getAverage(x: number, y: number): number {
return (x + y) / 2.0;
}
}
```
Methods blocks should describe what the function does and provide a description for each parameter and the return value:
```ts
/**
* Opens a modal dialog containing the given component.
* @param component Type of the component to load into the dialog.
* @param config Dialog configuration options.
* @returns JSX.Element
*/
open<T>(component: Component<T>, config?: DialogConfig): DialogRef<T> { ... }
```
### Comments
- Comments that explain what some block of code does are nice; act as an overview, telling us something in less time than it would take to follow through the code itself.
- They also serve as a prompt to remind mind us what we were trying to achieve last week ;p
- It isn't always nessecary to write comments, but if the code is particular complex, use comments as a narrative to guide the reader through the complexities.
When writing comments using the follow syntax:
- Typescript: `/** */`
- CSS: `//`
- HTML: `<!-- ... -->` comments
_Avoid_:
```ts
// Set default tabindex.
if (!tab['index']) {
tab['index'] = -1;
}
```
_Prefer_:
```ts
/**
* To exit the loop we have to set the tab index to -1
* this creates the condition for the {@link WizzyThing} to reset the index to 0 after it does something..
* I couldn't think of a better way to do it at the time :)
*/
if (!tab['index']) {
tab['index'] = -1;
}
```
### 7. Code Review<a name="code-review"></a>
###### Why:
The primary goal of a code review is to provide constructive feedback to improve the code. They also act as a great place to share knowlege amoungst the team.
Most importantly they ensure that the codebase maintains a high level of quality, readability, and maintainability which are outlined in this document.
When our code is being reviwed don't feel we need to complete all the feedback. It's not realistic to think that we need to change everything- but attempt to make the most high impact changes if we have the resources to do that.
It can also be that issues raised are out of scope for the work, in that case let the reviewer know and ensure the work is documented in a new ticket.
**_Example:_**
_Be Specific_ - Clearly state what needs to be improved or changed.
_Explain the Why_ - If suggesting changes, explain the reasoning behind them.
_Be Respectful_ - Use a respectful and professional tone. Remember, the goal is to improve the code, not criticize the author.
_This can be achieved by asking questions rather than making accusations about approaches or the relevant code._
#### Areas of focus
##### High-Impact Issues
- _Prioritize:_ Focus on critical issues, potential bugs, security concerns, and areas with high business impact.
##### Clarity & Readability
- _Code Readability:_ Ensure the code is clear, well-organized, and follows established conventions.
- _Comments and Documentation:_ Check for adequate comments and documentation to aid understanding.
##### Functional & Non-Functional Aspects
- _Functionality:_ Verify that the code meets functional requirements and specifications.
- _Performance and Scalability:_ Consider non-functional aspects like performance and scalability, if relevant.
##### Testing & Test Coverage
- _Test Cases:_ Ensure that the code is adequately covered by unit tests.
- _Edge Cases:_ Verify that edge cases and boundary conditions are handled appropriately.
##### Security & Compliance
- _Security Concerns:_ Look for potential security vulnerabilities and ensure best practices are followed. More infomation can be found in the [Bluecrest SDLC](https://bluecresths.atlassian.net/wiki/spaces/ISG/pages/3526950913/SDLC+-+Security+Considerations)
##### Continuous Improvement
- _Learn from Code Reviews:_ Use code reviews as a learning opportunity for all team members.
- _Iterate on Guidelines:_ Regularly review and update code review guidelines to reflect evolving best practices in this document!
### 8. Error Handling <a name="error-handling"></a>
###### Why:
We try to adpot a [_"Fail Fast"_](https://www.martinfowler.com/ieeeSoftware/failFast.pdf) approach which helps to identify and address issues as close to their origin as possible. This minimizes the potential for downstream consequences and makes debugging much easier.
Remember, "Fail Fast" doesn't mean "Fail Silently." It's about detecting and handling errors promptly, not ignoring them.
Errors degrade a users experience and if not dealt with accordingly can affect the businesses reputation and revenue.
We use tools such as [Sentry](https://bluecrest-health.sentry.io/issues/?project=4505743186526208&statsPeriod=14d) for capturing errors and making them observable so they can be dealt with as soon as possible. Ensure we are writing code to deal with errors and edge cases and make sure that they are handled.
***Example:***
_Avoid_
```ts
console.error(error)
```
_Prefer_
```ts
Sentry.captureException(error)
```
#### Error Boundaries
By default, if our application throws an error during rendering, React will remove its UI from the screen. To prevent this, we can wrap a part of our UI into an error boundary. An [error boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) is a special component that lets we display some fallback UI instead of the part that crashed- for example, an error message.
***Example:***
```ts
// If an error is thrown in the <Profile/> component
// Ensure there is a fallback to cover this case.
<ErrorBoundary fallback={<p>Something went wrong</p>}>
<Profile />
</ErrorBoundary>
```
#### Try-Catch
- _Avoid_ `try-catch` blocks, instead attempt to prevent an error from being thrown in the first place.
- When that's not possible, the `try-catch` block must include a comment that explains the specific error being caught and why it cannot be prevented.
### 9. Styling<a name="styling"></a>
###### Why:
Maintaining and ensuring styles stay managable can be one of the trickiest aspects of maintaining a scalable codebase.
We are using [Tailwind CSS](https://tailwindcss.com/) and [CSS Modules](https://github.com/css-modules/css-modules) for managing the styling of components and ultisie Tailwind [@apply](https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply) directive to compose styles from a [Atomic CSS](https://css-tricks.com/lets-define-exactly-atomic-css/) perspective.
#### Design System
We maintain several publicly accessible Storybook builds for relevant components used within each of our products:
* [Shared UI Storybook](https://v2022-emerald-shared-ui.bluecrestwellness.com)
* [My Wellness UI Storybook](https://v2022-emerald-my-wellness-ui.bluecrestwellness.com)
* [Marketing Site UI Storybook](https://v2022-emerald-marketing-site-ui.bluecrestwellness.com)
* [Checkout UI Storybook](https://v2022-emerald-checkout-ui.bluecrestwellness.com)
#### Composing Styles
- Avoid using inline styles for elements and prefer defining CSS module classes.
_Avoid_
```jsx
<div className="flex flex-1 bg-red-300" />
```
_Prefer_
```css
// whizzy-ting.modules.css
.whizzyThing {
@apply flex flex-1 bg-red-300
}
// whizzy-ting.tsx
<div className={styles["whizzyThing"]} />
```
#### Mobile First
Though as a company we may not focus "Mobile first" as engineers it is something that we should practice.
When designing visual elements- attempt to first think about how it will look on a small sized screen.
Using this limitation alone will bring forward many design issues and eliminate many potential ways of designing the code.
- [Mobile First Design](https://www.wix.com/blog/mobile-first-design)
- [The Rationale Behind Mobile First Design](https://subscription.packtpub.com/book/mobile/9781786465658/11/ch11lvl1sec67/the-rationale-behind-mobile-first-design)
#### Use lowest specificity possible
Always prioritize lower specificity over other factors. Most style definitions should consist of a single element or css selector plus necessary state modifiers.
```css
.calendar {
display: block;
}
.calendar-month {
display: inline-block;
}
.calendar-date.selected {
font-weight: bold;
}
```
#### Support styles for Windows high-contrast mode
This is a low-effort task that makes a big difference for low-vision users. Example:
```css
@media screen and (-ms-high-contrast: active) {
.unicorn-motocycle {
border: 1px solid #fff !important;
}
}
```
#### Explain what CSS classes are for
When it is not super obvious, include a brief description of what a class represents. For example:
```css
// The calendar icon button used to open the calendar pane.
.datepicker-button {
...;
}
// Floating pane that contains the calendar at the bottom of the input.
.datepicker-calendar-pane {
...;
}
// Portion of the floating panel that sits, invisibly, on top of the input.
.datepicker-input-mask {
}
```
---
Note: These guidelines serve as a foundation and can be adapted based on specific project requirements and team preferences. Consistency and clear communication are key to maintaining a healthy codebase