Building an application or package individually is very easy. However, when we do the same thing with a team, it is much more complicated. The difficulty mostly comes from the difference in coding style, local system discrepancy, and the gap in deployment knowledge. Therefore, in a team, it is recommended to agree on the same standard and pipeline, then strictly follow it.

This article summarises my experience to construct a standard for my team, ranging from local development to remote deployment. The article is more targeting frontend development, however, I think backend also can benefit some of the concepts.

Local Development

Code Linting:

I really like the idea of code linting and the benefits it gives to our code standard. It helps to make our code more beautiful and ensure high maintainability as well. If you are writing JS/TS, Eslint is the best in doing this. When integrating into IDE, it also provides auto lint features. An example of eslint config can be found here:

module.exports = {
  root: true,
  extends: ['eslint:recommended'],
  rules: {
    'no-unused-vars': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    'react/jsx-no-bind': 'off',
    '@typescript-eslint/ban-ts-ignore': 'off',
    'no-undefined': 'off',
    '@typescript-eslint/member-naming': ['error', { private: '^\\w' }],
    'max-lines-per-function': 'off',
    '@typescript-eslint/member-ordering': ['warn', { classes: [] }],
  },
};

We can also choose to alter some of its predefined rules, by turning them off, or modify their rules.

With the same idea, there are also some other linting like stylelint for CSS, commitlint for git commit. An example can be found here:

{
  "extends": "stylelint-config-standard",
  "plugins": [
    "stylelint-scss"
  ],
  "rules": {
    "at-rule-no-unknown": null,
    "scss/at-rule-no-unknown": true
  }
}
const {
  utils: { getPackages },
} = require('@commitlint/config-lerna-scopes');

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'scope-enum': async ctx => [2, 'always', [...(await getPackages(ctx)), '*']], // scope list: lerna + *
    'scope-empty': [2, 'never'], // not allow empty scope
  },
};

In my project, I was using Lerna as monorepo framework, which is a popular repo setting nowadays. I will have another article to explain more about this. In short, multiple packages will lie on the same repo. Therefore, I want to enforce scoping in git commit, to precisely let teammates know which package this commit goes to. In the above example, empty scope or wrong scope is not allowed. We also use (*) to mark commit belongs to multiple packages or root settings.

Pre-commit Git hook:

However, sometimes the developer may forget to lint before committing their code to the remote repo. Most of the time, it is hard to recognize a linting error on the remote code review stage, therefore people only will recognize this after the code has been merged. This would bring bothersomeness to the other teammates when pulling the code.

In order to avoid that, we can implement a pre-commit git hook which will help to check before pushing code to remote. One choice we can use is husky. We can also use a husky hook to run the unit tests before pushing code. Therefore, we only allow developers to push the code if it doesn’t break the existing unit tests. An example can be found here

  "husky": {
    "hooks": {
      "pre-commit": "CI=true yarn test && lint-staged",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },

Git commit message

Of course, each team can have the own git commit message template. If you didn’t come up with a good solution yet, then I want to recommend the angular template.

Demo page (for front-end)

This section is quite specific to people who are building a UI library for frontend developers. You can skip this section if it is not applicable to your interest.

When you build a UI library, you need to have some demo page/documentation for other developers to experience your product, so that they will know in advance if the library is compatible with their needs.

I suggest 2 common tools nowadays in the market, which are styleguidist and storybook.

For a better and detailed analysis of the two, I highly recommend this article by Dominic Nguyen. The link is here:. In this article, I will just only briefly compare the two.

Styleguidist

Styleguidst uses markdown format to write the documentation for UI components. The good thing about styleguidist is it provides the code sandbox on the page. This would mean that the user can change the code directly and the UI will change on runtime. This would help for experience customers who prefer to alter the code on runtime to experience every aspect of UI.

Example: https://react-styleguidist.js.org/examples/basic/#button

As you can see, we can modify directly the code and the UI will change on runtime.

However, the downside of styleguidist is because of its markdown format (.md). Therefore, when writing demo components, I find it quite difficult to lint the code, because there is no mature markdown lint yet. Another downside is its basic design and predefined tools are not mature and fancy like storybook.

Storybook

Storybook allows developers to write the demo components in JS/TS format. Therefore, we can reuse our eslint setting. Storybook also more mature and well-supported by more brilliant developers around the world, therefore, its ecosystem has a lot of good plugins. The storybook demo page also has a nicer design (based on my preference).

Example: https://storybook.js.org/docs/react/get-started/examples

A lot of built-in plugins, including dark mode, background change, CSS pattern set, desktop/mobile screen, … which will shorten development time by a lot.

The only downside is it does not provide a code sandbox on runtime like styleguidist.

Remote pipeline

After the code is pushed to the remote repo, there should also some remote pipeline to check the code quality. Due to system differences, some chunks of code only work on the local machine of the developer. Therefore, it is needed to recheck on the remote machine to make sure all parts of the code are verified. I would recommend some remote stage on the GitLab pipeline like below

On any personal development branch:

  • install: check if all dependencies are valid
  • build: check if the code can build successfully
  • test: run all unit test ( or only some specific test related to this commit)

On merge to master:

Usually, when the code is merged to the master branch, the code will be deployed. In order to automatic the deployment, I highly recommend semantic-release. I will have another article to describe this in more detail. Therefore, we can add more stages here for remote pipeline deployment.

(To be continued)

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *