• Skip to primary navigation
  • Skip to main content

Test-first

Master the art of Test-driven Development (TDD)

  • Home
  • Newsletter
  • Blog

How linting can set you apart from the competition

Shine a light

Finding a job as a junior developer can be tough. There are only a few open positions and thanks to coding bootcamps we now have a lot of competition.

What can we do?

One option is to hustle harder. Another option is be smarter than the others.

Today I want to show you how you can use linting to improve the code quality of your portfolio projects. Interestingly enough, hardly anybody does it.

Imagine you alarm rings in the morning. You slept terribly because of that important job interview today. It’s winter, and it’s still dark outside. You turn the light on, but the bedroom stays dark. A power outage! There is no light in the bathroom and no light near your wardrobe either.

Now imagine you had to prepare and dress up for today’s job interview in total darkness without any light. Would you feel confident enough to go to that interview? Could you be sure that you’re presenting yourself from your best side? Probably not.

Source: Random Overload

When it comes to our code we are facing a similar situation. Most of us did not shine a light on the code in order to check whether it is clean and readable.

Code is read much more often than it is written.

In a world of ever-changing requirements, we will almost certainly have to touch that code again. Therefore:

The simpler and cleaner the code, the better.

Everyone benefits from clean code. Changes are easier and faster to implement, end-users get features faster, your company saves money, and last but not least, your life as a developer will be so much more relaxed.

Being able to write clean code is an important skill, and the IT companies you really want to work for, look for that skill in their candidates (e.g. by scanning through your portfolio projects). So let’s shine a light.

Linting

A linter is a program that analyses the code without executing it in order to find potential issues. The process of running the linter regularly and fixing found issues is called linting or de-linting.

A linter can be configured to print a warning when the code is not written according to the agreed code conventions. A convention could be to prefer single quotes over double quotes.

Linting can also point out code that leads to common bugs. Using a function with a name that is not spelled like any defined function in the scope could be an indicator for a typo.

You can compare all these small code issues to real-life lint – small fibers or hair on your clothes.

Photo of a mechanical linter pulled over some cloth with a lot of lint
Source: amazon.com / Ninge

A lint remover can be used to remove the lint and clean the surface of the cloth.

Complexity

Most teams that use a linter on a daily basis stick to a pre-configured linting setup that catches basic issues like violations of code conventions.

In this article, I want to show you how you can use a linter to go beyond what most developers do. I want to show you a couple of linting rules that can help you uncover the worst parts of your code. Once we identified these code sections, we can try improving them.

If you can’t measure it, you can’t improve it.

— Peter Drucker

Code that violates code conventions is harder to read. A more serious problem however, is complex code.

We will use simple and common metrics to measure the complexity of your code.

Lines of Code per File

One of these metrics is Lines of Code (LoC) per File.

The more lines of code a file contains, the more complex the code.

Over the last 20 years, I’ve worked for dozens of companies and clients and I’ve seen the code of hundreds of projects. From time to time, I am confronted with files that contain 500 lines of code or more. Just a few weeks ago, I saw a React component with 3,000 lines of code!

It was really hard for me to understand the code. It violated several principles of good software engineering. The component did all kind of things. It was incomprehensible. It was too complex.

The god of clean code once said:

Functions and classes should be small. Very small.

— Uncle Bob

We can apply the same principle to any file that contains code. I recommend to not allow more than 100 lines of regular JavaScript code.

No more than 100 LoC per JavaScript file

Lines of Code per File is a simple metric, but it is not perfect. That’s why we use multiple metrics in conjunction.

Statements per Function

Another useful metric is Statements per Function.

Every action in our code is represented by a statement. The following code segments has 3 statements:

let answer = 41
answer++
console.log(answer)

I recommend to not allow more than 10 statements per function.

No more than 10 Statements per Function

A file with a function can exceed both, the maximum allowed amount lines per file, and the maximum allowed amount of statements per function.

Overview

So let’s shine some light onto our code and start improving it.

In the following sections we will:

  1. Prepare the project
  2. Setup the linter
  3. Fine-tune the linting rules
  4. Check the code for problems
  5. Improve the code

Preparing the Project

First, we need to select a project.

