A guide to automating development processes so you can spend more time on your code

A guide to automating development processes so you can spend more time on your code

Hello folks,

Setting up automated development processes are a great way to reduce errors, save time and make developers happier. In this article I'm going to introduce you to some of my favourite automations, how they work and how to set them up. Let's start with a deep dive into the why, the how and the what, but first, there is one simple statement that summarizes exactly why automating mundane, tedious technical tasks is critical:

You are dealing with humans, they will make mistakes.

bender.png

Why

  • Greatly reduces the error percentage by having almost all the work done by a process with strict rules.
  • Makes your developers happier by saving development time in their every day tasks and removes the burden of wasting their energy on repetitively boring tasks.
  • Enforce the usage of guidelines you might have by converting them to processes.

What

Automate as much as processly possible. I will list a few processes which you will have a quick overview as to how you can implement them in your own project. This is definitely not an exhaustive list, but it does cover the bare minimum of what a project should automate. Ok, maybe it is a bit more than the bare minium.

  • Unified code commits for features
  • Building + running test on every pull requests
  • Commit convention
  • Automatic versioning
  • Automatic change-logs
  • Package publishing

How

The how will be explained using examples from my open-source project ss-search which uses TypeScript. Even though I'm navigating in the JavaScript world, most of the tools I will be using are language agnostic.

Unified code commits of features

If you're already using a monorepo you can mark a check on this one; If not keep on reading. I won't make a detailed case of whether or not you should be using a monorepo, I will simply layout undeniable advantages of using one.

  • Process automation is centralized in only one place, no need to duplicate your automation code for all of your projects.
  • Easier feature development for the one's which span on many different projects, and only a single pull request to review. No more forgetting to review the related pull request of a feature.
  • Easier management of dependencies between dependent projects. No more unsynchronized code base causing failures.
  • Gone is the need to pull 10 projects to be fully up to date.

Using lerna we are able to efficiently fetch all of our project dependencies and manage those projects. I will layout the most basic steps to get started with lerna.

  1. You need a folder containing all of your projects, which is usually named packages.
  2. Create a config file (lerna.json) specifying your packages directory:
    {
         "packages": [
         "packages/*"
      ],
    }
    
  3. Run lerna bootstrap which will link local packages together and install remaining package dependencies.

Building + running test on every pull requests

For my open source project I'm using travis, but there are many other alternatives. I will layout my full travis config and explain every important line along the way.

language: node_js
node_js:
  - 13

install:
  # installs the top level dependencies we need such as lerna
  - npm i
  # installs the dependencies of all of our packages
  - npx lerna bootstrap

script:
  # `lerna run` will run the command for all the projects containing a script corresponding in it's package.json
  #  lints the code and will commit modified files
  - npx lerna run lint
  # run the tests
  - npx lerna run test
  # run a production build
  - npx lerna run build

after_script:
  # runs a benchmark to check if there are any performance regressions
  - npm run benchmark --prefix packages/ss-search
  # generates change-log, update package version and publish them
  - npx lerna run semantic-release

Commit convention

In order to automate change-logs and versioning, having a commit convention is mandatory. I highly recommend you to use the well defined standard of conventional commits. I won't go into too much details concerning this convention, but I will show you a screenshot of what it looks like.

conventional-commit.png

Now that you have adopted this convention, you will want to make sure your developers are respecting it. In order to do that, I will be using husky to easily manage git hooks, commitizen to help you with respecting the convention and commitlint to actually enforce this convention.

Husky hooks

"husky": {
    "hooks": {
      // prompt an interactive cli when using git commit (see screenshot below)
      "prepare-commit-msg": "exec < /dev/tty && git cz --hook || true",
      // enforce the commit message respects the convention (when not using the cli)
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },

git-commit.png

commitizen & commitlint

Those are integrated in our git hooks, no need to configure anything else.

Automatic versioning, change-logs and package publishing

Using semantic-release we will be able to increment the version of the projects that were updated since the last release, generate the appropriate change logs for each projects and publish projects that require publishing. Yes, this package does all of that for us, with very little configuration needed. In your package.json of each project, you will add something similar to this:

"release": {
    "plugins": [
      // analyzes the commits to identify changes
      "@semantic-release/commit-analyzer",
      // generates the release notes
      "@semantic-release/release-notes-generator",
      // generates the change logs
      "@semantic-release/changelog",
      // publishes the package to npm
      "@semantic-release/npm",
      // creates a new release on github (only if using github)
      "@semantic-release/github",
      // commits the changelog file, package.json and package-lock.json to update the version
      [
        "@semantic-release/git",
        {
          "assets": [
            "CHANGELOG.md",
            "package.json",
            "package-lock.json",
          ],
          "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
        }
      ]
    ]
  }

Hopefully this article helped you gain a better understanding as to why, what and how you should automate your processes.