こんにちは、フロントエンドエンジニアの佐野です。
今回は、社内のコーディングルールを作成してみました。
どのように考えて、どのようなルールを作ったのか、順に紹介していきたいと思います。
目次
コーディング規約を決める目的
コーディング規約を決めることは大切とよく言われますが、なぜ大切なのでしょうか?
どういう目的があり、どういうメリットがあるのか?
それを理解し、チーム内で認識の共有をすることが、良いコーディングルール作成の第一歩だと思い、下記のようにまとめてみました。
目的の1つ目は、コードの属人化を防ぎ、コードの解析を容易にすること
です。
これによって引き継ぎを楽にしたり、不具合修正や機能拡張にかかる時間の短縮することが目的になります。
そしてこれらの目的を言い換えると、可読性、保守性を高めること
と言い換えることができると考えました。
2つ目は、一定の品質を担保すること
です。
1つの共通したルールに則ってコーディングすることで、チームメンバーの誰が実装するかによって品質が著しく変わるということを防ぐことができます。
ということで、今回コーディング規約を決める目的は以下の2つです。
- 可読性、保守性を高めること
- 一定の品質を担保すること
目的を達成するための項目を決める
目的は整理できました。では、目的を達成するためにどのようなルールの項目を決めればいいのでしょうか?
それを紐解くために、目的をさらに深掘りしてみましょう
可読性、保守性を高めるとは?
可読性とはコードの見やすさ、保守性とは機能の拡張や不具合修正の容易さです。
この2つを高めるということはつまり、みんなが読みやすいコードを書く
ということだと考えます。
では、みんなが読みやすいコードとはどのようなコードなのでしょうか?さらに深掘りしていきましょう。
コードの処理がわかりやすい
関数名、変数名を見て処理の内容が把握しやすかったり、適切なコメントが記述されていればコードの処理がわかりやすく、みんなが読みやすいコードになると思います。
なので以下の項目を、ルールの項目に入れようと思います。
- 変数/関数名の命名規則
- 変数/関数名の省略形について
- コメントの配置ルール
コードの場所を見つけやすい
importのファイルや、cssのプロパティが多くなると、修正する内容がわかっても対象のファイルやプロパティを探すのに時間がかかってしまうことがあります。
最近ではエディタも進化して、補佐してくれるプラグインもあるかとは思いますが、考慮することに損はないでしょう。
なので以下の項目を、ルールの項目に入れようと思います。
- importの順番
- cssの記述順序
そもそもの視認性、可視性
視認性や可視性に気を配らなくてはいけないのはデザイナーだけではありません。
エディタ上でソースを見た時に、記述されたコードが見やすいように、プログラマーも気を配らなければいけません。
なので以下の項目を、ルールの項目に入れようと思います。
- インデント
- 空行/空白の配置ルール
一定の品質を担保するためには?
一定の品質を担保するためには、不要な記述や非推奨な記述をしないことや、できるだけ計算量の小さい記述をすることが考えられます。
今までが命名や記述についてのルールだったのに対して、品質担保の目的を達成するためには、アルゴリズムなどその他のルールを設定する必要があるかと思います。
なので以下の項目を、ルールの項目に入れようと思います。
- 推奨事項の設定
- 禁止事項の設定
さて、先述した目的を達成するためのルールの項目が決まりました。
いよいよ、各項目のルールを決めていきましょう
各項目のルールを作成する
変数/定数/関数名の命名規則
変数
- キャメルケースで記述する
- ex.) animationType
- 型がbool値の場合、
is(〜である)/can(〜ができる)/has(〜を持っている)
のいずれかを接頭語につける- ex.) isSpDevice(SPデバイスである)、canSubmit(送信できる)、hasUserId(ユーザーIDを持っている)
- 名前の短さよりも分かりやすさを優先して命名する、名前が長くなることを気にしすぎない
- エディタでの入力補完とかを頼る
- 配列やオブジェクトなど、複数のデータがある場合、変数名は複数形にする
- ex.) deviceLabels = [‘sp’[ ], ‘pc’]
- For や While ループでのインデックス(カウンタ)として使う変数は、i, j, k を使う。その際、一番外側のループから順に i, j, k を使用する。
定数
- 全て大文字でスネークケースで記述する
- ex.) LAYOUT_TYPE = 1
関数
- キャメルケースで記述する
- ex.) resetImageSize
- 名前の短さよりも分かりやすさを優先して命名する、名前が長くなることを気にしすぎない
- エディタでの入力補完とかを頼る
- 値を返す関数の場合、返ってくる値が明確な名前にする。また、その返り値を格納する変数は関数に合わせる
- ex.) userID = getUserId(userName)
変数/関数名の省略形について
単語の省略
決められた単語のみ、省略形を使用する。それ以外の単語は原則として省略しない。
- introduction → intro
- information → info
- navigation → nav
- thumbnail → thumb
- personal computer → pc
- smart phone → sp
カラーコードの省略
- 英字は小文字で統一
- 省略できる場合は省略する。
○:#fff, #f00, #abc123
×:#ffffff, #F00, #CCCCCC
コメントの配置ルール
アノテーションコメント
記述 | 意味 |
---|---|
TODO: | あとで追加、修正するべき機能がある。 |
FIXME: | 既知の不具合があるコード。修正が必要。 |
HACK: | あまりきれいじゃないコード。リファクタリングが必要。 |
NOTE: | なぜ、こうなったという情報を残す。 |
WARNING: | 注意が必要。 |
コメントの文言
コードを見ればわかるようなコメントはしない。逆説、コードからパッとわからない内容をコメントに書く。
// typeが動画の時 ←BAD if文を見ればわかる
if (type === 'video') {
}
// ノードがbody以外の時、表示する ←BAD if文を見ればわかる
// オーバーレイではない時、表示する ←GOOD ノードがbody以外の時 = オーバーレイではない時という事象を、コメントで補完している
if (this.targetElm.nodeName.toLowerCase() !== 'body') {
this.targetElm.style.display = 'block';
}
importの順番
上から順に
reduxやmaterial-uiなどシステム周り > import元のファイルパス毎にまとめる > style系 > 画像や映像系 > 定義系
import React from 'react'
import { Field, reduxForm } from 'redux-form'
import { connect } from 'react-redux'
import _ from 'lodash'
import { withStyles } from '@material-ui/core/styles'
import * as Validator from '../common/Validations'
// ↑システム周り
import TabMenu from '../common/molecules/TabMenu'
import Accordion from '../common/molecules/Accordion'
import ColorPickerBar from '../common/molecules/ColorPickerBar'
// ↑moleculesでまとめる
import Button from '../common/atoms/Button'
import Icon from '../common/atoms/Icon'
import Paper from '../common/atoms/Paper'
import H2 from '../common/atoms/H2'
// ↑atomsでまとめる
import palette from '../../assets/styles/variables/palette'
import typography from '../../assets/styles/variables/typography'
import utility from '../../assets/styles/utility'
// ↑style系
import imageIcon from '../../assets/images/icon.svg'
import imageLogo from '../../assets/images/logo.svg'
// ↑画像や映像系
import {
DISABLE,
ENABLE,
} from '../../services/define'
// ↑定義系
cssの記述順序
上から順に
width > height > margin > padding > position > 位置系を時計回り > アルファベット順
例外としてdisplay:flex / gridに関するプロパティだけはアルファベット順を無視して1箇所に固める
.hoge {
width: 100px;
height: 100px;
margin: 10px auto;
padding: 0 20px;
position: relative;
top: 0;
right: 0;
bottom: 0;
left: 0;
border: solid 1px #fff;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
z-index: 2;
}
インデント
Reactを使うことが多いので、ESLintの設定ファイルから抜粋して半角スペース2字
参考
空行/空白の配置ルール
空行
近い処理やメソッドは、場所をまとめて記述。まとめの区切りは空行を入れる
componentDidMount() {
}
componentDidUpdate(prevProps) {
}
componentWillUnmount() {
const hoge
const fuga
// 空行
return hoge + fuga
}
// 空行
renderRadioGroup = () => {
}
renderBannerPreview = () => {
}
空白
- 演算子の両端には空白を入れる
- ifやforと条件文の間には空白を入れる
- 引数などの,(カンマ)の後には空白を入れる
hoge = fuga
hoge = 1 + 2
hogehoge = (hoge, fuga) => {
if (hoge === fuga) {
}
}
推奨事項の設定
実装時に推奨とする項目、理由がある場合は違反しても良いが理由をコメントに残すこと
- ネストは最大でも3階層までにする(超える場合は、アルゴリズムを再考する)
- ローカル変数の宣言は原則、メソッドの先頭で行う
- マジックナンバーは原則使用しない マジックナンバーとは
index = 0
など、初期化のための値は例外とする
禁止事項の設定
原則として、いかなる理由があっても違反してはいけない項目
- console.logなど、デバッグに使用したコードをマージ時に残さない
- 不要なコメントアウトをマージ時に残さない
- ネットで検索したサンプルプログラムのコードをむやみにコピーして使わない。コピーすること自体は禁止しないが、動作やアルゴリズムに問題がないかよく確認する
まとめ
今回は、コーディングルールを1から作成してみました。
このルールが最良なんてことはありませんし、運用しながら随時アップデートは絶対に必要だと思います。
しかし、ちゃんと目的を考え、チーム内で共有し、それを土台にルールを作成することで、なぜルールを作らなければならないのか?なぜルールを守らなければならないのか?という認識を合わせることは大切だと思います。