I recommend you chose one of your bigger projects. I will use one of Edith’s portfolio projects. Edith is one of my mentees and agreed to play the guinea pig.

By the way, Edith is located in Berlin and wants to take her full stack career to the next level. Send me a message and I will get you in touch.

Edith’s project is a React project. The lessons from this article can of course be applied to any project. We will use ESLint in this article which is currently the most popular linter for all things JavaScript.

There is a linter for most languages. If your project is written in Java for instance, you might want consider SonarQube.

Before we start, we should check that everything works:

1. Verify app works as expected

Let’s run our test suite and verify that the app works as expected.

If you don’t have a suite of automated tests, start your app and test it manually.

Automated tests and test-driven development are beyond the scope of this article, but I will cover it in one of my upcoming newsletters.

2. Commit all changes

Now, let’s make sure that there are no uncommitted changes.

Once everything works as expected, we should commit all our changes. Should something go wrong, we are able to revert everything back to this state.

3. Verify app works after re-installing

Finally, let’s perform another check to see if our project is working.

Once, everything is committed, let’s pretend that we just cloned the project from GitHub. We can do that by deleting the node_modules folder and re-installing all dependencies:

rm -rf node_modules
npm install

Make sure to execute these terminal commands in the root folder of your project.

Let’s verify again that the app works as expected.

We are now ready to set up the linter.

If something goes wrong during that process, we can easily go back to this state by:

  1. Reverting all changes (e.g. with git checkout .)
  2. Deleting node_modules (rm -rf node_modules)
  3. Re-installing all dependencies (npm i)

Setting up the Linter

Setting up ESLint is usually straight forward. There are some differences depending on the type of project we have in front of us.

1. Check if ESLint is present

Some projects have ESLint already installed. If you have created your app with create-react-app you will have ESLint installed almost certainly. Let’s check our project’s node_modules folder for the eslint dependency:

ls node_modules/eslint

If you see something like ls: node_modules/eslint: No such file or directory it means ESLint is not installed. If you see something like CHANGELOG.md README.md conf ..., ESLint is already present in your project.

2. (optional) Install ESLint as dev dependency

Obviously, we would only do that if ESLint is not already present in our project.

npm install --save-dev eslint

3. Add some NPM scripts

This will allow us to run the linter more easily. We open the project’s package.json and add the follow linter scripts to the scripts section:

{
  "...": "...",
  "scripts": {
    "...": "...",
    "lint": "eslint --ext=js,jsx --ignore-path=.gitignore --max-warnings=0 --color . || true"
  }
}

If you’re working on a React app that was created with create-react-app, you need to set the environment variable EXTEND_ESLINT to true (you can tell by whether there is an NPM script "eject": "react-scripts eject"):

{
  "...": "...",
  "scripts": {
    "...": "...",
    "lint": "EXTEND_ESLINT=true eslint --ext=js,jsx --ignore-path=.gitignore --max-warnings=0 --color . || true"
  }
}

And if you’re working on a Windows machine, you should use the following configuration:

{
  "...": "...",
  "scripts": {
    "...": "...",
    "lint": "set EXTEND_ESLINT=true && eslint --ext=js,jsx --ignore-path=.gitignore --max-warnings=0 --color ."
  }
}

Make sure you have a .gitignore file in your project and that it mentions node_modules.

4. Run ESLint

Now it’s time to run the linter for the very first time.

On the terminal execute:

npm run lint

In case of Edith’s project, we get 9 linting issues:

> portfolio@1.0.0 lint ./portfolio
> EXTEND_ESLINT=true eslint --ext=js,jsx --ignore-path=.gitignore --max-warnings=0 --color . || true

Warning: React version not specified in eslint-plugin-react settings. See https://github.com/yannickcr/eslint-plugin-react#configuration .

...

./portfolio/src/projectthree.js
 21:36  error  `'` can be escaped with `'`, `‘`, `'`, `’`  react/no-unescaped-entities

./portfolio/src/projecttwo.js
 20:36  error  `'` can be escaped with `'`, `‘`, `'`, `’`  react/no-unescaped-entities

✕ 9 problems (9 errors, 0 warnings)

9 problems. That’s actually not too bad. Most of the above problems can be fixed by replacing the single quotes ' in the listed JSX files with the save HTML escape code '.

