Skip to content

Development Practices

Tony Dong edited this page Jan 4, 2020 · 8 revisions

General

Modularise functions

Modularise functions. If your file or function is getting too big, break it up <3

Keep branches short and focused. If refactor work is needed, finish that off in a another branch first. Makes code reviews less painful, get things merged in faster.

Branch Names

Follow branch name convention:

<module>/#<ticket-number>/description

e.g. frontend/#69/adding-cool-box

No spaces - use dashes. No capitalisation.

React

File structure

Note we have components and view as separate folders. Components are for reusable components, view is for describing the actual page layout.

> api
> component
> types
> view
    > Header
    > Timetable
        - Timetable.tsx
    - App.tsx
    - App.test.tsx
> utils
    - colorMapper.ts
    - colorMapper.test.ts
- index.ts
 

Component structure

import React, { FunctionComponent } from 'react'
 
interface MyComponentProps {
  name: string
  meow(): void
  meowName(name: string): string
} // always have object as react prop. Type using interfaces
 
const MyComponent: FunctionComponent<MyComponentProps> = ({ name, meow, meowName }) => { // destructure here
    return <div />  
}
 
export { MyComponent } // named exports and named imports
 

No arrow functions in jsx

The view (jsx part) shouldn't contain logic. Keep them separate for readbility.

GOOD:
const MyComponent = () => {
    const handleClick = () => {
        console.log()
    }
 
    return <button onClick={handleClick} />  
}
 
BAD:
const MyComponent = () => {
    return <button onClick={() => console.log()} />
}
 

Typescript

Use interface to define objects. Use types for everything else. Interfaces has extra functionality such as (extends) which helps with objects.

interfaces
GOOD:
interface TimePeriod {
    start: Date
    end: Date
}
 
BAD:
type TimePeriod = {
    start: Date
    end: Date
}
 
// types are good for more complex typescript
// type unions, type intersections, conditional, generic types
type ClassData = NormalClassData | SpecialClassData

Interface Function Signatures

Prefer non-arrow function signatures for interfaces (for syntax formatting & slightly nicer readibility)

GOOD:
interface MyInterface {
   name: string
   addNumbers(num1: number, num2: number): number
}

BAD:
interface MyInterface {
   name: string
   addNumbers: (num1: number, num2: number) => number
}

Explicit Typing

It's okay to not explicitly type variables/objects if it is obvious what type they are. If it's not obviously clear, then you should be typing them explicitly:

const cat = (): string | number => {...}
 
const dog: string | number = cat()
 

Arrow functions ONLY (unless you have to use this)

GOOD:

const foo = (): bar => {...}

BAD:

function foo() {...}

Function args as objects (named parameters)

This is to enforce that the arguments you pass into a function have the exact same names. This is to prevent accidentally mis-ordering your arguments.

GOOD:

const foo = ({arg1, arg2, arg3}: {arg1: string, arg2: string, arg3: boolean}): string => {...}

or if you want it to be more readable

interface fooParams {
    arg1: string,
    arg2: string,
    arg3: boolean
}
const foo = ({arg1, arg2, arg3}: fooParams): string => {...}
 

BAD:

const foo = (arg1: string, arg2: string, arg3: boolean): string => {...}

Functional Practices

Aim for functional practices since its cleaner

use map, filter, some, every etc. over for loops

_.chain is neat, allows you to chain multiple lodash functions together

Aim for pure functions, no mutations and should only depend on parameters

If it gets overly complex with functional style, feel free to fall back to cleanest solution