”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 通过 Linting 提高代码质量

通过 Linting 提高代码质量

发布于2024-11-06
浏览:128

Whenever I start a new project, one of the first things I do is put in place a code linter. For the uninitiated, linters analyze your project and call out potential issues. They can provide guidance on everything from how much spacing is used between lines to specific implementation details, such as restricting the usage of certain patterns and habits. You can take these tools one step further by making them part of your CICD workflow, so your build fails if it detects non-compliant code.

Improving Code Quality with Linting

To many, this may seem like a hassle, but depending on the scale of the project and the number of people working on it, using these tools is a way to standardize shared best practices and opinions across a whole team. After all, everyone is opinionated when it comes to writing code. You can take two completely different code bases written in the same language, and neither would look the same. In JavaScript, there are multiple ways to write the same thing. There are various ways of writing loops, defining functions, and even variables. As more people work on the same code base, their opinions come with them, and without a way to standardize, you'll soon end up in a hellhole of pull requests where people constantly leave unproductive "/nitpick" comments for basic things. 

Linting and Formatting JavaScript

Setting up these types of tools is typically straightforward; you'll install a package, often from a registry, and then run something either on the command line or directly within your IDE with the help of a plugin.

One prevalent linting option for JavaScript is ESLint. ESLint is based on a shared configuration file you provide in your repository. Let's look at a relatively simple example of a configuration that inherits a series of grouped recommended rules for best practices. The community drives these recommended rules and even provides a mechanism for auto-fixing some of them with the –-fix flag.

import js from '@eslint/js';
import globals from 'globals';

export default [
  js.configs.recommended,
  {
    languageOptions: {
      ecmaVersion: 12,
      globals: {
        ...globals.browser,
      },
    },
  }
];

In addition to using the recommended rules, I also like to expand on them by adding several optional, more opinionated rules. For example, I'm not too fond of functions with many parameters as I've found that in the past, they can cause problems and generally become hard to follow, so in most of the codebases I work on, I enforce this using the max-params rule. I also like to ensure that my code is formatted a certain way, so I use the Prettier plugin to ensure that everything matches my Prettier config file, preventing discrepancies around commas and spacing.

import js from '@eslint/js';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import globals from 'globals';

export default [
  js.configs.recommended,
  eslintPluginPrettierRecommended,
  {
    languageOptions: {
      ecmaVersion: 12,
      globals: {
        ...globals.browser,
      },
    },
  },
  {
    rules: {
      'max-params': ['error', 3],
      'no-unused-vars': 'error',
    },
  },
];

With this configuration, the following function will flag an error directly in my IDE or when I run ESLint via the command line, as it does not adhere to my max-param restrictions or the prettier configuration. If I were to run the linter in my CICD workflow, it would cause the build to fail, preventing me from merging it upstream, which is crucial if we want to ensure our shared branches are always in a good state.

const fetchData = async (endpoint, token, headers, params) => {
  try {
          const response = await fetch(endpoint, {
      headers: Object.assign({
        Authorization: `Bearer ${token}`
      }, headers),
      params
    });

const data = await response.json();


const newData = {
      ...data,
    additionalProperty: 'newValue',
    };

                return newData;
  } catch (error) {
    console.error('Error fetching data:', error);
  }
};