It is common to get hundreds or even thousands of linting issues when setting up ESLint. Often, it is because ESLint tries to lint files that are not supposed to be linted or the configuration of the linting rules does not match the code conventions.

Fine-tuning the Linting Rules

Now that ESLint is set up and working, let’s fine-tune the linting rules. In order to do that, we need an ESLint configuration file.

1. Check ESLint configuration is present

First, let’s check if there is already an ESLint configuration file present in the project.

In the root directory of the project look for one of the following files:

  • .eslintrc.yml
  • .eslintrc.js
  • .eslintrc.json

Another possible location for the config is package.json. Look for an entry called:

  • "eslintConfig": {

If you cannot find any ESLint configuration, let’s create one.

2. (optional) Create ESLint config

ESLint comes with a small Q&A-based helper tool to generate an initial ESLint configuration file. Let’s execute it with:

npx eslint --init

Depending on the version of ESLint, the questions and answers will vary a bit. Answer them based on what you know about your project. Don’t worry, if you got something wrong. You can abort and restart, or fix the mistake later in the config file itself.

Here is the Q&A protocol of the current ESLint version for a React app, created with create-react-app:

✓ How would you like to use ESLint? · problems
✓ What type of modules does your project use? · esm
✓ Which framework does your project use? · react
✓ Does your project use TypeScript? · No / Yes
✓ Where does your code run? · browser, node
✓ What format do you want your config file to be in? · YAML

When being asked to install missing dependencies such as the eslint-plugin-react, do so unless you are working on a React app, created with create-react-app. In the later case skip that step.

Here is the Q&A protocol of a slightly older version of ESLint for a React app, created without create-react-app:

? How would you like to configure ESLint? Answer questions about your style
? Are you using ECMAScript 6 features? Yes
? Are you using ES6 modules? Yes
? Where will your code run? Browser, Node
? Do you use CommonJS? No
? Do you use JSX? Yes
? Do you use React? Yes
? What style of indentation do you use? Spaces
? What quotes do you use for strings? Single
? What line endings do you use? Unix
? Do you require semicolons? No
? What format do you want your config file to be in? YAML

I prefer YAML over JSON when it comes to config files because the syntax is shorter and more readable and YAML supports comments.

Here is a configuration file for a React app, created with create-react-app:

env:
  browser: true
  es6: true
  node: true
extends:
  - react-app
  - eslint:recommended
  - plugin:react/recommended
globals:
  Atomics: readonly
  SharedArrayBuffer: readonly
parserOptions:
  ecmaFeatures:
    jsx: true
  ecmaVersion: 2018
  sourceType: module
plugins:
  - react
rules: {}

Make sure that the first entry of extends (line 6) is - react-app. Add it if necessary and remember:

Whitespace for indentation in YAML files matters!

Here is another configuration file for a React app, created without create-react-app:

env:
 browser: true
 es6: true
 node: true
extends:
 - eslint:recommended
 - plugin:react/recommended
globals:
 Atomics: readonly
 SharedArrayBuffer: readonly
parserOptions:
 ecmaFeatures:
   jsx: true
 ecmaVersion: 2018
 sourceType: module
plugins:
 - react   
settings:
 react:
   version: detect
rules: {}

3. Turn off rules temporarily

Let’s turn off the other rules that cause the current linting issues temporarily.

Today, we want to focus on a couple of linting rules that can help us reduce the complexity of our code.

And in my case I see errors unrelated to complexity, but related to the following two rules: no-unused-vars and react/no-unescaped-entities. So let’s turn these rules temporarily off by adding the following rule statements to .eslintrc.yml:

# ...
rules:
  no-unused-vars: off # TODO: remove this line before committing
  react/no-unescaped-entities: off # TODO: remove this line before committing

If your configuration is JSON-based, it will look more like this:

{
  "...": "...",
  "rules": {
    "no-unused-vars": "off",
    "react/no-unescaped-entities": "off"
  }
}

4. Run ESLint again

Finally, let’s run the linter again to see if everything is configured correctly.

npm run lint

We expect to see no linting issues for the time being.

Max Statements

One rule that can help us reduce the complexity of our code and thus make it more readable and maintainable is max-statements.

Let’s add a rule that limits the maximum amount of allowed statements in a function to 10:

# ...
rules:
  # ...
  max-statements: [ error, 10 ]

Or in JSON:

{
  "...": "...",
  "rules": {
    "...": "...",
    "max-statements": [ "error", 10 ]
  }
}

Executing the linter reveals one issue in Edith’s project:

./portfolio/src/cube.js
  11:22  error  Method 'componentDidMount' has too many statements (17). Maximum allowed is 10  max-statements

The problem is located in line 11 of the file cube.js:

// ...

export default class Cube extends React.Component {
  // ...

  componentDidMount() {
    // === THREE.JS CODE START ===
    var scene = new THREE.Scene()
    scene.background = new THREE.Color(0xFFFFFF)
    var camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000)
    var renderer = new THREE.WebGLRenderer()
    // renderer.setClearColor(0x000000, 0.5)
    renderer.setSize(700, 300)
    this.mount.appendChild(renderer.domElement)
    var geometry = new THREE.BoxGeometry(1, 1, 1)
    var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
    var cube = new THREE.Mesh(geometry, material)
    scene.add(cube)
    // cube.position.set(0, 0, 0)
    scene.add(cube)
    cube.scale.x = 2.5 // SCALE
    cube.scale.y = 2.5 // SCALE
    cube.scale.z = 2.5 // SCALE
    camera.position.z = 5
    var animate = function () {
      requestAnimationFrame(animate)
      cube.rotation.x += 0.01
      cube.rotation.y += 0.01
      renderer.render(scene, camera)
    }
    animate()
    // === THREE.JS EXAMPLE CODE END ===
  }

  render() {
    return <div className="cube" ref={ref => (this.mount = ref)} />
  }
}

