Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

【前編】24新卒開発研修で新しいツールを導入した話 〜Ruffの導入方法と使い方など〜

こんにちは、研究開発部 Automationグループで研究員をしている李です。業務として、Bill Oneの請求書情報に関する作業効率改善に従事しています。

例年通り *1 *2 4月24日(水)~4月26日(金)で研究開発部(以下、R&D)内の技術研修を実施しました。開発環境は昨年からのアップデートとして、Ruffとpre-commitを試みました。

本ブログでは、まず前編として、Ruffの導入方法と使い方などについて共有します。

目次

はじめに

Ruff は、Pythonのコードを効率的に解析し、パフォーマンスを向上させるための静的解析ツールです。さらに、リンター(Linter)とフォーマッター(Formatter)機能を持ち、ソフトウェア開発においてコードの品質を保持し、一貫性を高めるのに役立ちます。 これらは特に大規模なプロジェクトやチームでの開発において重要で、コードの可読性と保守性を向上させる助けとなります。以下、それぞれについて基本的な概要を説明します。

リンター(Linter)

  • リンター(Linter)とは、プログラムのソースコードを解析し、スタイルの違反、プログラムエラー・バグの可能性があるコード構造、そしてコードの改善点を指摘するツールです。
  • これらのツールは、コードの可読性と保守性を高めることにより、開発の効率性と品質を向上させることを目的としています。

例えば、以下のコードに対して Flake8(後述)というリンターを適用すると、いくつかエラーが表示されます:

  • 修正前 ugly.py
import sys

a= 2
b = 10;
[1, 2,3]
  • リンターの出力
$ flake8 ugly.py
ugly.py:1:1: F401 'sys' imported but unused
ugly.py:3:2: E225 missing whitespace around operator
ugly.py:4:7: E703 statement ends with a semicolon
ugly.py:5:6: E231 missing whitespace after ','

ここで、

  • F401 'sys' imported but unused
    • sys モジュールがインポートされていますが、このファイル内で実際には使用されていません。
    • 不要なインポートはコードの可読性を下げ、無駄なリソースの消費を引き起こす可能性があるため、削除することが推奨されます。
  • E225 missing whitespace around operator
    • 演算子の周囲に空白がないことが指摘されています。
    • Python の一般的なスタイルガイド(PEP8)では、演算子の前後には空白を 1 つ入れることが推奨されています。
  • E703 statement ends with a semicolon
    • 文末にセミコロンが使用されています。
    • Python ではセミコロンを使って複数の文を同じ行に記述することは可能ですが、一般的には各文を新しい行に記述することが推奨されており、不要なセミコロンは避けるべきです。
  • E231 missing whitespace after ','
    • カンマの後に空白がないことが指摘されています。
    • PEP8では、カンマの後にはスペースを一つ入れることで、要素がよりはっきりと区切られ、読みやすくなるとされています。

よく使われるPythonリンター

リンターは大きくコードスタイル系と型チェック系の二つに分類されます。これらはソフトウェア開発においてコードの品質を維持し、問題を早期に特定するために重要なツールです。特に研究開発部では、これらのツールを標準的に利用してプロジェクトの効率を向上させ、エラーの可能性を低減しています。

コードスタイル系

コードスタイル系リンターは、ソースコードが特定のスタイルガイドやコーディング規約に従っているかをチェックするツールです。これにより、コードの一貫性が保たれ、読みやすく、保守しやすいコードベースが確保されます。研究開発部で一般的に利用されているコードスタイル系リンターには以下のものがあります:

  • Flake8
    • Python開発で用いられるツールで、PEP8(Python のスタイルガイド、後述します)に準拠しているかどうかをチェックします。
      • PEP8 とは、Python Enhancement Proposal(Python改善提案)の略称であり、番号8の提案文書に基づいています。この文書は、Pythonのコードを書く際のスタイルガイドとして広く受け入れられており、Pythonコードの書き方に関する規約やベストプラクティスを提供しています。主な目的は、Pythonコミュニティ内でのコードの読みやすさと一貫性を向上させることです。
    • 実は pyflakes, pycodestyle, mccabe のラッパです。
    • 数多くのプラグインが存在します。
      • flake8-bugbear(バグを生みそうな書き方を指摘します)
      • flake8-builtins(ビルトインと被る命名をすると警告が生じます)
    • pyproject.toml に設定を書けず、別途、.flake8 を用意する必要があります。