fetchData('https://api.example.com', 'token', { 'Content-Type': 'application/json' }, { page: 1 });
/Users/jives/fetch.js
   1:60  error  Async arrow function has too many parameters (4). Maximum allowed is 3                                                                                                                                                                  max-params
   2:3   error  Delete `··`                                                                                                                                                                                                                             prettier/prettier
   2:11  error  'cat' is assigned a value but never used                                                                                                                                                                                                no-unused-vars
   3:3   error  Delete `··`                                                                                                                                                                                                                             prettier/prettier
   4:1   error  Delete `········`                                                                                                                                                                                                                       prettier/prettier
   5:1   error  Replace `········headers:·Object.assign(` with `······headers:·Object.assign(⏎········`                                                                                                                                                 prettier/prettier
   6:43  error  Insert `,`                                                                                                                                                                                                                              prettier/prettier
   7:11  error  Replace `·headers)` with `⏎········headers`                                                                                                                                                                                             prettier/prettier
   8:7   error  Replace `··params` with `),⏎······params,`                                                                                                                                                                                              prettier/prettier
   9:1   error  Replace `······` with `····`                                                                                                                                                                                                            prettier/prettier
  10:1   error  Replace `······⏎` with `⏎··`                                                                                                                                                                                                            prettier/prettier
  12:1   error  Delete `··`                                                                                                                                                                                                                             prettier/prettier
  13:5   error  Delete `··⏎······`                                                                                                                                                                                                                      prettier/prettier
  15:7   error  Replace `········additionalProperty:·'newValue'` with `additionalProperty:·'newValue',`                                                                                                                                                 prettier/prettier
  16:1   error  Replace `······` with `····`                                                                                                                                                                                                            prettier/prettier
  17:1   error  Delete `··`                                                                                                                                                                                                                             prettier/prettier
  18:5   error  Delete `··············`                                                                                                                                                                                                                 prettier/prettier
  19:1   error  Replace `····` with `··`                                                                                                                                                                                                                prettier/prettier
  20:1   error  Delete `··`                                                                                                                                                                                                                             prettier/prettier
  21:3   error  Delete `··`                                                                                                                                                                                                                             prettier/prettier
  22:1   error  Delete `··`                                                                                                                                                                                                                             prettier/prettier
  23:1   error  Delete `··`                                                                                                                                                                                                                             prettier/prettier
  24:1   error  Replace `··fetchData('https://api.example.com',·'token',·{·'Content-Type':·'application/json'·},·{·page:·1·});` with `fetchData(⏎··'https://api.example.com',⏎··'token',⏎··{·'Content-Type':·'application/json'·},⏎··{·page:·1·},⏎);⏎`  prettier/prettier

✖ 23 problems (23 errors, 0 warnings)

If I resolve these issues, I no longer get an error, and the build will pass.

const fetchData = async ({ endpoint, token, headers, params }) => {
  try {
    const response = await fetch(endpoint, {
      headers: {
        Authorization: `Bearer ${token}`,
        ...headers,
      },
      params,
    });

    const data = await response.json();

    const newData = {
      ...data,
      additionalProperty: 'newValue',
    };

    return newData;
  } catch (error) {
    console.error('Error fetching data:', error);
  }
};

fetchData({
  endpoint: 'https://api.example.com',
  token: 'token',
  headers: { 'Content-Type': 'application/json' },
  params: { page: 1 },
});

Linters can also be a great way to reinforce good habits. For example, having a linting rule in your project for no unused variables can be a great way to teach the practice of not leaving unused references in code. As a maintainer, it improves your quality of life, and it's one less thing you'll need to consider during the pull request process. You can even use a linter to enforce that any types aren't used in TypeScript and other harmful practices that can cause bugs in your code.

Over time, your configuration file will evolve as you form best practices and needs amongst your team; you may decide to add or even remove unnecessary rules. The ultimate goal is more approachable code and less wasted time—what's not to like?

You Can and Should Lint CSS

You can even lint your stylesheets if you're working with CSS. One of my favourite tools for that is Stylelint. Similar to ESLint, it's configuration-based and lets you define what rules you want to include, it also has a recommended configuration that you can extend from.

{
  "extends": "stylelint-config-standard"
}

For example, linting CSS can be beneficial in cases where you need to support legacy browsers. Downgrading JavaScript is pretty common, but it's not always as simple for CSS. Using a linter allows you to be honest with yourself by flagging problematic lines that won't work in older environments, ensuring your pages look as good as possible for everyone.

{
  "extends": "stylelint-config-recommended",
  "plugins": [
    "stylelint-no-unsupported-browser-features"
  ]
  "rules": {
    "plugin/no-unsupported-browser-features": [true, {
      "browsers": ["Chrome >= 66"]
    }]
  }
}

Using this Stylelint plugin, the following CSS would flag an error as you can't use flex-gap in older Chrome versions. These properties are commonplace in modern codebases and can be easily missed if you're not testing older browsers. You can catch these issues with a linter before they become a problem.

.container {
  align-items: center;
  display: flex;
  gap: 10px;
  justify-content: center;
}
[js] src/styles.css
[js]    4:1  ✖  Unexpected browser feature "flexbox-gap" is not supported by Chrome 66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83  plugin/no-unsupported-browser-features

CSS linting can also be leveraged to ensure that you stick to a single unit of measurement for things like font sizes and even enforce the usage of CSS variables for certain property types, like colours and padding. These rules go a long way to providing consistency, especially when you may have multiple views or components.

Lint Early, Lint Often

