〈完全なプログラミング〉を目指す会 2020

第四回 ドキュメント

yewton

ドキュメント

〈完全なプログラミング〉を目指す会 2020

の前に

〈完全なプログラミング〉を目指す会 2020

復習

〈完全なプログラミング〉を目指す会 2020

〈完全なプログラミング〉

とは?

〈完全なプログラミング〉を目指す会 2020

「読めば分かるコードを書く」
「必要十分なドキュメントを書く」

〈完全なプログラミング〉を目指す会 2020

余計なモノを
一切必要としない

ソフトウェアづくり

〈完全なプログラミング〉を目指す会 2020

その心は?

〈完全なプログラミング〉を目指す会 2020

コードは圧倒的に
人間によって読まれる

〈完全なプログラミング〉を目指す会 2020

故に〈完全なプログラミング〉は
圧倒的に はやい

〈完全なプログラミング〉を目指す会 2020

さらに

〈完全なプログラミング〉を目指す会 2020

品質の改善は
コストを削減する

〈完全なプログラミング〉を目指す会 2020

故に〈完全なプログラミング〉は
圧倒的に やすい

〈完全なプログラミング〉を目指す会 2020

はやい
やすい

〈完全なプログラミング〉を目指す会 2020

うまい
😋

〈完全なプログラミング〉を目指す会 2020

命名は重要か?

〈完全なプログラミング〉を目指す会 2020

名前重要

〈完全なプログラミング〉を目指す会 2020

命名 Tips

  • それが何をするのか、何を表すのか、まずは 言葉で説明してみる
  • 解読 の必要が生じるような命名を避け、 読めば分かる 名前をつけよう
  • 悩んだら ライブラリをお手本にしよう
〈完全なプログラミング〉を目指す会 2020

今日持ち帰ってほしいこと

  1. 効果的な コメント の書き方
  2. 効果的な コミットメッセージ の書き方
  3. 残すべき ドキュメント とは何か
〈完全なプログラミング〉を目指す会 2020

効果的
コメント

〈完全なプログラミング〉を目指す会 2020
r = n / 2;
while ( abs( r - (n/r) ) > t ) {
  r = 0.5 * ( r + (n/r) );
}
System.out.println( "r = " + r );
〈完全なプログラミング〉を目指す会 2020
// ニュートン法で n の平方根の近似値を求める
r = n / 2;
while ( abs( r - (n/r) ) > t ) {
  r = 0.5 * ( r + (n/r) );
}
System.out.println( "r = " + r );
〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020
private double SquareRootApproximation(n) {
  r = n / 2;
  while ( abs( r - (n/r) ) > t ) {
    r = 0.5 * ( r + (n/r) );
  }
  return r;
}
System.out.println( "r = " + SquareRootApproximation(r) );
〈完全なプログラミング〉を目指す会 2020

理想

〈完全なプログラミング〉を目指す会 2020

コメントが全く無い
コード

〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

コメントが全く
必要無い
コード

〈完全なプログラミング〉を目指す会 2020

読めば分かる コード

〈完全なプログラミング〉を目指す会 2020

適切な
モジュール名
クラス名
メソッド名

〈完全なプログラミング〉を目指す会 2020

「コードが仕様」は
正義

〈完全なプログラミング〉を目指す会 2020

現実

〈完全なプログラミング〉を目指す会 2020

制約

〈完全なプログラミング〉を目指す会 2020

歴史的経緯
時間的制約
リソース制約

〈完全なプログラミング〉を目指す会 2020

😭

〈完全なプログラミング〉を目指す会 2020

コードでは表現できない
筆者の気持ち・意図
を残すためにコメントを使う

〈完全なプログラミング〉を目指す会 2020

ポイント

〈完全なプログラミング〉を目指す会 2020

コメントの種類

〈完全なプログラミング〉を目指す会 2020

1. コードの繰り返し

〈完全なプログラミング〉を目指す会 2020
// productを"base"に設定する
long product = base;
// 2から"num"までループする
for (int i = 2; i < base; i++) {
  // "base"と"product"を掛ける
  product = product * base;
}
// 標準出力に product を表示する
System.out.println(String.format("Product = %d", product));
〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020
  • 読み手に追加情報を一切提供せず、 読む量を増やすだけ
  • こんなコメントは止めましょう
