♥️

チーム開発に参加する時の心得を持つ

6 min read 1

チーム開発に参加前に...

これから本格的にチーム開発に参加するであろう段階にある自分に、思いつく限りの心得を示しておきます。実際に参加してみて気づいたことがあれば、ここに追記していくのも良さそう。

より良いコードを書くために

読みやすいコードを書く

読みやすいコードを書くメリットは主に3つの利点がある。

  • 保守性が高まる
  • フレキシブルな開発体制が構築できる
  • 個人・組織の生産性が上がる

これらを実現するために、機能の実装中もしくは実装後に、エンジニアはリファクタリングと呼ばれるソースコードの書き換えを行う。

リファクタリング

実装した機能に影響を与えずに、ソースコードを読みやすい状態に改善すること。リファクタリングをすることで、開発に携わるすべての人にとって、実装されているコードの理解や修正が容易になる。

上記の利点を感じるために、一度読みやすいコードと読みにくいコードの見比べてみる。
以下のAとBのコードを読み、どちらのコードが読みやすいコードなのか考えてみる。(AとBの処理内容は同じ)

A

const getYear = (year) => {
  if (year % 4 == 0){ 
    if (year % 100 == 0 && year % 400 != 0){
      console.log(`${year}年は閏年ではありません`);
    } else {
      console.log(`${year}年は閏年です`);
    };
  }else {
    console.log(`${year}年は閏年ではありません`);
  };
};

B

// yearが閏年かどうかを判定する関数
const isLeapYear? = (year) => {
  if (year % 400 == 0) {
      // 400で割り切れる年は閏年
      return true;
  };
  if (year % 100 == 0) {
      // 400で割り切れず、100で割り切れる年は平年
      return false;
  };
  if (year % 4 == 0) {
      // 400でも100でも割り切れず、4で割り切れる年は閏年
      return true;
  };

  // それ以外は平年
  return false;
};

if (isLeapYear?(year)) {
  console.log(`${year}年は閏年です`);
} else {
  console.log(`${year}年は閏年ではありません`);
};

読みやすいコードはB。主な理由として以下の3点が挙げられる。

  • 変数名(関数名)が他者を考慮した命名になっている
  • 複雑なロジックがなく、読みやすい
  • コメントが記載されており、全体像を把握しながらコードを読むことができる

これから、上記の「読みやすいとされる理由」の詳細を順番に追って確認。

変数と関数の命名はわかりやすく

これまでは、自分で変数や関数を定義する際は特に意識していなかったであろうことがある。それは「自分以外の人を考慮した命名をすること」。これを徹底することで、自分自身はもちろん、一緒に開発しているチームメンバーの負担を大幅に減らすことができる。

変数を命名する時は、抽象的な名前は避けて、具体的な名前をつけることを意識する。抽象的な名前にすると、読み手は変数に代入されている値の役割を把握できず、理解するのに多くの時間を要してしまう。

関数名は動詞+名詞を意識してみる。

ロジックの単純化

実装の便宜上、ネストされているコードの中にネストの記述をする場合がある。だが、この書き方をしてしまうと、非常に読みづらくなってしまう。以下のコードを例にして、順を追ってリファクタリングを行ってみる。

const getYear = (year) => {
  if (year % 4 == 0){ 
    if (year % 100 == 0 && year % 400 != 0){
      console.log(`${year}年は閏年ではありません`);
    } else {
      console.log(`${year}年は閏年です`);
    };
  }else {
    console.log(`${year}年は閏年ではありません`);
  };
};

コードが読みにくくなってしまう要因として、以下のようにif文の中にif文を記述してしまっている事が確認できる。

if (year % 4 == 0){ 
  if (year % 100 == 0 && year % 400 != 0){
    console.log(`${year}年は閏年ではありません`);
  } else {
    console.log(`${year}年は閏年です`);
  };
}else {
  console.log(`${year}年は閏年ではありません`);
};

これらを解消するためは、何を意識すれば良いのか?

ネストの中のネストを避ける

ネストの中にネストを作成してしまうと、構造が複雑になるので、どうしても読みづらいコードになりがち。読みづらさを解消するためには、、

  • 早い段階で戻り値を返し、処理自体を小分けにする
  • 論理演算子を用いる

今回の例はは論理演算子を用いて、if文の処理を書き換えてみる。書き換えると以下のような記述になります。

const getYear = (year) => {
  if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
    console.log(`${year}年は閏年です`);
  } else {
    console.log(`${year}年は閏年ではありません`);
  };
};

論理演算子を用いることで、if文の中にif文を記述することを回避できた!

条件式を簡潔にする

if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) {
    // 処理
};

条件式の記述が長い場合は、一目で処理の内容を把握するのが困難になる。この読みづらさを解消するために、条件式はあらかじめ関数化しておき、その関数を呼び出す記述様式にする。

const isLeapYear? = (year) => {
    return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
};

if (isLeapYear?(year)) {
  console.log(`${year}年は閏年です`);
} else {
  console.log(`${year}年は閏年ではありません`);
};

このように関数化することで、非常に読みやすい条件式になった!

