シンクロ・フード エンジニアブログ

飲食店ドットコムを運営する、株式会社シンクロ・フードの技術ブログです

Filelint を作って社内プログラムのコーディングスタイルを矯正した話

はじめまして。今年新卒で入社した基盤チームの川井 (@fohte) です。

最近までは、新卒企画研修として開発した wenu という飲食店向け Web サービスの開発基盤を整えたり、フロントエンド (React) のロジック部分を担当していました。

社内の既存プログラムの問題点

新卒研修終了後に配属され、既存プロジェクトの開発に携わったのですが、古くからあるコードが多く残されており、またコーディングスタイルも統一されていませんでした。
具体的には、以下のような問題がありました。

  • 行末にスペースやタブが残されている
  • ファイルの最後に空行が存在したりしなかったりする
  • インデントがソフトタブだったりハードタブだったりと混在している

これらの問題は構文エラーではなく、コンパイルも正常に通るために放置されていました。
しかし、各開発者が編集した際に無駄にスペースが挿入されたり削除されたりと、意味のない変更が差分として上がってしまう問題がありました。
意味のない差分はレビュワーにとっても負担になりますし、これらを強制することでこのような負担を軽減できないかと考えました。

解決手段

上記の問題は ESLintRuboCop といった言語特化型の lint ツールでは対応していないファイルでも見られるため、単純に様々な lint ツールを導入するといった方法では完全に解決することが困難です。
そのため、今回はこれらの lint ツールを使いつつ、また全てのファイルに対してコーディングスタイルを統一できるような手段について検討しました。

EditorConfig

まず、前述の問題を解決するような、言語を問わずコーディングスタイルを統一するツールとして、EditorConfig というツールが存在します。

EditorConfig は、開発者ごとのエディタや IDE の設定によってコーディングスタイルに差異が生まれてしまう問題を解決しようとしているツールの 1 つです。
.editorconfig という ini 形式のファイルをプロジェクトに配置し、それを各エディタや IDE が参照することで、設定内容に従って自動的にコーディングスタイルが統一されるようになります。

各自が自分の利用しているエディタや IDE に EditorConfig プラグインを入れる必要があるため少し抵抗になりますが、それを上回るメリットが存在すると感じたため、今回は開発チーム全体にプラグインを導入してもらうことにしました。

.editorconfig は以下のように設定し、各プロジェクトのルートディレクトリに配置しました。

root = true

[*]
end_of_line = lf
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

Filelint

EditorConfig を用いることで新規プログラムではコーディングスタイルが統一されますが、既存プログラムに対して適用することはできません。
そこで、EditorConfig のように言語を問わずコーディングスタイルを統一できる Golang 製の lint ツールを作成しました。

github.com

https://user-images.githubusercontent.com/11088009/27952943-16962632-6345-11e7-896f-f6d43aff084b.gif

Filelint では、以下の一般的なコーディングルールを提供しています。

ルール名 内容
indent (実験的なため不安定) ハードタブ (タブ文字) でのインデントあるいはソフトタブ (半角スペース) でのインデントを強制する
linebreak 改行コードを LF または CRLF に強制する
first-newline ファイルの最初に改行を必須にする / しない
final-newline ファイルの末尾に改行を必須にする / しない
no-bom UTF-8 BOM の挿入を禁止する
no-eol-space 各行の末尾が半角スペースあるいはタブ文字で終わることを禁止する

インデントの強制に関しては、Filelint は構文を解析していないため、うまくインデントが解析できないことがあります。 そのため、ESLint や RuboCop といった構文を解析する lint ツールを併用することを推奨しています。

使い方

ESLint 風のコマンドで、特定のディレクトリやファイルに対してコーディングスタイルに関する lint が実行できます。

# 現在のディレクトリ下のテキストファイルに再帰的に lint をかける
filelint
# README.md に lint をかける
filelint README.md
# 現在のディレクトリ下のテキストファイルに再帰的に lint をかけて自動修正する
filelint --fix
# some/dir 下のテキストファイルに再帰的に lint をかけて自動修正する
filelint some/dir --fix

以下のような YAML 形式の設定ファイルを .filelint.yml というファイル名でプロジェクトに配置することで、各 lint ルールの設定の他、特定のファイルを除外したり、特定のファイルには別のルールを適用することができます。

files:
  exclude:
    - '**/*.min.css'
    - '**/*.min.js'
targets:
  - patterns: ['**/*']
    rules:
      indent:
        enforce: false
      linebreak:
        enforce: true
        style: lf
      first-newline:
        enforce: true
        num: 0
      final-newline:
        enforce: true
        num: 1
      no-bom:
        enforce: true
      no-eol-space:
        enforce: true
  - patterns: ['**/*.md']
    rules:
      no-eol-space:
        enforce: false

インストール方法

GitHub Releases で各 OS 向けのバイナリを含んだパッケージを公開しています。 ここからダウンロードして解凍し、 $PATH に通されているディレクトリ下にバイナリファイルを配置してください。

Golang の環境がある場合は以下のコマンドでインストールできます。

go get github.com/synchro-food/filelint

実際の運用

前述の .filelint.yml はあるプロジェクトで利用しているもので、これを基本として各プロジェクトに配置し、ベースブランチにて filelint --fix コマンドで自動修正しました。

また、すでにベースブランチから切られている開発中ブランチに関しても、コンフリクトを避けるために以下の手順で各開発者の手元で修正して頂きました。

# 適用するブランチ (開発中のブランチ) に移動する
git checkout <branch name>

# .filelint.yml が追加されたコミットを現在のブランチに適用する
git cherry-pick $(git log --reverse origin/master --format="%H" -- .filelint.yml | head -1)

# Filelint を実行する
# (一時的に --rule オプションを用いてインデント修正をしています)
filelint --rule 'indent: {enforce: true, size: 2, style: soft}' --fix $(git diff $(git merge-base origin/master HEAD) --diff-filter=d --name-only)

今後継続的にコーディングスタイルを統一させるため、CI を用いた自動コードレビューシステムを現在構築しています。

最後に

Filelint は社内初の OSS プロダクトです。 今後 OSS 活動を推進していきたいと思っていますので、バグ報告や機能追加要望、PR などお待ちしております。

シンクロ・フードでは OSS 活動に興味のあるエンジニアも募集しています。 ご興味のある方は以下よりご連絡ください。