〈完全なプログラミング〉を目指す会 2020

2. コードの説明

〈完全なプログラミング〉を目指す会 2020
// VERY IMPORTANT NOTE:
// このクラスのコンストラクタにはUiPublicationへの参照を渡す。
// UiPublicationオブジェクトはDataPublicationオブジェクトよりも前に
// 破棄してはならない。もしそうすれば、
// DatabasePublicationオブジェクトはプログラムをクラッシュさせる原因となる。
〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

悪いコードを説明してはならない。書き直せ。

— プログラム書法 第2版 Brian W. Kernighan and P. J. Plauger

〈完全なプログラミング〉を目指す会 2020

■ クラスのユーザーについてあれこれ推測しない

クラスは、クラスのインターフェイスが示唆する規約に従って設計し、実装すべきである。
インターフェイスについて明記されていること以外は、インターフェイスがどのように使用されるか
(または、されないか)について憶測すべきでない。

— CODE COMPLETE 6.2.2 良いカプセル化

〈完全なプログラミング〉を目指す会 2020

複雑 なコード
トリッキー なコード
細心の注意が必要 なコード

〈完全なプログラミング〉を目指す会 2020

"凝った" コードの
コメントは怪しい

〈完全なプログラミング〉を目指す会 2020

コードが複雑すぎて説明の必要がある?

〈完全なプログラミング〉を目指す会 2020

Keep It
Simple,
Sxxx

〈完全なプログラミング〉を目指す会 2020

コメント追加の前に
コードを改良

〈完全なプログラミング〉を目指す会 2020

3. コードの目印(マーカー)

    // FIXME: Additional parameters are required in TS3.6, but ignored in 3.5.
    // Remove the any cast once google3 is fully on TS3.6.

https://github.com/angular/angular/blob/f7815cf96defa6b19fff482824c5997f03fd78ea/packages/compiler-cli/src/ngtsc/util/src/typescript.ts#L128-L129

  • TODO
  • FIXME
  • etc,
〈完全なプログラミング〉を目指す会 2020

チーム内で
認識を合わせる

〈完全なプログラミング〉を目指す会 2020

言い訳のようにつけられた
TODOFIXME

〈完全なプログラミング〉を目指す会 2020

いつまでも
残っていませんか

〈完全なプログラミング〉を目指す会 2020

いつか直す

〈完全なプログラミング〉を目指す会 2020

その "いつか"

〈完全なプログラミング〉を目指す会 2020

永遠に
来ない

〈完全なプログラミング〉を目指す会 2020

4. コードの概要

〈完全なプログラミング〉を目指す会 2020

コードを要約するコメント

〈完全なプログラミング〉を目指す会 2020

数行のコードを
1つか2つの文で表現

〈完全なプログラミング〉を目指す会 2020

コードよりも 素早く読める

〈完全なプログラミング〉を目指す会 2020

特に 作成者以外の誰か
の役に立つ

〈完全なプログラミング〉を目指す会 2020

5. コードの意図の説明

〈完全なプログラミング〉を目指す会 2020

解決領域ではなく
問題領域 の説明

〈完全なプログラミング〉を目指す会 2020
// createdAt が LocalDate.now() の Entry を filter して 降順ソートする
〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020
// 今日のランキングを作成する
〈完全なプログラミング〉を目指す会 2020

パフォーマンスを重視して
直感に反する 実装になっている場合

〈完全なプログラミング〉を目指す会 2020
/*
 * 対象データについてはバイナリサーチよりもボイヤー-ムーア法の方が
 * 速いことが判明したため、当初は文字列検索技術を適用するには
 * 不適当と考えられていたが、より高速だが複雑なこの方法を採用した。
 */
〈完全なプログラミング〉を目指す会 2020

必要ならいくらでも
長くなっていい

〈完全なプログラミング〉を目指す会 2020

6. コードでは表せない情報

〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

まとめると

Code Tells You How
Comments Tell You Why
Code Tells You How, Comments Tell You Why

〈完全なプログラミング〉を目指す会 2020

