How to Prevent Legacy Code

How to Prevent Legacy Code

In the dynamic sphere of software development, legacy code often poses a significant challenge. Over time, this code can accumulate due to the continuous application of patches and quick fixes just to keep the system operational. This article will help you understand and apply strategies for managing this common issue so you can ensure that your code remains clean, efficient, and updated.

What Are the Problems of Legacy Code?

Legacy code can be a huge headache for developers. An inherited codebase often comes with technical debt, the result of quick fixes and patches that were originally intended to keep the system afloat. Over time, these band-aid solutions can make the code increasingly hard to understand, modify, or update without triggering a rush of new bugs or system errors. Legacy code can slow down progress, complicate the adoption of new technologies, and consume more resources, causing problems with development efficiency and productivity.

How Can Legacy Code Be Solved?

An effective strategy to tackle this issue is the use of strict typing in TypeScript. This feature activates an extensive range of type-checking behaviors that help with program accuracy.

TypeScript

If you enable the strictNullChecks option, null and undefined are treated as distinct types. This means that if you try to use them in a place where a specific value is expected, you’ll encounter a type error. This simple but powerful tool can protect your system against runtime errors when trying to access undefined fields. In addition, the strictFunctionTypes flag improves the accuracy of function parameter checking, making your codebase even more robust.

But be careful—enabling all strict mode options may be difficult due to potential legacy errors. But you don’t have to make this shift in one single leap; instead you can choose to implement it gradually.

One way to do this is by using the lint-staged package in combination with the tsc-files library. tsc-files runs TypeScript compiler (tsc) on specific files without disregarding tsconfig.json. This way, you can start improving your codebase one step at a time.

Here’s how that might look:

"lint-staged": {
  "*.{js,ts}": ["tsc-files --noEmit --noImplicitAny --strictNullChecks"],
}

In this example, TypeScript compiles without creating any output files, thanks to the ‘noEmit‘ flag. The ‘noImplicitAny‘ and ‘strictNullChecks‘ flags, which are disabled in the main tsconfig file, come into play for code verification. This approach means you don’t have to fix all errors in the project at once; instead, you only need to resolve errors in the files that have changed before committing to Git.

Next, let’s look at additional tools that can help you deal with legacy code.

Lint

Linting is the process of analyzing code for errors and style inconsistencies. It is used to ensure code quality and maintainability by quickly catching and rectifying potential errors and enforcing consistent style. Linter helps to catch bugs early in the development process, making your code easier to read and understand, facilitating smoother collaboration and more efficient code reviews.

Linter is a great tool for linting. We recommend using it with custom configuration to reap the full benefits. For example you can ask it to parse *.ts files according to main tsconfig.json, or *.html files with extending recommended rules ‘plugin:@angular-eslint/template/recommended‘. Using custom configurations like these help to keep Linter aligned with the precise goals of your project.

Another aspect to consider is the use of the lint-staged‘ library. Linting an entire project can be time-consuming and, in some cases, the results might not be entirely relevant. To avoid these annoyances, ‘lint-staged‘ allows you to target only those files that are staged for commit. This way, you can maintain the quality of your code efficiently, focusing on changes that are about to become part of your codebase.

Here’s how that looks:

"lint-staged": {
  "*.{js,ts}": [
	"eslint --cache",
] }

In this case only *.ts and *.js files will be linted, because they are what were committed.

The Husky‘ library is also worth knowing about. Husky enables the automation of tasks such as linting commit messages, running tests, and linting code whenever you make a commit or push to your repository.

Here’s how to run Husky (pre-commit.sh file):

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

npx lint-staged
npm run validate

In this case, the lint-staged command executes linting, and a custom validation npm command specified in the configuration file triggers with each Git commit.

Prettier Code

Another useful tool to keep in your back pocket is Prettier (https://prettier.io/). Prettier is a popular code formatter that can automatically format your code to follow a consistent style. Prettier has several configuration options that allow you to customize its behavior to fit your project’s needs. For example, you can configure line length and indentation style.

Here’s how you can use Prettier:

"lint-staged": {
	"*.{js,ts,css}": "prettier --write"
}

In this instance, Prettier is applied to JavaScript, TypeScript, and CSS files within the “lint-staged” context. Each time you commit changes to your Git repository, Prettier will automatically format the staged files. This ensures that the committed code conforms to your predefined stylistic rules, enhancing its consistency. The --write flag tells Prettier to not only identify formatting issues but also to overwrite the files with the recommended formatting changes.

Verification will be only for certain categories of files, and on every commit to Git repository. We use the -save option to rewrite corrected files.

Conclusion

Handling legacy code necessitates a strategic and proactive approach. By leveraging TypeScript’s strict type feature, along with robust tools like ESLint, Husky, and Prettier, you can ensure code quality, prevent legacy code accumulation, and foster a maintainable, efficient codebase. The road to clean code is a continuous journey, but with the right tools and techniques, it’s worth undertaking—cleaning up your legacy code can lead to significantly improved code quality and enhanced software development processes.

How to Prevent Legacy Code

Subscribe
to our newsletter

Get the latest industry trends, exclusive insights, and Gcore
updates delivered straight to your inbox.