How do you feel when you are looking at that code for the first time?

It is a bit overwhelming, isn’t it? It looks complicated, doesn’t it? We also say “it 💩 smells”!

Some developers might even get a bit scared – especially if their task involves changing that code.

Now imagine how a hiring tech lead feels like, when they see this kind of code in your portfolio projects.

The function componentDidMount violates the Small Function Principle.

The good news:

Thanks to linting, do we know now that this code needs improvement.

Complexity linting rules such as max-statements are like little 💩 detectors.

What does the above code actually do?

It uses the three.js library to create and render a 3D animation of a cube:

Let’s reduce the complexity of this code and make it more readable.

In our first iteration, let’s remove all unnecessary comments:

// ...

export default class Cube extends React.Component {
  // ...

  componentDidMount() {
    let scene = new THREE.Scene()
    scene.background = new THREE.Color(0xFFFFFF)
    let camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000)
    let renderer = new THREE.WebGLRenderer()
    renderer.setSize(700, 300)
    this.mount.appendChild(renderer.domElement)
    let geometry = new THREE.BoxGeometry(1, 1, 1)
    let material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
    let cube = new THREE.Mesh(geometry, material)
    scene.add(cube)
    scene.add(cube)
    cube.scale.x = 2.5
    cube.scale.y = 2.5
    cube.scale.z = 2.5
    camera.position.z = 5
    let animate = function () {
      requestAnimationFrame(animate)
      cube.rotation.x += 0.01
      cube.rotation.y += 0.01
      renderer.render(scene, camera)
    }
    animate()
  }

  render() {
    return <div className="cube" ref={ref => (this.mount = ref)} />
  }
}

I also replaced var with the more secure let.

In general, the majority of comments are not necessary if we write clean code. Good variable and function names as well as short functions and classes usually provide enough context to understand what is happening.

Commenting out code and not deleting it is also a bad practice.

But what if we need that code later on?

We use git version control and are able to retrieve any deleted code at any time. No need to leave commented out code in our files.

It looks like the first half of the function deals with setting up a scene, a camera, a renderer and a cube. Let’s extract the creation of these entities into helper functions:

// ...

const createScene = () => {
  let scene = new THREE.Scene()
  scene.background = new THREE.Color(0xFFFFFF)
  return scene
}

const createCamera = () => {
  let camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000)
  camera.position.z = 5
  return camera
}

const createRenderer = () => {
  let renderer = new THREE.WebGLRenderer()
  renderer.setSize(700, 300)
  return renderer
}

