After a year in a new workplace I have noticed several development lifecycle tools that are missing from projects all over, some have it partially, some not at all.
This article covers the essential tools that will improve your team's development experience and code maintainability.
pnpm
We all know npm, this is the go-to, youtube tutorial, default package manager in the NodeJS ecosystem.
In every code boot-camp or school you will learn npm, as a derivative of that fact and the fact that the npmjs.com registry is the default registry for NodeJS packages we come to the result that the npm package manager is the most used package manager in the NodeJS ecosystem.
But is it the best?
It's a matter of opinion, but no, it's not.
There are several package managers in the NodeJS ecosystem, the most popular ones are yarn and pnpm.
Both of them are great, but I prefer pnpm for several reasons:
-
Performance:
pnpmis faster thannpmandyarnin most cases, this is becausepnpmuses a unique approach to store packages, it creates a single store for all packages and uses hard links to link the packages to the project, this way it avoids duplication of packages and saves disk space. -
Determinism:
pnpmis more deterministic thannpmandyarn, this means that the same set of dependencies will always result in the same set of installed packages, this is important for reproducible builds and for avoiding issues with dependency resolution. -
Monorepo support:
pnpmhas built-in support for monorepos, this means that you can manage multiple packages in a single repository with ease, this is important for large projects and for teams that work on multiple packages. -
Strictness:
pnpmis more strict thannpmandyarn, this means that it enforces stricter rules for dependency resolution and package installation, this helps to avoid issues with conflicting dependencies and ensures that the project is always in a good state.
For these reasons, I recommend using pnpm as the package manager for your NodeJS projects.
But again, it's a matter of opinion, you should try both and see which one works best for you and your team.
ESLint
ESLint is likely the most adopted tool on this list, so I'll keep this brief—if you're reading an article about project tooling, you probably already know what it does.
What's worth mentioning: ESLint 9 introduced flat config (eslint.config.js), which simplifies configuration significantly. If you're still using .eslintrc, consider migrating. The official documentation covers the migration path.
Prettier
Prettier eliminates formatting debates—tabs vs spaces, semicolons, quote style—by enforcing a consistent style automatically.
Configure it with a .prettierrc file and integrate it with ESLint using eslint-config-prettier to avoid rule conflicts.
cspell
Typos are the enemy of code quality—they make code harder to read and search for.
cspell is a spell checker designed for code. Unlike regular spell checkers, it understands camelCase, PascalCase, and snake_case, so it won't flag getUserById as a typo.
Configure it with a cspell.json file:
{
"version": "0.2",
"language": "en",
"words": ["myproject", "signup", "authtoken"],
"ignorePaths": ["node_modules", "dist", "coverage", "pnpm-lock.yaml"]
}
Add project-specific terms to the words array. VS Code users can install the Code Spell Checker extension for real-time feedback.
knip
Dead code is a silent burden—it increases bundle size, confuses developers, and makes refactoring harder.
knip finds unused files, dependencies, and exports across your entire project—not just unused variables within a file. It detects:
- Unused files not imported anywhere
- Unused dependencies in
package.json - Unused exports never imported elsewhere
- Unlisted dependencies used but not declared
Configure it with a knip.json file:
{
"entry": ["src/index.ts", "src/main.ts"],
"project": ["src/**/*.ts"],
"ignore": ["**/*.test.ts", "**/*.spec.ts"],
"ignoreDependencies": ["@types/*"]
}
Running knip regularly keeps your codebase lean and makes refactoring easier.
Husky
Now that we have all these quality tools, we need a way to enforce them on every commit and push. Git hooks can do this, but they're local to your machine—not shareable with the team.
Husky solves this by placing hook scripts in a .husky folder that gets committed to the repository and automatically installed when running pnpm install.
A typical pre-commit hook runs lint-staged (covered next), while a pre-push hook ensures you don't push broken code:
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
pnpm tsc --noEmit && pnpm test --run
This runs type-checking and tests before every push. If either fails, the push is blocked.
lint-staged
But wait—what if you have work-in-progress code in your working directory that isn't ready for linting? You don't want unfinished code blocking your commit.
lint-staged runs validation tools only on staged files, so you can commit clean code while keeping your messy experiments untouched.
Configure it in .lintstagedrc.json:
{
"*.{ts,tsx,js,jsx}": ["eslint --fix", "prettier --write"],
"*.{json,md}": ["prettier --write"],
"*.{ts,tsx,js,md,json}": ["cspell lint --no-must-find-files"]
}
Commitlint With Conventional Commits
We all saw that commit message that was "fix" or "update" or even "fox" or the worse: ".".
We all wondered what was fixed or updated, and we all wished that the commit message was more descriptive.
This is the job of commitlint, to make sure your co-workers (and you) write good commit messages.
Commitlint is a tool that helps us to enforce a consistent commit message format, this is important for several reasons, it makes it easier to understand the history of the project, it makes it easier to generate changelogs, and it makes it easier to automate the release process.
We can use commitlint with the conventional commits format to enforce a consistent commit message format.
The conventional commits format is a specification for adding human and machine readable meaning to commit messages, it consists of a type, a scope, and a subject, for example:
feat(auth): add login functionality
fix(api): fix the bug in the user endpoint
docs: update the README file
We can configure commitlint to use the conventional commits format by installing the @commitlint/config-conventional package and creating a commitlint.config.js file like this:
module.exports = { extends: ["@commitlint/config-conventional"] };
Dependabot/Renovate
Keeping dependencies up to date is crucial for security and stability, but manually checking for updates is tedious and often forgotten.
This is where automated dependency update tools come in.
Dependabot is GitHub's built-in solution for automated dependency updates.
It scans your package.json (and other dependency files) and opens pull requests when new versions are available.
Renovate is a more powerful and flexible alternative that works across multiple platforms (GitHub, GitLab, Bitbucket, etc.).
It offers features like grouping updates, automerging minor updates, and custom scheduling.
Both tools integrate with your CI pipeline, so you can ensure that updates don't break your tests before merging.
Which one should you use? Dependabot is simpler and requires no setup if you're on GitHub.
Renovate offers more control and works across platforms, making it ideal for complex projects or organizations with specific update policies.
Centy
Issue tracking is essential for any project, but traditional tools like Jira or Linear live outside your codebase, creating a disconnect between your code and your tasks.
Centy takes a different approach by storing issues as Markdown files directly in your repository.
This means your issues live alongside your code, are version controlled with git, and require no external services or databases.
Why is this approach beneficial?
- Git-friendly: Issues are plain Markdown files, so you can track changes, review history, and collaborate using the same git workflow you use for code.
- Offline-first: Everything runs locally, no internet connection or external service required.
- AI-ready: Designed to work seamlessly with AI coding assistants, making it easy to reference and resolve issues programmatically.
- Simple: No complex setup, no accounts, no subscriptions. Just initialize and start tracking.
If you prefer keeping everything in one place and want your issue tracking to be as version-controlled as your code, Centy is worth exploring.
Summary
These tools form a quality pipeline that catches issues at every stage:
- pnpm manages dependencies efficiently
- ESLint + Prettier keep your code consistent and clean
- cspell catches typos before they become permanent
- knip removes dead weight from your codebase
- Husky + lint-staged enforce quality on every commit
- Commitlint keeps your git history readable
- Dependabot/Renovate keep dependencies fresh
- Centy tracks issues alongside your code
You don't need to adopt everything at once. Start with ESLint, Prettier, and Husky—they give you the most impact for the least effort. Add the others as your project matures.
The goal isn't to have the most tools, it's to have the right tools that help your team ship better code with less friction.
Full disclosure: I am the creator of Centy, AI assisted fixing typos in this article.
Top comments (0)