効果的な
コミットメッセージ

〈完全なプログラミング〉を目指す会 2020

人は 何故
コミットメッセージを書くのか

〈完全なプログラミング〉を目指す会 2020

短期的には: レビューのため

〈完全なプログラミング〉を目指す会 2020

どのように問題を分割して解決したのか?

例: まずモデルを作り、サービスを作り、それらを利用するバッチを作り、最後に定期実行の設定をした

〈完全なプログラミング〉を目指す会 2020

コードには残らない
自分なりの問題の捉え方
アプローチ方法
を共有する

〈完全なプログラミング〉を目指す会 2020

中長期的には: 問題解決のため

〈完全なプログラミング〉を目指す会 2020

1ヶ月後、半年後…

〈完全なプログラミング〉を目指す会 2020

障害だ!!

〈完全なプログラミング〉を目指す会 2020

なんでこうなってるんだ?

〈完全なプログラミング〉を目指す会 2020

直していいのか?

〈完全なプログラミング〉を目指す会 2020

頼りになるのは
コミットメッセージ

〈完全なプログラミング〉を目指す会 2020
commit 6e32f28b06c128840f233de553c0885e91da2b87 (HEAD -> master, origin/master)
Author: John Doe <john@doe.com>
Date:   Fri Apr 24 06:22:36 2020 +0900

    レビュー指摘反映
〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

変更内容は diff を見れば分かる

〈完全なプログラミング〉を目指す会 2020

背景気持ち
は分からない

〈完全なプログラミング〉を目指す会 2020

コード中のコメントには
なぜそう書いてあるか
を書く

〈完全なプログラミング〉を目指す会 2020

コミットメッセージには
なぜ書かなければならなかったのか
を書く

〈完全なプログラミング〉を目指す会 2020

Re-establishing the context of a piece of code is wasteful.
We can't avoid it completely, so our efforts should go to reducing it [as much] as possible.

Commit messages can do exactly that

〈完全なプログラミング〉を目指す会 2020

and as a result, a commit message shows whether a developer is a good collaborator.
Who-T: On commit messages

〈完全なプログラミング〉を目指す会 2020

メッセージの構成

  1. このコミットを適用するとどうなるか を1行で言うと?
  2. なぜこの変更が為されたのか?その他補足情報
  3. チケットやissueなど、関係するリソースへのリンク
〈完全なプログラミング〉を目指す会 2020

feat(api): 特定のアカウントのクリップ数を取得出来るようにする

* パフォーマンス要件は一旦考えず、動作するものを提供することを優先する
* この後パフォーマンスを測定し、必要があれば改善する

fixes #999
〈完全なプログラミング〉を目指す会 2020
  • 説明部分は箇条書きでも普通の文章でも 👌
  • リンク部分は Fix #99 みたいなのでも 👌
    • コンテキストの再構築に役立つ
〈完全なプログラミング〉を目指す会 2020

(FYI)Spring Boot もこのような形式

Restore support for TransactionAwareCacheDecorator

This commit makes sure to unwrap any transaction aware cache before
collecting metrics for them.

Closes gh-13861

https://github.com/spring-projects/spring-boot/commit/a25b6bd473fb2e2071e377f9bfce8b60b1900c95

〈完全なプログラミング〉を目指す会 2020

(FYI)Angular もこのような形式