上記のコードでは、isLeapYear?関数の戻り値がtrueの場合はconsole.log(year年は閏年です)が実行され、指定の文字列がConsoleに出力される。逆に、isLeapYear?関数の戻り値がfalseの場合はconsole.log(year年は閏年ではありません)が実行される。

また、関数の名前に「?」をつけることで、このメソッドの戻り値が「true/false」のいずれかである(真偽値である)ことを示す事がdキル。読み手はメソッドの挙動をある程度把握できるので、理解しやすくなります。

複雑な条件を分解してみる

先ほどのリファクタリングによって、条件式は非常に読みやすい記述になった。しかし、依然として関数化した処理はまだ読みづらさが残っている。if文の数を減らすために、1行で記述してしまったので、処理の内容を把握するのに少し時間がかかってしまう。

const isLeapYear? = (year) => {
  return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
};

分解してみると、、、

const isLeapYear? = (year) => {
  if (year % 400 == 0) {
      return true;
  };
  if (year % 100 == 0) {
      return false;
  };
  if (year % 4 ==0) {
      return true;
  };
  return false;
};

if (isLeapYear?(year)) {
  console.log(`${year}年は閏年です`);
} else {
  console.log(`${year}年は閏年ではありません`);
};

このように分解することで、条件分岐がより簡潔に!

ここまでのリファクタリングの流れをまとめておくと、、、

  • if文の中にif文を記述するのはなるべく避ける → 論理演算子を使用
  • 条件式を短くする → 条件式を関数化する
  • 複雑な条件式を分解する → 複数のif文に分ける

何より大切なのは、自分以外の人が読みやすいように意識してコードを書く必要があることを認識しておくこと。

コメントで正確な情報を伝える

コメントの目的は実装者の意図を読み手に伝えること。つまり、自分以外の人にソースコードの内容を具体的に伝えることがコメントの役割です。

冒頭に紹介した例をもう一度確認してみる、、、

// yearが閏年かどうかを判定する関数
const isLeapYear? = (year) => {
  if (year % 400 == 0) {
      // 400で割り切れる年は閏年
      return true;
  };
  if (year % 100 == 0) {
      // 400で割り切れず、100で割り切れる年は平年
      return false;
  };
  if (year % 4 == 0) {
      // 400でも100でも割り切れず、4で割り切れる年は閏年
      return true;
  };

  // それ以外は平年
  return false;
};

if (isLeapYear?(year)) {
  console.log(`${year}年は閏年です`);
} else {
  console.log(`${year}年は閏年ではありません`);
};

全体的にコードが長くても、コメントがあることでコードの流れを理解しやすくなっている。
これからの実装では、以下のようなときにコメントを残すようにする。

複雑なロジックを記述した場合は、処理の概要と自分の考えをコメントで残す。

なぜそのような処理をしているのかをコメントで残す。

コメントを残すべきでない場面

「コメントを残すべき場面」とは逆に、可読性の観点から考えて「コメントを残すべきでない場面」というのも存在する。

例えば、、、

コメントの補足のコメント

コメントは1文で済ませる。1文目のコメントに対して補足をしてしまうと、かえって読みづらくなってしまう。

ひどいコードを補う「補助的コメント」

処理が複雑で1つのコメントでは補えない場合がある。そのような場合は、コメントを追加するのではなく、複雑なソースコードを修正する必要がある。

Reference

https://www.amazon.co.jp/リーダブルコード-―より良いコードを書くためのシンプルで実践的なテクニック-Theory-practice-Boswell/dp/4873115655

Discussion

個人の感想なのですが

ifで分割する前の段階のほうがわかりやすかった。。

多分うるう年を判定するための条件ってなんだろう?

っていう観点でソースを読んだときに

400で割れたら閏年で
400で割れなくて100で割れたら閏年じゃなくて
400でも100でも割り切れず、4で割り切れる年は閏年で
全部の条件に当てはまらなかったら閏年じゃないのか。。。。

true→false→true→false
っていう並びになってるのが原因だと思います

リーダブルコード読んでないからわからないんですが
ifを連ねてすぐに処理を返そうとしてるということはガード節を作ろうとしてるんですかね?

もしそうならその時のifのリターンは同じになるのが読みやすいです

  if(処理を実行する権限がない){
    return false;
  }
  if(設定ファイルを読み込めない){
    return false;
  }
  if(DBにつながらない){
    return false;
  }
  return true;

みたいな

https://www.nao.ac.jp/faq/a0306.html#:~:text=(2)(1)の,ますので、平年となります。

によると閏年の条件は

(1)西暦年号が4で割り切れる年をうるう年とする。
(2)(1)の例外として、西暦年号が100で割り切れて400で割り切れない年は平年とする。

なのでガード節で書くとすれば

  if ( year % 4 !== 0 ) {
    // 4で割り切れない年は平年
    return false;
  }
  if ( year % 100 === 0 && year % 400 !== 0 ) {
    // 4でも100でも割り切れるが400で割り切れない年は平年
    return false;
  }
  // 上記以外は閏年
  return true;

位でいいんじゃないでしょうか?

ログインするとコメントできます