」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 透過 Linting 提高程式碼品質

透過 Linting 提高程式碼品質

發佈於2024-11-06
瀏覽:339

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]刪除
最新教學 更多>
  • 為什麼HTML無法打印頁碼及解決方案
    為什麼HTML無法打印頁碼及解決方案
    無法在html頁面上打印頁碼? @page規則在@Media內部和外部都無濟於事。 HTML:Customization:@page { margin: 10%; @top-center { font-family: sans-serif; font-weight: ...
    程式設計 發佈於2025-07-15
  • 用戶本地時間格式及時區偏移顯示指南
    用戶本地時間格式及時區偏移顯示指南
    在用戶的語言環境格式中顯示日期/時間,並使用時間偏移在向最終用戶展示日期和時間時,以其localzone and格式顯示它們至關重要。這確保了不同地理位置的清晰度和無縫用戶體驗。以下是使用JavaScript實現此目的的方法。 方法:推薦方法是處理客戶端的Javascript中的日期/時間格式化和...
    程式設計 發佈於2025-07-15
  • 對象擬合:IE和Edge中的封面失敗,如何修復?
    對象擬合:IE和Edge中的封面失敗,如何修復?
    To resolve this issue, we employ a clever CSS solution that solves the problem:position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%)...
    程式設計 發佈於2025-07-15
  • 人臉檢測失敗原因及解決方案:Error -215
    人臉檢測失敗原因及解決方案:Error -215
    錯誤處理:解決“ error:((-215)!empty()in Function Multultiscale中的“ openCV 要解決此問題,必須確保提供給HAAR CASCADE XML文件的路徑有效。在提供的代碼片段中,級聯分類器裝有硬編碼路徑,這可能對您的系統不准確。相反,OPENCV提...
    程式設計 發佈於2025-07-15
  • 找到最大計數時,如何解決mySQL中的“組函數\”錯誤的“無效使用”?
    找到最大計數時,如何解決mySQL中的“組函數\”錯誤的“無效使用”?
    如何在mySQL中使用mySql 檢索最大計數,您可能會遇到一個問題,您可能會在嘗試使用以下命令:理解錯誤正確找到由名稱列分組的值的最大計數,請使用以下修改後的查詢: 計數(*)為c 來自EMP1 按名稱組 c desc訂購 限制1 查詢說明 select語句提取名稱列和每個名稱...
    程式設計 發佈於2025-07-15
  • 您如何在Laravel Blade模板中定義變量?
    您如何在Laravel Blade模板中定義變量?
    在Laravel Blade模板中使用Elegance 在blade模板中如何分配變量對於存儲以後使用的數據至關重要。在使用“ {{}}”分配變量的同時,它可能並不總是是最優雅的解決方案。 幸運的是,Blade通過@php Directive提供了一種更優雅的方法: $ old_sectio...
    程式設計 發佈於2025-07-15
  • Spark DataFrame添加常量列的妙招
    Spark DataFrame添加常量列的妙招
    在Spark Dataframe ,將常數列添加到Spark DataFrame,該列具有適用於所有行的任意值的Spark DataFrame,可以通過多種方式實現。使用文字值(SPARK 1.3)在嘗試提供直接值時,用於此問題時,旨在為此目的的column方法可能會導致錯誤。 df.withco...
    程式設計 發佈於2025-07-15
  • 如何將來自三個MySQL表的數據組合到新表中?
    如何將來自三個MySQL表的數據組合到新表中?
    mysql:從三個表和列的新表創建新表 答案:為了實現這一目標,您可以利用一個3-way Join。 選擇p。 *,d.content作為年齡 來自人為p的人 加入d.person_id = p.id上的d的詳細信息 加入T.Id = d.detail_id的分類法 其中t.taxonomy ...
    程式設計 發佈於2025-07-15
  • 編譯器報錯“usr/bin/ld: cannot find -l”解決方法
    編譯器報錯“usr/bin/ld: cannot find -l”解決方法
    錯誤:“ usr/bin/ld:找不到-l “ 此錯誤表明鏈接器在鏈接您的可執行文件時無法找到指定的庫。為了解決此問題,我們將深入研究如何指定庫路徑並將鏈接引導到正確位置的詳細信息。 添加庫搜索路徑的一個可能的原因是,此錯誤是您的makefile中缺少庫搜索路徑。要解決它,您可以在鏈接器命令中添...
    程式設計 發佈於2025-07-15
  • 如何在無序集合中為元組實現通用哈希功能?
    如何在無序集合中為元組實現通用哈希功能?
    在未訂購的集合中的元素要糾正此問題,一種方法是手動為特定元組類型定義哈希函數,例如: template template template 。 struct std :: hash { size_t operator()(std :: tuple const&tuple)const {...
    程式設計 發佈於2025-07-15
  • 如何正確使用與PDO參數的查詢一樣?
    如何正確使用與PDO參數的查詢一樣?
    在pdo 中使用類似QUERIES在PDO中的Queries時,您可能會遇到類似疑問中描述的問題:此查詢也可能不會返回結果,即使$ var1和$ var2包含有效的搜索詞。錯誤在於不正確包含%符號。 通過將變量包含在$ params數組中的%符號中,您確保將%字符正確替換到查詢中。沒有此修改,PD...
    程式設計 發佈於2025-07-15
  • 如何使用“ JSON”軟件包解析JSON陣列?
    如何使用“ JSON”軟件包解析JSON陣列?
    parsing JSON與JSON軟件包 QUALDALS:考慮以下go代碼:字符串 } func main(){ datajson:=`[“ 1”,“ 2”,“ 3”]`` arr:= jsontype {} 摘要:= = json.unmarshal([] byte(...
    程式設計 發佈於2025-07-15
  • 如何使用FormData()處理多個文件上傳?
    如何使用FormData()處理多個文件上傳?
    )處理多個文件輸入時,通常需要處理多個文件上傳時,通常是必要的。 The fd.append("fileToUpload[]", files[x]); method can be used for this purpose, allowing you to send multi...
    程式設計 發佈於2025-07-15
  • 為什麼不````''{margin:0; }`始終刪除CSS中的最高邊距?
    為什麼不````''{margin:0; }`始終刪除CSS中的最高邊距?
    在CSS 問題:不正確的代碼: 全球範圍將所有餘量重置為零,如提供的代碼所建議的,可能會導致意外的副作用。解決特定的保證金問題是更建議的。 例如,在提供的示例中,將以下代碼添加到CSS中,將解決餘量問題: body H1 { 保證金頂:-40px; } 此方法更精確,避免了由全局保證金重置...
    程式設計 發佈於2025-07-15
  • Java數組中元素位置查找技巧
    Java數組中元素位置查找技巧
    在Java數組中檢索元素的位置 利用Java的反射API將數組轉換為列表中,允許您使用indexof方法。 (primitives)(鏈接到Mishax的解決方案) 用於排序陣列的數組此方法此方法返回元素的索引,如果發現了元素的索引,或一個負值,指示應放置元素的插入點。
    程式設計 發佈於2025-07-15

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3