feat(language-service): improve non-callable error message (#35271)

This commit improves the context of a non-callable function error
message by providing the affected call target and its non-callable type.

PR Close #35271

https://github.com/angular/angular/commit/acc483e2ebe7d8207fe183d8ec1424c549863184

〈完全なプログラミング〉を目指す会 2020

ありがちで
やめろ!
と思うもの

〈完全なプログラミング〉を目指す会 2020
レビュー対応
〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

レビューで指摘されたから
直したのかどうかは
どうでもいい

〈完全なプログラミング〉を目指す会 2020

直すべきだと思ったなら
その気持ち を書け

最悪その Pull Request コメントへのリンクは貼れ

〈完全なプログラミング〉を目指す会 2020
微修正
〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

知るか

〈完全なプログラミング〉を目指す会 2020

まとめろ

〈完全なプログラミング〉を目指す会 2020
もろもろ修正
〈完全なプログラミング〉を目指す会 2020
〈完全なプログラミング〉を目指す会 2020

まとめるな

〈完全なプログラミング〉を目指す会 2020

分けろ

〈完全なプログラミング〉を目指す会 2020

ありがちだけど
別にいいと思うもの

〈完全なプログラミング〉を目指す会 2020
コード整理をする
コードフォーマッタをかける
〈完全なプログラミング〉を目指す会 2020

読まなくていいことが分かるので 👌

〈完全なプログラミング〉を目指す会 2020
コメントを追加する
〈完全なプログラミング〉を目指す会 2020

実装にまとまってた方が望ましいけれど、
読まなくてよさそうなことが分かるので 👌

〈完全なプログラミング〉を目指す会 2020
typoを修正する
〈完全なプログラミング〉を目指す会 2020

読まなくていいことが分かるので 👌

〈完全なプログラミング〉を目指す会 2020

ただし

〈完全なプログラミング〉を目指す会 2020

同一 Pull Request 内なら
(変更の文脈が同じなら)
まとめてほしい

〈完全なプログラミング〉を目指す会 2020

ケアレスミス直したとか
考え直して変えたとか
人に言われて変えたとか

〈完全なプログラミング〉を目指す会 2020

一ヶ月後、一年後には
不要なコンテキスト

〈完全なプログラミング〉を目指す会 2020

そうは言ってもさぁ…

〈完全なプログラミング〉を目指す会 2020

開発中にいちいち
コミットメッセージ
熟考してらんないよ

〈完全なプログラミング〉を目指す会 2020

しなくていい

〈完全なプログラミング〉を目指す会 2020

レビュー前に
整理すればいい

〈完全なプログラミング〉を目指す会 2020

開発中のメッセージの例

〈完全なプログラミング〉を目指す会 2020
コードフォーマットかけた
typo 修正
修正漏れ
仮実装
fixup! 仮実装
fixup! 仮実装
デバッグログ追加
修正
なぜか直ってくれ
今度こそ頼む
specついk
〈完全なプログラミング〉を目指す会 2020

レビュー提出時

〈完全なプログラミング〉を目指す会 2020
コード整理をする
なんとかモデルを追加する ...
なんとかサービスを追加する ...
なんとかの処理が出来るようにする ...
なんとか処理を定期的に実行するようにする ...
〈完全なプログラミング〉を目指す会 2020

使える道具

〈完全なプログラミング〉を目指す会 2020

git rebase -i

〈完全なプログラミング〉を目指す会 2020

git commit --fixup

When the commit log message begins with "squash! …​" (or "fixup! …​"), and there is already a commit in the todo list that matches the same ...,
automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified,
and change the action of the moved commit from pick to squash (or fixup).

Git - git-rebase Documentation

〈完全なプログラミング〉を目指す会 2020

git commit --fixup

  • git rebaseautosquah オプションと組み合わせて使う
  • 素で fixup 対象のコミットハッシュ指定するのはしんどいので、
    fzf なり Magit なりを使いましょう
〈完全なプログラミング〉を目指す会 2020

git push --force-with-lease

しかし、 --force には余り知られていない類似のオプションがあります。
これを利用すると強制更新によるリポジトリの破壊を部分的に保護してくれます。これが --force-with-lease です。
この --force-with-lease は誰もブランチのアップストリームを変更していないなど、期待された状況にならない限りはブランチへの更新を拒否します。

-force は有害だという考え; git の -force-with-lease を理解する - Atlassian Japan 公式ブログ | アトラシアン株式会社

〈完全なプログラミング〉を目指す会 2020

git push --force-with-lease

  • レビュー後にローカルで rebase した結果をリモートに反映するときに使う
  • いかなる force push も許容しないという考え方もあるのでチームの指針に従いましょう
〈完全なプログラミング〉を目指す会 2020

git add -p

  • Git - 対話的なステージング
  • 修正内容の一部分だけステージング出来る
  • 同じファイル内に、複数の目的が異なる変更が入っちゃってる場合に使う
  • ぼちぼち使う
〈完全なプログラミング〉を目指す会 2020

git config commit.template

[commit]
	template = ~/.gitmessage
〈完全なプログラミング〉を目指す会 2020

コミットメッセージ
まとめ

〈完全なプログラミング〉を目指す会 2020

どのように よりも
何を何故
に重点を置く

どのように は diff の役割

〈完全なプログラミング〉を目指す会 2020

このコミットを
cherry pick すると何が起こるのか?
このコミットは
どういう背景で積まれたのか?

Pull Request の説明文を書くつもりで

〈完全なプログラミング〉を目指す会 2020

その他のドキュメント

〈完全なプログラミング〉を目指す会 2020

残すべき
ドキュメント

〈完全なプログラミング〉を目指す会 2020

Architecture Decision Record

One of the most effective ways of documenting architecture decisions is through Architecture Decision Records (ADRs).
ADRs were first evangelized by Michael Nygard in a blog post and later marked as “adopt” in the ThoughtWorks Technology Radar.

Fundamentals of Software Architecture

〈完全なプログラミング〉を目指す会 2020

アーキテクチャ とは

1つは、システムから個々のパーツへとどこまでもブレークダウンできるということ、もう1つは、簡単には変更できない決定事項だということである

— エンタープライズアプリケーションアーキテクチャパターン

〈完全なプログラミング〉を目指す会 2020

Architecture Decision
Record

  • 容易に変更できない アーキテクチャ上の意思決定 について記録する
    • なぜ そうしたのか、 他に何を検討した のか
〈完全なプログラミング〉を目指す会 2020

なぜ残すのか?

〈完全なプログラミング〉を目指す会 2020

将来同じようなことを
やりたくなったときに役に立つ

〈完全なプログラミング〉を目指す会 2020

どうしても変更したくなったときに
何を考慮しなければならないか分かる

〈完全なプログラミング〉を目指す会 2020

Architecture Decision Record
フォーマット(例)

〈完全なプログラミング〉を目指す会 2020

参考にしたもの: Y-Statements

要点を汲みつつ日本語での書き易さを考えて再構成

〈完全なプログラミング〉を目指す会 2020

目的

背景

前提

選択肢

結論

〈完全なプログラミング〉を目指す会 2020

目的

  • 何を決定するものなのかを書く

    例) 行動ログ集約のアーキテクチャを決定する