Creating a baseline set of standards around linting at the start of a project is much easier. Adapting code as you write is much more straightforward than retrofitting an existing project. If you're starting a new project and already have another application that uses a linter, add the same ruleset to your new one and adapt it as you go along. Just because it's harder to adapt a linter to something already established doesn't make it any less worth it, though; it's just more of a time sink.

Most linters allow you to mark a rule as a warning or an error; warnings inform you of the problem when the linter is run, whereas an error will break your build pipeline, preventing it from going any further. I typically avoid setting any rules as a warning unless I am actively migrating code to where I can turn it on as an error. My process is usually:

  1. Determine what rules should be enabled, discuss with your team if you're not working solo.
  2. Enable the rule as a warning.
  3. Fix the code that is flagging the warning.
  4. Enable the rule as an error once all the warnings are resolved.
  5. Avoid lint-ignore comments unless absolutely necessary.

I've made the mistake of doing large refactors to turn on linting rules and broken things. Play it safe and slowly make changes over time to avoid unnecessary headaches.

Closing Notes

Linting is a great way to ensure that your codebase is consistent and that you're following best practices; many options are available for different languages and frameworks that you can find with a quick search. They can be a pain to set up for established projects, but the benefits far outweigh the initial setup time. It's also a great way to reinforce good habits and ensure your codebase is clean and maintainable. If you're not already using a linter, I highly recommend you start. It's a small change that can positively impact your quality of life as a developer.

