← Back to list

Code alone isn’t enough for production systems.

Writing code is one thing; maintaining it over time is another. In real-world projects, long-term quality depends not just on what you…

Gaurav Kumar · 2026-01-29 07:56 · 0 claps · 4.9 min read
#coding-best-practices #husky #commit-lint #eslint-configuration #prettier
Open on Medium ↗
Wiki topics: 💻 · Programming

Code alone isn’t enough for production systems.

Writing code is one thing; maintaining it over time is another. In real-world projects, long-term quality depends not just on what you write, but how you write it, commit it, and verify it before it ever reaches production.

This is where disciplined workflows come in. A well-maintained codebase usually enforces a few core practices:

  • Consistent code formatting
  • Clear and standardized commit messages
  • Automated checks before code is committed

These checks can include unit tests, build validations, linting rules, and formatting enforcement. The goal is simple: catch problems early, reduce review friction, and keep the codebase predictable as it grows.

In this article, we’ll explore how tools like Husky and Commitlint, combined with ESLint for code quality and Prettier for formatting, work together to enforce standards automatically — right at the developer’s keyboard, before code ever leaves your machine.

Husky

Husky has one simple job: run scripts at specific Git events like pre-commit, commit-msg, and pre-push.

In practice, this means that when you’re about to commit some changes, you can make sure the code follows certain rules before the commit is created. These rules could include running lint checks, verifying formatting, or even executing a small set of unit tests.

Setting up Husky

Installing Husky is straightforward. First, add it as a development dependency:

bun add -d husky

Then initialize Husky:

bunx husky init

This creates a .husky/ directory in your project.

Inside this folder, you can define Git hooks. For example, create a pre-commit file and add the commands you want to run before every commit:

bun run format

Now, whenever you try to commit code, Husky will automatically run these commands. If they fail, the commit won’t go through.

Commitlint

The internet is full of memes about bad commit messages — and most of us have written one. Huge changes, committed as “small changes”.

With AI generating code faster than ever, this problem is only getting worse. Vague commit messages make it harder to understand why a change was made and turn git log into a guessing game.

This is where Commitlint helps.

Commitlint validates commit messages against a set of rules. It doesn’t force long explanations, but it ensures commits follow a consistent structure, often based on conventions like Conventional Commits.

When paired with Husky, Commitlint runs automatically on the commit-msg hook, rejecting invalid commit messages before they ever reach the repository. The result is a cleaner, more readable, and more useful Git history.

Setting up commitlint.

bun add -d @commitlint/cli @commitlint/config-conventional

After installing, create a configuration file that tells Commitlint which rules to use:

// commitlint.config.js
export default { extends: ['@commitlint/config-conventional'] }; 

Eslint

ESLint was created to help developers follow best practices while writing JavaScript. As TypeScript grew in popularity, support for TypeScript was added through dedicated parsers and plugins, which is why ESLint is now commonly used in TypeScript projects as well.

ESLint helps maintain code quality by analyzing code for potential errors and poor patterns. It catches issues like unused variables, unsafe comparisons, missing dependencies, and inconsistent conventions before they turn into real bugs.

ESLint is rule-driven. Project-specific rules are usually defined in an eslint.config.js (or .eslintrc) file, allowing teams to tailor standards to their codebase.

By enforcing a shared set of rules, ESLint makes code more predictable, easier to review, and simpler to maintain — especially in larger or collaborative projects.

Prettier

Prettier focuses on one thing only: code formatting.

Unlike ESLint, Prettier doesn’t care about code correctness or best practices. Its job is to take your code and format it in a consistent way every single time — handling things like indentation, line length, quotes, trailing commas, and spacing.

This removes an entire class of problems from code reviews. No more debates about tabs vs spaces or where a line should break. Prettier makes those decisions for you and applies them automatically.

By enforcing a single formatting style across the codebase, Prettier keeps the code clean, readable, and consistent — regardless of who wrote it or which editor they use.

Setting up ESlint with Prettier

I use ESLint with Prettier most of the time. For this article i am sharing the config files used in nestjs project.

First, install the required dependencies:

bun add -d eslint typescript-eslint eslint-plugin-prettier prettier

Next, add the ESLint configuration file (eslint.config.mjs) with the TypeScript-aware setup shown above. This enables type-checked linting using your own tsconfig.json and enforces both code quality and formatting rules.

import tseslint from 'typescript-eslint';
import eslintPluginPrettier from 'eslint-plugin-prettier';

export default tseslint.config({
  ignores: ['.eslint.config.mjs'],
  languageOptions: {
    parser: tseslint.parser,
    parserOptions: {
      project: './tsconfig.json',
      sourceType: 'module',
    },
    globals: {
      node: true,
      jest: true,
    },
  },
  plugins: {
    '@typescript-eslint': tseslint.plugin,
    prettier: eslintPluginPrettier,
  },
  rules: {
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'error',
    '@typescript-eslint/array-type': 'error',
    '@typescript-eslint/await-thenable': 'error',
    '@typescript-eslint/ban-types': 'error',
    '@typescript-eslint/naming-convention': [
      'error',
      {
        selector: ['variable', 'function'],
        format: ['camelCase', 'UPPER_CASE'],
      },
    ],
    '@typescript-eslint/no-confusing-non-null-assertion': 'warn',
    '@typescript-eslint/no-confusing-void-expression': 'warn',
    '@typescript-eslint/no-for-in-array': 'error',
    '@typescript-eslint/no-unused-vars': 'error',
    'prettier/prettier': 'error',
  },
});

Prettier is configured separately using a .prettierrc file:

{
  "singleQuote": true,
  "trailingComma": "all"
}

With this setup:

  • ESLint handles code quality and TypeScript-specific rules
  • Prettier enforces consistent formatting
  • Formatting violations are surfaced as ESLint errors

To run linting, use:

bunx eslint .

This keeps formatting and code quality checks consistent across the codebase and ensures the same rules apply for every developer.

Lint Stagged

Running linting and formatting on the entire codebase before every commit can quickly become slow and frustrating, especially as a project grows. This is where lint-staged helps.

Lint-staged runs linters and formatters only on files that are staged for commit. Instead of checking everything, it focuses on the exact files you’re about to commit, keeping pre-commit checks fast and relevant.

Lint-staged is most commonly used together with Husky. Husky triggers the pre-commit hook, and lint-staged decides which files should be checked. If any of the checks fail, the commit is blocked.

This approach strikes a good balance: strict enforcement without slowing down the development workflow.

Setting up lint-staged

First, install lint-staged as a development dependency:

bun add -d lint-staged

Then add this to your package.json file

  "lint-staged": {
    "*.{ts}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md}": [
      "prettier --write"
    ]
  }

After that, add lint-staged to Husky pre-commit hook

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

pnpm lint-staged

Relevant packag.json scripts from the NestJS project

  "scripts": {
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"libs/**/*.ts\"",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "prepare": "husky install",
  },

Conclusion

Good production systems aren’t built by writing perfect code once — they’re built by making it hard to do the wrong thing repeatedly.

Tools like Husky, Commitlint, ESLint, Prettier, and lint-staged encode good engineering practices directly into the workflow. They catch issues early, reduce manual effort, and keep teams aligned.

More from me:

Building Hanma, the Shadcn for backend coding.

Stop uploading files to the server unnecessarily

Backend development: more than just CRUDs

A walk through arrays in JavaScript

Building Shadcn for the backend, an overview of Hanma

The problems I am facing while building Hanma

Understanding the conversions in JavaScript.

Functions are crazy in JavaScript

Backend development: More than endpoints

A deep dive into JavaScript Console


메타데이터
post_id
5fe7e485bd96
slug
code-alone-isnt-enough-for-production-systems-5fe7e485bd96
url
https://medium.com/@itstheanurag/code-alone-isnt-enough-for-production-systems-5fe7e485bd96
canonical_url
https://medium.com/@itstheanurag/code-alone-isnt-enough-for-production-systems-5fe7e485bd96
author_url
https://medium.com/@itstheanurag
status
ok
fetched_at
2026-07-17 02:32:58