〈完全なプログラミング〉を目指す会 2020

背景

  • なぜその決定を下す必要があるのかを書く

    例) リアルタイムにユーザー行動の分析を行うことでサービスの改善を高速に行いたい

〈完全なプログラミング〉を目指す会 2020

前提

  • 特筆すべき前提条件があれば書く

    例) 10分までの遅延は許容する

〈完全なプログラミング〉を目指す会 2020

選択肢

  • 検討した内容をすべて説明する
    • それぞれの提案について、メリット・デメリットを述べる
〈完全なプログラミング〉を目指す会 2020

結論

  • 採用する提案とその理由を書く。特に、デメリットにどう対処するのか(あるいは許容するのか)を忘れずに書く。

    例) A案を採用する。技術人員リソースの不足は開発支援チームの支援を受けることで解決する。

〈完全なプログラミング〉を目指す会 2020

まとめ

〈完全なプログラミング〉を目指す会 2020

Code Tells You How
Comments Tell You Why

〈完全なプログラミング〉を目指す会 2020

コミットメッセージは変更内容ではなく
変更の意図・背景 を説明する

〈完全なプログラミング〉を目指す会 2020

初めから
整理されたコミットメッセージ
を書く必要はない

〈完全なプログラミング〉を目指す会 2020

容易に変更不可能な
アーキテクチャ上の意思決定の過程
はドキュメントに残す

〈完全なプログラミング〉を目指す会 2020

この瞬間から

〈完全なプログラミング〉を目指す会 2020

完全なコメント
完全なコミットメッセージ
書きましょう

〈完全なプログラミング〉を目指す会 2020

大きめの機能設計などの際には
Architecture Decision Record
を残すようにしましょう

〈完全なプログラミング〉を目指す会 2020

おわり

〈完全なプログラミング〉を目指す会 2020

Credit

〈完全なプログラミング〉を目指す会 2020