const createCube = () => {
  let geometry = new THREE.BoxGeometry(1, 1, 1)
  let material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
  let cube = new THREE.Mesh(geometry, material)
  cube.scale.x = 2.5
  cube.scale.y = 2.5
  cube.scale.z = 2.5
  return cube
}

export default class Cube extends React.Component {
  // ...

  componentDidMount() {
    const scene = createScene()
    const camera = createCamera()
    const renderer = createRenderer()
    const cube = createCube()
    scene.add(cube)
    scene.add(cube)
    this.mount.appendChild(renderer.domElement)
    let animate = function () {
      requestAnimationFrame(animate)
      cube.rotation.x += 0.01
      cube.rotation.y += 0.01
      renderer.render(scene, camera)
    }
    animate()
  }

  render() {
    return <div className="cube" ref={ref => (this.mount = ref)} />
  }
}

Let’s look at the code again. How do you feel now? Better?

I do feel better. The code is not perfect and there is more we could do, but reading the first few lines of componentDidMount is not so overwhelming anymore. It creates a scene, then a camera, the a create renderer, then a cube, then adds the cube to the scene, …

Even if I do not fully understand what a scene is or how it is created, I can get the idea of what’s happening here. If I need to know more, I can look into the corresponding function (e.g. createCube) and dig deeper into the details. On the higher level of componentDidMount it doesn’t matter that the camera’s z coordinate is set to 5.

The cognitive load onto my brain is lower now. I might even have some capacity left to question why the cube is added twice to the scene (it’s indeed a copy-paste artifact and not necessary).

Let’s run the linter again.

npm run lint

The problem is gone. We successfully reduced the complexity of the code. Our future self and other developers will be thankful.

Max Lines

Another rule that can help make our code cleaner is max-lines.

Let’s add a rule that limits the maximum amount of lines in a file to 100:

# ...
rules:
  # ...
  max-lines: [ error, 100 ]

Or in JSON:

{
  "...": "...",
  "rules": {
    "...": "...",
    "max-lines": [ "error", 100 ]
  }
}

JSX code is a bit different when compared to vanilla JavaScript code, and usually I allow a few more lines in React component files (up to 150). In one of my upcoming newsletters I will cover how to override rule configurations for different file types.

Executing the linter reveals another issue in the project:

./portfolio/src/otherprojects.js
  101:1  error  File has too many lines (117). Maximum allowed is 100  max-lines

This time the problem is located in file otherprojects.js.

import React from 'react'

export default class OtherProjects extends React.Component {
  // ...

  render() {
    return (
      <React.Fragment>
        <div className="op-btm">
          <img src="/raccoon.png" className="header-logo" alt="logo" width="45px" height="45px" background="none" />
          <a className="op-back " href="http://www.edith....com/" > back to main </a>
        </div>
        <div className="op-container">
          <div className="op-card">
            <img src="/connectfourDemo.gif" className="op-img" alt="logo" width="250px" height="150px" />
            <div className="op-text">
              <h1 className="op-title">connect 4</h1>
              <br></br>
              <h3 className="op-tech">technologies:</h3>
              <p className="op-tech-text">Javascript // HTML// CSS// JSON </p>
              <br></br>
              <h3 className="op-description">project's description:</h3>
              <p className="op-description-text"> sweet 2D connect four!</p>
              <br></br>
              <a className="op-demo" href="https://edith....github.io/connect-four/" rel="noopener noreferrer" target='_blank'>Demo</a>
              <br></br>
              <a className="op-github" href="https://github.com/Edith.../connect-four" rel="noopener noreferrer" target='_blank'>Github</a>
            </div>
          </div>
          <div className="op-card">
            <img src="/spotifyAPI.gif" className="op-img" alt="logo" width="250px" height="150px" />
            <div className="op-text">
              <h1 className="op-title" >spotify API</h1>
              <br></br>
              <h3 className="op-tech">technologies:</h3>
              <p className="op-tech-text">Javascript // HTML// CSS// JSON// Spotify API// proxy </p>
              <br></br>
              <h3 className="op-description">project's description:</h3>
              <p className="op-description-text"> search and find the artists you love!</p>
              <br></br>
              <a className="op-github" href=" https://github.com/Edith.../spotify-API" rel="noopener noreferrer" target='_blank'>Github</a>
            </div>
          </div>
          <div className="op-card">
            <img src="/TwickerDemo.gif" className="op-img" alt="logo" width="250px" height="150px" />
            <div className="op-text">
              <h1 className="op-title">twitter API</h1>
              <br></br>
              <h3 className="op-tech">technologies:</h3>
              <p className="op-tech-text">Javascript // HTML// CSS// JSON// Twitter API</p>
              <br></br>
              <h3 className="op-description">project's description:</h3>
              <p className="op-description-text"> keep track of the hottest news via ticker displaying twitter tweets!</p>
              <br></br>
              <a className="op-github" href="https://github.com/Edith.../Twicker" rel="noopener noreferrer" target='_blank'>Github</a>
            </div>
            {/* ... */}
          </div>
        </div>
        <p className="copyright-project">Copyright © 2020 Edith</p>
      </React.Fragment >
    )
  }
}