型チェック系

型チェック系リンターは、コード中の型の使用が正しいかどうかを分析するツールです。静的型付け言語だけでなく、動的型付け言語での使用も増えています。これにより、開発の早い段階で型関連のエラーを検出し、ランタイムエラーのリスクを減少させることができます。研究開発部でよく利用されている型チェック系リンターには以下のものがあります:

  • mypy
    • Pythonのオプショナルな静的型チェックを提供するツールで、Pythonコードに型アノテーションを導入して、より堅牢なコードを書くのに役立ちます。

フォーマッター(Formatter)

  • フォーマッター(Formatter)は、リンターやそのほかの規則に則ってコードを修正するツールです。
  • コードの書式を統一し、可読性を高めるために広く使用されています。
import datetime
import json
import firebase_admin
from tqdm import tqdm
import os
import pandas as pd
import re
from collections import Counter
from .schemas import RawData

上記の例はisortにより、以下のように修正されます:

import datetime
import json
import os
import re
from collections import Counter

import firebase_admin
import pandas as pd
from tqdm import tqdm

from .schemas import RawData

よく使われるPythonフォーマッター

  • Black
    • 自動的にフォーマットするための厳密なツールです。
    • コードの読みやすさを向上させるために、PEP8(Pythonのスタイルガイド)に準拠していますが、いくつかの場合にはPEP8よりも厳しいルールを適用します。
    • 特定の書き方にそろえることで、コードのスタイルに関する議論を減らすことを目指しています。
    • コードの一貫性が向上し、レビューの時間が短縮されます。
  • isort
    • インポート文を整理し、ソートするツールです。
    • PEP8のガイドラインに従って、標準ライブラリ、サードパーティーのライブラリ、ローカルのライブラリごとにセクションを分け、各セクション内でアルファベット順にインポートを並べ替えます。
    • プロジェクト全体でインポートの整合性と読みやすさを保つのに役立ちます。

Ruffとは

-Ruffは、特にユーラシアに生息する小型の渉禽類である、リュウキュウシギという鳥の名前です。繁殖期にはオスが顕著な首の飾り羽を持つことで知られています。

  • Ruffとは、Pythonのコードにおける構文エラーやスタイルの問題を検出するための高速な静的解析ツールです。
  • Flake8やPyLintなどの既存の静的解析ツールよりも速く、リアルタイムでフィードバックを提供することを目指しています。
  • 開発者はコーディング中にすぐに潜在的な問題を特定・修正できます。

Ruffを導入した経緯

目的

  • コードの品質を向上させ、保守性や可読性を高めることにあります。特に、大規模なプロジェクトや長期間にわたるプロジェクトでコード品質が一定の水準を保てます。
  • 静的解析ツールを利用することで、実行前に潜在的なバグやコーディングの問題を発見し、修正することが可能になります。これにより、開発サイクル全体の効率を向上させることができます。

背景

  • 新入社員が迅速にプロジェクトに参加し、価値を生み出すことが求められています。しかし、開発に不慣れなメンバーを対象に実施する研修であるため、複数のリンターやフォーマッターを設定に費やす時間が長くなってしまうのが課題でした。
  • R&D内では、開発プロセスの効率化と改善が常に求められています。特に、Pythonが多く使用されている環境で、コードの一貫性を保ちつつ、エラーを減少させる必要があります。多くのプロジェクトがチームで行われるため、コードスタイルの一貫性を保つことが重要です。Ruffの導入により、チーム内でのコードの統一性を保ち、コーディングのガイドラインに沿った開発を容易にします。