版本声明 本文转载于:https://dev.to/jamesives/improving-code-quality-with-linting-4no1?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • PHP 设计模式:适配器
    PHP 设计模式:适配器
    适配器设计模式是一种结构模式,允许具有不兼容接口的对象一起工作。它充当两个对象之间的中介(或适配器),将一个对象的接口转换为另一个对象期望的接口。这允许那些因为具有不同接口而不兼容的类在不修改其原始代码的情况下进行协作。 适配器结构 适配器模式一般由三个主要元素组成: 客户端:期望与特定接口的对象一...
    编程 发布于2024-11-06
  • 了解 PHP 中的 WebSocket
    了解 PHP 中的 WebSocket
    WebSockets 通过单个 TCP 连接提供实时、全双工通信通道。与 HTTP 不同,HTTP 中客户端向服务器发送请求并等待响应,WebSocket 允许客户端和服务器之间进行连续通信,而无需多次请求。这非常适合需要实时更新的应用程序,例如聊天应用程序、实时通知和在线游戏。 在本指南中,我们将...
    编程 发布于2024-11-06
  • Visual Studio 2012 支持哪些 C++11 功能?
    Visual Studio 2012 支持哪些 C++11 功能?
    Visual Studio 2012 中的 C 11 功能随着最近发布的 Visual Studio 2012 预览版,许多开发人员对 C 11 功能的支持感到好奇。虽然 Visual Studio 2010 已提供部分 C 11 支持,但新版本提供了扩展的功能。Visual Studio 2012...
    编程 发布于2024-11-06
  • 如何在Windows启动时自动运行Python脚本?
    如何在Windows启动时自动运行Python脚本?
    在 Windows 启动时运行 Python 脚本每次 Windows 启动时执行 Python 脚本对于自动化任务或启动基本程序至关重要。多种方法提供不同级别的自定义和用户控制。自动执行脚本的选项:1。打包为服务:创建 Windows 服务并安装它。此方法在计算机上运行脚本,无论用户是否登录。需要...
    编程 发布于2024-11-06
  • 探索 Astral.CSS:彻底改变网页设计的 CSS 框架。
    探索 Astral.CSS:彻底改变网页设计的 CSS 框架。
    在快节奏的 Web 开发世界中,框架在帮助开发人员高效创建具有视觉吸引力和功能性的网站方面发挥着关键作用。在当今可用的各种框架中,Astral CSS 因其独特的设计理念和易用性而脱颖而出。本文深入探讨了 Astral CSS 的功能、优点和总体影响。 什么是星界? Astral 是一个现代 CSS...
    编程 发布于2024-11-06
  • ESnd 箭头函数综合指南
    ESnd 箭头函数综合指南
    ES6简介 ECMAScript 2015,也称为 ES6 (ECMAScript 6),是对 JavaScript 的重大更新,引入了新的语法和功能,使编码更高效、更易于管理。 JavaScript 是用于 Web 开发的最流行的编程语言之一,ES6 的改进大大增强了其功能。 本...
    编程 发布于2024-11-06
  • 揭示算法和数据结构:高效编程的基础
    揭示算法和数据结构:高效编程的基础
    在这一系列文章中,我将分享我的学习历程,涉及在学术环境和大型科技公司中广泛讨论的两个主题:算法和数据结构。尽管这些主题乍一看似乎令人畏惧,特别是对于像我这样由于其他职业挑战而在整个职业生涯中没有机会深入研究这些主题的人,但我的目标是让它们易于理解。 我将从最基本的概念开始,然后转向更高级的主题,创建...
    编程 发布于2024-11-06
  • 如何使用 pprof 来分析 Go 程序中的 goroutine 数量?
    如何使用 pprof 来分析 Go 程序中的 goroutine 数量?
    使用 pprof 分析 Goroutine 数量检测 Go 程序中潜在的 Goroutine 泄漏需要监控一段时间内活动的 Goroutine 数量。虽然标准 go 工具 pprof 命令提供了对阻塞的深入了解,但它并不直接解决 goroutine 计数问题。要有效地分析 goroutine 数量,...
    编程 发布于2024-11-06
  • 如何将类方法作为回调传递:了解机制和技术
    如何将类方法作为回调传递:了解机制和技术
    如何将类方法作为回调传递后台在某些场景下,您可能需要将类方法作为回调传递给其他函数以提高效率具体任务的执行。本文将指导您完成实现此目的的各种机制。使用可调用语法要将函数作为回调传递,您可以直接将其名称作为字符串提供。但是,此方法不适用于类方法。传递实例方法类实例方法可以使用数组作为回调传递,该数组以...
    编程 发布于2024-11-06
  • 网页抓取 - 有趣!
    网页抓取 - 有趣!
    一个很酷的术语: CRON = 按指定时间间隔自动安排任务的编程技术 网络什么? 在研究项目等时,我们通常会从各个网站编写信息 - 无论是日记/Excel/文档等。 我们正在抓取网络并手动提取数据。 网络抓取正在自动化这一过程。 例子 当在网上搜索运动鞋时,它会显示包...
    编程 发布于2024-11-06
  • 感言网格部分
    感言网格部分
    ?在学习 CSS 网格时刚刚完成了这个推荐网格部分的构建! ?网格非常适合创建结构化布局。 ?现场演示:https://courageous-chebakia-b55f43.netlify.app/ ? GitHub:https://github.com/khanimran17/Testimonia...
    编程 发布于2024-11-06
  • 为什么 REGISTER_GLOBALS 被认为是 PHP 中的主要安全风险?
    为什么 REGISTER_GLOBALS 被认为是 PHP 中的主要安全风险?
    REGISTER_GLOBALS 的危险REGISTER_GLOBALS 是一个 PHP 设置,它允许所有 GET 和 POST 变量在 PHP 脚本中用作全局变量。此功能可能看起来很方便,但由于潜在的安全漏洞和编码实践,强烈建议不要使用它。为什么 REGISTER_GLOBALS 不好?REGIS...
    编程 发布于2024-11-06
  • Nodemailer 概述:在 Node.js 中轻松发送电子邮件
    Nodemailer 概述:在 Node.js 中轻松发送电子邮件
    Nodemailer 是一个用于发送电子邮件的 Node.js 模块。以下是快速概述: Transporter:定义电子邮件的发送方式(通过 Gmail、自定义 SMTP 等)。 const transporter = nodemailer.createTransport({ ... }); ...
    编程 发布于2024-11-06
  • JavaScript 中的轻松错误处理:安全赋值运算符如何简化您的代码
    JavaScript 中的轻松错误处理:安全赋值运算符如何简化您的代码
    JavaScript 中的错误处理可能很混乱。将大块代码包装在 try/catch 语句中是可行的,但随着项目的增长,调试就变成了一场噩梦。幸运的是,有更好的方法。输入 安全赋值运算符 (?=) - 一种更干净、更有效的错误处理方法,使代码保持可读性并简化调试。 什么是安全赋值运算符...
    编程 发布于2024-11-06
  • Javascript 很难(有悲伤)
    Javascript 很难(有悲伤)
    这将是一个很长的阅读,但让我再说一遍。 JAVASCRIPT很难。上次我们见面时,我正在踏入 Javascript 的世界,一个眼睛明亮、充满希望的程序员踏入野生丛林,说“这能有多难?”。我错得有多离谱??事情变得更难了,我(勉强)活了下来,这是关于我的旅程的一个小混乱的故事。 变量:疯狂的开始 ...
    编程 发布于2024-11-06

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3