How do feel when looking at (what is actually only half of) the code?

It is possible to grasp what’s going on, but it is a lot of code.

Let’s do the Squint Test. What do you notice when squinting your eyes and looking at the code?

A repeating structure. Every project listed in this file is represented by what seems to be a card:

{/* ... */}
<div className="op-card">
  <img src="/spotifyAPI.gif" className="op-img" alt="logo" width="250px" height="150px" />
  <div className="op-text">
    <h1 className="op-title" >spotify API</h1>
    <br></br>
    <h3 className="op-tech">technologies:</h3>
    <p className="op-tech-text">Javascript // HTML// CSS// JSON// Spotify API// proxy </p>
    <br></br>
    <h3 className="op-description">project's description:</h3>
    <p className="op-description-text"> search and find the artists you love!</p>
    <br></br>
    <a className="op-github" href=" https://github.com/Edith.../spotify-API" rel="noopener noreferrer" target='_blank'>Github</a>
  </div>
</div>
{/* ... */}

Every card has the same structure:

  • image
  • title
  • technologies
  • description
  • links

Now imagine, we had to change the order of description and technologies on all cards. We would have to change that for every project. 7 times!

This markup violates the principle of DRY – Don’t Repeat Yourself.

The good news:

ESLint – the 💩 detector – helped us once again spotting the problem. Let’s fix it!

In the previous example we used sub functions to reduce the complexity and make the code more readable. When it comes to React components we can often use sub components to achieve a similar result.

Let’s create a helper component Project:

import React from "react"
import PropTypes from "prop-types"

const Project  = ({ title, imageUrl, technologies, description, links }) => (
  <div className="op-card">
    <img src={imageUrl} className="op-img" alt="logo" width="250px" height="150px" />
    <div className="op-text">
      <h1 className="op-title">{title}</h1>
      <br />
      <h3 className="op-tech">technologies:</h3>
      <p className="op-tech-text">{technologies.join(" // ")}</p>
      <br />
      <h3 className="op-description">project's description:</h3>
      <p className="op-description-text">{description}</p>
      {links.map(link => (
        <>
          <br />
          <a className="op-demo" href={link.url} rel="noopener noreferrer" target='_blank'>{link.title}</a>
        </>
      ))}
    </div>
  </div>
)

Project.propTypes = {
  title: PropTypes.string.isRequired,
  imageUrl: PropTypes.string.isRequired,
  technologies: PropTypes.arrayOf(PropTypes.string).isRequired,
  description: PropTypes.string.isRequired,
  links: PropTypes.arrayOf(PropTypes.shape({
    title: PropTypes.string.isRequired,
    url: PropTypes.string.isRequired,
  })).isRequired,
}

export default Project

The Project component is a stateless component, and we decided to use the shorter functional component style.

The property technologies is an array of strings and we join it in line 11 into a single string where all listed technologies are separated with a double slash //.

The property links is an array of small objects. Each object has a title and a url. We use the map function to convert each link into an <a> tag (prepended with a <br> tag).

A good practice is to provide a PropType definition (line 25) to describe the type of each property and whether a property is required or optional.

Now let’s use the new Project component in otherprojects.js:

import React from 'react'
import Project from './project'

export default class OtherProjects extends React.Component {
  // ...