対処法

  • Ruffは、コードのフォーマットやスタイルを自動的に統一し、開発者がコードレビューに費やす時間を削減することが可能になります. Ruffの拡張機能(後述)を導入することで、commitされるコードが自動的に検証されるようにします。これにより、デプロイ前に問題を捉え、開発の迅速化と安全性の向上を図ります。
  • 複数の設定オプションが用意されており、プロジェクト固有のルールや除外設定を簡単に定義できるため、さまざまな開発環境やプロジェクトの要件に対応できる柔軟性や、開発者は自己のチェックルールを作成し、既存のルールを拡張できる高いカスタマイズ性も評価されています。

Ruffの特徴

  • 速い
    • とにかく速いです。
  • サポートするルールが多いです。
    • Flake8, isort, PEP8, Blackなどをネイティブサポートしています。
  • 一部のミス(例:isort)は保存時に自動的に修正してくれます。
  • pyproject.toml に対応できます。
    • isortとBlackはpyproject.tomlにおける設定が可能であったのに対し、Flake8は非対応です。一方で、Ruffを使用することによって、Flake8 + Black + isortの設定を1つのファイルで記述できます。
...

[tool.poetry.group.dev.dependencies]
ruff = "^0.3.7"

...

[tool.ruff]
target-version = "py310"
line-length = 120
select = ["ALL"]
ignore = [
    ...
]
exclude = [
    ...
]

[tool.ruff.isort]
known-first-party = ["..."]

[tool.ruff.mccabe]
max-complexity = 5

Ruffのメリットとデメリット

メリット

  • 高速性:
    • 大規模なコードベースに対しても迅速に解析し、問題点を指摘できます。
  • レビュー時間削減:
    • 複数の制約ツールを 1 つまとめることが可能になり、レビューにかかる時間を削減できます。

デメリット

  • ルールのカバレッジ:
    • ほかの静的解析ツール(例えばFlake8)と比較すると、カバーしているルールやチェックの範囲が限定的な場合があります。
    • 例えば、以下に示すように、Flake8は、GoogleやNumPy形式のドキュメンテーションスタイルなどを検証するために使えますが、Ruffにもドキュメントストリングチェックの機能はあるものの、特定のスタイルにはまだ特化していません。
    • 他にも、flake8-radon ではサイクロマティック複雑性や他のコードメトリクスを測定できますが、Ruffでは基本的な複雑性チェックしか含まれていなかったり、flake8-bugbear でサポートされている全てのバグが発生しやすい特定のコードパターンがまだ完全にはカバーされていなかったり、不十分なところがあります。
[tool.flake8]
docstring-convention = "google"
  • 慣れ親しんだツールからの移行:
    • 既存プロジェクトにいきなり入れようとすると大変なので、従来のルールとの差分が最小限になるように設定を選択して導入していくのが望ましいです。
    • 対して、プロジェクトの初期から導入しておけば、厳しめのルールであっても問題は少ないです。研修コードはテンプレートとしても利用されるので、今後部署内のコード品質を向上していくことを期待します。(GitHub Actionsも整備しましたよ!)

まとめ

Ruffの導入は、新卒研修だけでなく、R&D内にも大きなメリットをもたらしました。このツールにより、コードの品質を維持しながら開発速度を向上させることが可能になり、特に高速な実行速度と直感的なエラー報告が開発者から高く評価されました。さらに、より複雑なプロジェクトや研究開発でも、一貫したコーディングスタイルの保持と効率的なエラー検出が行えるようになり、生産性の向上が見られました。また、チーム間でのコードレビューがより効率的になり、価値のある技術的な議論に集中できるようになったことは、部門全体の成長に寄与しています。

© Sansan, Inc.