  render() {
    return (
      <React.Fragment>
        <div className="op-btm">
          <img src="/raccoon.png" className="header-logo" alt="logo" width="45px" height="45px" background="none" />
          <a className="op-back " href="http://www.edith....com/" > back to main </a>
        </div>
        <div className="op-container">
          <Project
            title="connect 4"
            imageUrl="/connectfourDemo.gif"
            technologies={[ "Javascript", "HTML", "CSS", "JSON" ]}
            description="sweet 2D connect four!"
            links={[
              { title: "Demo", url: "https://edith....github.io/connect-four/" },
              { title: "Github", url: "https://github.com/edith.../connect-four" },
            ]}
          />
          <Project
            title="spotify API"
            imageUrl="/spotifyAPI.gif"
            technologies={[ "Javascript", "HTML", "CSS", "JSON", "Spotify API", "proxy" ]}
            description="search and find the artists you love!"
            links={[ { title: "Github", url: "https://github.com/edith.../spotify-API" } ]}
          />
          <Project
            title="twitter API"
            imageUrl="/TwickerDemo.gif"
            technologies={[ "Javascript", "HTML", "CSS", "JSON", "Twitter API" ]}
            description="keep track of the hottest news via ticker displaying twitter tweets!"
            links={[ { title: "Github", url: "https://github.com/edith.../Twicker" } ]}
          />
          {/* ... */}
        </div>
        <p className="copyright-project">Copyright © 2020 Edith</p>
      </React.Fragment >
    )
  }
}

Let’s look at otherprojects.js again. How do you feel now?

I feel that the intention of the file is more obvious. It contains a list of projects. And I can immediately grasp what a project is made of.

The code is again not perfect and there is more we could do, but it is more readable now.

Changing the order of description and technology is now a quick and easy task, performed by swapping a few lines in project.js. It will automatically be applied to all projects.

First make the change easy, then make the easy change.

— Kent Beck

Let’s run the linter again.

npm run lint

The problem is gone. We reduced the size of otherprojects.js from 117 to 75 lines of code.

Cleanup

It’s time to re-enable the temporarily disabled linting rules by deleting the lines we added earlier.

We should also commit our changes.

Summary

Here are the steps that brought us here today:

  1. Prepare project – First, we prepared the project by making sure that there are no uncommitted changes.
  2. Setup linter – Then, we installed ESLint and added convenient NPM scripts to execute it regularly.
  3. Fine-tune linting rules – Next, we fine-tuned the linting rules by adding rules that measure the complexity.
  4. Check code for problems – Then, we ran the linter in order to find problematic code.
  5. Improve code – And finally, we refactored the code in order to reduce its complexity.

The big takeaway is that static code analysis tools such as ESLint can be used to measure the complexity of code, and to enforce simpler and cleaner code in your projects.

Utilize static code analysis tools to enforce writing simpler code

I recommend continuing improving your project code and running the linter regularly. It’s a great opportunity to practice refactoring, and you can train the habit of writing clean code.

Your (future) employer will appreciate it.

And don’t forget to mention ESLint on your list of skills!

What’s next?

There is obviously much more to linting and static code analysis. We covered only two of hundreds of rules, and you might have further questions like:

  • Is there a way to automatically fix linting issues? (yes, there is)
  • How often should I run the linter?
  • How do I introduce linting in my team?
  • Can I automate running the linter? (yes, you can)
  • How do I exclude certain code sections from the linting process?
  • What to do when I don’t have time to fix hundreds of issues now? (there’s a technique called ratcheting)

I will cover answers to all these questions and much more in my Test-first newsletters. So if you liked this article and if you are interested in learning more about smart software engineering skills, feel free to sign up here.

See you around,
-David

Get David’s best advice on TDD straight to your inbox

Tap into a senior software developer’s brain with 25 years of industry-relevant experience that successfully uses Test-driven Development daily.

I’ll send you a few emails per month to keep you posted. Of course, you can opt out at any time.
By subscribing, you accept my privacy policy. I promise I won’t spam you or sell your data.

Connect

  • Email
  • LinkedIn
  • Phone
  • YouTube

Discover

www.cultivate.software

Legal

Imprint  ·  Privacy Policy  ·  Cookie Policy

Copyright © 2025 · cultivate GmbH