= リーダブルコードの意義と、実践の方法 : author 結城洋志 : institution 株式会社クリアコード : content-source リーダブルコード演習 : date 2021-09-13 : allotted-time 90m : theme clear-code = 全体の流れ * 1日目(本日) * 前半:概要と進め方の説明 * 後半:チームで実装 * 2日目(明日) * 前半:チーム同士で実装を交換、\n    続きを実装 * 後半:全体で結果を共有 == ノート まず最初に、全体の大まかな予定を示すが、ここはサッと流す。 進行の進行度合いによっては、実装作業の時間を長めに取る可能性があることを伝える。 = 講師紹介 (('tag:center'))(('tag:margin-bottom * 2')) 結城洋志(ゆうき ひろし)\n aka Piro * 株式会社クリアコード所属 * FirefoxやThunderbirdの\n 法人サポートに従事 * トラブルの原因や対策を探るため\n ソースコードを調査することが多い == ノート Mozilla FirefoxはWebブラウザー。 Mozilla Thunderbirdはメールクライアント。 開発元のMozillaは、企業ユーザーからのトラブル問い合わせなどに回答するサービスは提供していない。 そのため、クリアコードのような事業者が、ある意味で勝手にサポートサービスを行っている。 クリアコードは10人未満の小規模事業者で、エンドユーザーからの電話問い合わせに対応するといったビジネスはできないので、そうではなく、企業の情報システム担当者や、大学であればシステム管理者といったレベルの方が、ある程度までは自分達で対応した上で、それでもまだ分からないことを問い合わせる(エスカレーションする)先としてテクニカルサポートを提供している。 FirefoxもThunderbirdも、自分達が作っている物ではないし、作っている人達は企業ユーザーの実際の利用の仕方を知らないので、企業ユーザー向けの技術資料といった物も特にない。 よって、問い合わせを受ける度にソースコードを読んで調査することになる。 「リーダブルなコード」の恩恵を非常に強く受ける立場だと言える。 = チューター紹介 (('tag:center'))(('tag:margin-bottom * 2')) 足永拓郎\n (あしえ たくろう) * 株式会社クリアコード所属 * Fluentd開発メンバー * 参加者のサポート係(オンライン) == ノート 今日明日とオンラインの演習の時間のみ参加。 Fluentdというのは、いわゆるログ収集のためのソフトウェア。 = チューター紹介 (('tag:center'))(('tag:margin-bottom * 2')) 大場光一郎\n (おおば こういちろう) * 株式会社Speee所属 * JRubyコミッター * 参加者のサポート係 == ノート 会社の業務で関わりがあり、今回お手伝い頂く事に。 Rubyの実装は、CRubyと呼ばれていて、言語を解釈して実行する処理系はC言語で実装されている。 JRubyは、Rubyの処理系をJavaで実装したもの。 = アジェンダ * ((*講座の目的*))を確認 * リーダブルコードの\n ((*必要性*))を確認 * リーダブルコードの\n ((*実践方法*))を紹介 * 実践方法を((*練習*))(('note:(次のコマから)')) == ノート サッと流す。 = 講座の目的 * ((*リーダブルコード*))を\n ((*日常的に書く*))上での\n ((*基礎となる考え方*))\n を実践し、持ち帰る == ノート サッと流す。 = 目的でないこと * テクニックをたくさん覚える * 難しいプログラムを実装する * プログラムを速く実装する * 高性能なプログラムを実装する * 奇抜な方法で目立つ == ノート サッと流す。 =   == ノート 講義の目的を伝えたということで、ここからは講義の本体。 = そもそもの話 * リーダブルコードはなぜ必要か * 何の役に立つのか? == ノート サッと流す。 = リーダブルコードが必要な理由 * 既存のコードを読んで\n ((*素早く内容を把握したい*)) * 既存のコードに\n ((*素早く手を加えたい*)) * ((*開発速度*))を落としたくない == ノート サッと流す。 = 「速度」? * 「読みやすさに気をつけたら\n  開発スピードが\n  落ちそう……」 == ノート サッと流す。 = むしろ「読みにくいと開発が遅くなる」 * 既存のコードを\n 理解しにくいと…… * 修正・機能追加に時間がかかる\n (('note:(理解しないと変更できない)')) * 後退バグが発生しやすい\n (('note:(理解しないまま変更すると問題発生)')) (('tag:center')) →((*コストがかかる*)) == ノート 後退バグとは、今まで期待通りに動いていた物が、別の変更の影響によって、期待通りには動かなくなってしまう、という種類の不具合のこと。 = 時間が経つほど影響大 # image # src = images/readable-code-reasonability.svg # relative_width = 90 (('tag:center')) (('note:(注意:グラフではなく概念図です)')) == プロパティー : enable-title-on-image false == ノート グラフ風の図 グラフの縦軸は開発スピードで、上に行くほど爆速、下に行くほど停滞。 グラフの横軸は時間経過。 読みやすさを考えずにコードを書き散らかすと、最初のうちは開発速度が爆速だけれども、時間経過に伴って、やがて急速に開発スピードが落ちて、進捗が悪くなってくる。 読みやすさを考えながらコードを書くと、慣れないうちは平均的に少し開発速度が遅くなるけど、時間が経過しても、開発スピードが落ちにくく、進捗も悪くなりにくい。 これはいろんな人が言っている。 「読みやすさ」よりもう少し広く「内部品質の高さ」「保守性の高さ」という切り口の話になるけれど、「保守性を犠牲にして開発する場合と、保守性を高く保って開発する場合に、曲線が交差する点(つまり損益分岐点)には1ヵ月くらいで到達する」と言われている。 1ヵ月という数字の出典: 質とスピード(2020秋100分拡大版) / Quality and Speed 2020 Autumn Edition https://speakerdeck.com/twada/quality-and-speed-2020-autumn-edition この事実を無視して、読みにくいコードのままで開発を続けながら〆切を守ろうとすると、徹夜や連勤といったことになって、自分の命を削ることになる。 あるいは、増員をするために人件費が増える。 = つまり * 現実的なコストの範囲で * 既存のコードを継続的に、\n 無理なく改良・修正したい (('tag:center')) →なので、リーダブルコード == ノート ここはサッと流す。 = 既存のコードを読んだり手を加えたりする場面 (('note:在学中だとどんな場面がありそう?')) == ノート 2人くらい受講者の人の意見を聞いてみる。 = 既存のコードに手を加える場面 * ((*複数人*))で研究 * 共同研究、院生と学部生で分担 * 自分の研究を((*発展*)) * 修士で作った物を博士で使う * 途中で((*方針修正・転換*)) * 先輩の研究を((*参照*)) == ノート 受講者から出てこなかった物について述べる。 = 既存のコードを読む場面 * ((*論文に含まれるコード*))\n を読むとき * 自分が誰かの論文を読むとき * 自分の論文が誰かに読まれるとき (('note:コードがリーダブルだと参照されやすくなるかも……')) == ノート 受講者から出てこなかった物について述べる。 = 営利企業ではどうか * 分かる人が1人しかいない→危険 * 実装者が抜けたら詰む * 変更できてもものすごくコストがかかる (('wait')) (('tag:center')) ↓\n チームで開発し、\n チームの誰でも作業を引き継げる\n 状態にしておきたい == ノート 有名なのはスクウェアのファイナルファンタジー1~3のメインプログラマーだったナーシャ・ジベリ氏。 ファミコンの時代(30年くらい前)に「すごいテクニックを駆使して、ハードウェア性能を超えているとしか思えないような物凄い演出を実現した」ということで知られている。 彼が書くコードは非常に難解だったそうで、バグが多かったままの発売も多かった(本人でなければデバッグできなかった)とか。 ビジネスが個人に強く依存することは、企業にとってはリスクなので、基本的には避けたいことだと言える。 =   == ノート リーダブルコードの意義について説明した。 ここからは、どうやればリーダブルコードを書けるか、実践できるかの話。 プログラムを勢いで書いた後で、リーダブルに書き直すのは、実際やると大変。 それよりは、日々プログラムを書くにあたって、息を吸うようにリーダブルなコードを書けることが理想。 どうすればそんな風になれるのか? = リーダブルコードの実践 (('note:どうすれば無理なく実践できる?')) = リーダブルコードの実践 コードを((*読む*))\n 習慣を作る = 読む?書くじゃないの? * リーダブルコードを書くには\n コードを読むことが欠かせない * なぜ? (('wait')) (('tag:center')) ↓\n 書いている最中は\n ((*読みやすさ・読みにくさ*))\n を実感しにくいから == ノート ……という説明だけでは実感が湧かないと思うので、ここからは例を示す。 = 実際のコードで考えてみる * Excelワークシートの生成で\n 見出しセルを結合する関数 # image # src = images/xlsx.png # relative_width = 90 == プロパティー : enable-title-on-image false == ノート (自己紹介で触れた場合)自分は業務でFirefoxの法人運用のサポートをしている。 このExcelワークシートは、「Firefoxの自動更新」という設定項目に対して、選択肢が「1. 自動更新します」「2. 自動更新を停止します」の2つがある、といった要領で、企業での運用でよく求められるカスタマイズ内容とその設定方法を表にした物。お客さん向けの資料。 カスタマイズ項目の情報は、Gitで管理しやすいようにテキストファイルを原本としている。 それを元に、プログラムで自動的にExcelワークシートを作るようにしている。 これから紹介するのは、そのプログラムの一部の話。 = やりたいことを言語化してみる 関数 項目の見出し列(B~C)のセルを結合する(行番号, 項目): 項目の選択肢の数が1以下だったら: 何もせず終了 そうでないなら(選択肢が2つ以上あるなら)、 「見出し列の定義の配列」の全要素について、 要素の番号 を使って: シートの範囲を結合( 開始行 → 行番号, 開始列 → 要素の番号, 終了行 → 行番号 + 選択肢の数 - 1, 終了列 → 要素の番号) == ノート これは、「こういう手順でこういうことをやろう」という擬似コード。 「関数名」の所だけ述べてサッと先に進め、ここからは表と擬似コードを並べて説明する。 = 項目の見出し列の…… # image # src = images/xlsx-01.png # relative_width = 90 == プロパティー : enable-title-on-image false == ノート 「項目の見出し列」というのは、この図で赤く囲った部分のこと。 = セルを結合する # image # src = images/xlsx-02.png # relative_width = 90 == プロパティー : enable-title-on-image false == ノート 「セルを結合」というのは、この図で赤く囲った部分のこと。 この図では3行分が結合されている。 = 見出し列の定義の配列 # image # src = images/xlsx-03.png # relative_width = 90 == プロパティー : enable-title-on-image false == ノート 「見出し列の定義の配列」は、図の右上に示したような物が別途定義されているイメージ。 この配列の要素がそれぞれB列とC列に対応している。 = シートの範囲を指定して結合 # image # src = images/xlsx-04.png # relative_width = 90 == プロパティー : enable-title-on-image false == ノート 「シートの範囲を結合」という部分で、行番号と列番号を使って範囲を指定し、セルを結合する。 これは、ライブラリがそのようなAPIを提供している。 ここまでの話を頭に入れた上で、実際にコードを書いたらどうなるだろうか。 = 実際のコード def m(s, n, i): if len(i['o']) <= 1: return s2 = s.s for i2, c in enumerate(hcol): s2.merge_range(n, i2, n + len(i['o']) - 1, i2, '') == ノート リーダブルな書き方が身に着いていない人が書くコードは、こういう感じになることがある。 Pythonだと思って読んで欲しい。 変数名は短くて、名前に意味は無い。 = 書いた本人の視界 関数 項目の見出し列(B~C)のセルを結合する(行番号, 項目): 項目の選択肢の数が1以下だったら: 何もせず終了 そうでないなら(選択肢が2つ以上あるなら)、 「見出し列の定義の配列」の全要素について、 要素の番号 を使って: シートの範囲を結合(開始行番号, 開始列番号, 開始行番号 + 選択肢の数 - 1, 終了列番号) ---------------------------------------------------------- def m(s, n, i): if len(i['o']) <= 1: return s2 = s.s for i2, c in enumerate(hcol): s2.merge_range(n, i2, n + len(i['o']) - 1, i2, '') == ノート 書いた本人の頭の中には、上半分のイメージがある。 それと無意識で照らし合わせているので、コードの意味が分かるという状態。 脳が無意識に補完するので、読みにくさに気付けない 書いてある以上の事を「読み取って」いる では、「頭の中のイメージ」を共有していない人が見ると、どうなるか。 それを次に示す。 = 第三者の視界 def m(s, n, i): if len(i['o']) <= 1: return s2 = s.s for i2, c in enumerate(hcol): s2.merge_range(n, i2, n + len(i['o']) - 1, i2, '') ------------------------------------------------ 関数 何か(何か, 何か, 何か): 何かの数が1以下だったら: 何もせず終了 そうでないなら、 何か = 何かが保持している何か 何かについて、 何か を使って: 何かの範囲を結合(何か, 何か, 何か + 何か - 1, 何か) (('note:前情報無しだと、意味ある情報を読み取るのは困難……')) == ノート さっきの「頭の中にあるイメージ」を共有していない人が上のコードを読んでも、下に書いた程度のことしか分からない。 「何か」ばっかりで、まるで意味が分からない。 コードを後から引き継いだ人が読んだときや、コードを書いた本人でも1年後に読み返したときには、こうなりがち。 こういう状態で「不具合を直す」「壊さないように改修する」というのは非常に難しい。 そのため、プロはこういうのを見ると悪態をつきがち。 = リーダブルなコードの場合 def try_merge_item_heading(self, row, item): if len(item['options']) <= 1: return sheet = self.sheet for index, _column in enumerate(HEADING_COLUMNS): sheet.merge_range(row, index, row + len(item['options']) - 1, index, '') ----------------------------------------------------------- 関数 項目の見出しセルを結合してみる(自分, 行, 項目): 項目の選択肢の数が1以下だったら: 何もせず終了 そうでないなら、 シートは自分が保持している 見出しの列 の全項目について、 番号 を使って: シートの、範囲を結合( 行, 番号, 行 + 項目の選択肢の数 - 1, 番号, '') (('note:先の例よりは意味のある情報を読み取れるはず')) == ノート 変数や関数の名前が適切に付けられていると、漢文の読み下しや、ふりがなが振られた文章のように、何を書いてあるかをある程度読み取れるようになる。 周辺のコードや、実際の出力結果と見比べて、処理の意図する所をより正確に把握していく必要はあるが、それでも、スタート地点でだいぶ助かるのは間違いない。 結城が業務でFirefoxやThunderbirdのソースコードを調査するときも、こういう要領で未知のソースコードを読み進めていっている。 OSSのソースコードは比較的リーダブルであることが多い。 それは、様々なバックグラウンドを持つ人々が、散発的に開発に関われる状態にするにあたって、ソースとは別の詳細な設計資料などを用意したり、内容を更新し続けたりすることに、充分なコストをかけられないから。 営利企業でもそこは変わらないが、OSSの場合は「お金をそんなにかけられない」という問題がよりシビアなので、リーダブルなコードにする圧力が働きやすい。 = よく聞く話 * 手紙を書いたら一晩寝かせろ! * 試験の回答文は、最後に\n もう一回読み返せ! (('tag:center')) ((*前提を知らない読み手*))\n の気持ちになろう == ノート 毎回一晩待つ訳にもいかない。 一晩待たずに「まっさらな頭で読む」状態に近付けるには、訓練がいる。 「前提知識を共有していない読み手」の感覚になって読むというのは、意識して実行しないと、なかなかできない。 それを身に着けるには、実際にプログラムを「読む」癖を付けるしかない。 読み手視点で「もっとこう書いてあったら読みやすいのに」と思えるようになれば、自分がコードを書くときもそれを意識できるようになる。はず。 =   == ノート リーダブルコードをどうやれば実践できるかについて、短い例を使って説明した。 今度は、もっと実際のケースに近い「コードを読む状況」を体験してもらう。 = 実際のコードを読んでみよう Excelのワークシートを\n 自動生成するPythonスクリプト # image # src = images/xlsx.png # relative_width = 90 == プロパティー : enable-title-on-image false == ノート 先ほど例で示した物なので、ここはサッと流す。 = ビジネス上の要件 * Firefoxを法人運用向けに\n 設定したい * 設定を一覧で管理したい * 「自動更新:有効/無効」など * 顧客ごとに設定内容を変えたい * バージョンごとの変化を見たい * Firefox 78からFirefox 91の間で\n 追加された設定、廃止された設定 == ノート ソフトウェアの法人運用、ということについて簡単に紹介する。 お客さんはFirefoxの詳細に詳しいわけでないので、「情報を一覧できる」「何がどう変化したかを把握できる」資料を用意できることにはビジネス上の価値がある。 = 元々はExcelワークシートを手作りしていた * Gitでバージョン管理しにくい * 同じ変更内容を複数顧客の\n ワークシートに反映するのに\n 手間がかかる どうにかしたかった == ノート 詳しくは説明せず、とにかく面倒・ミスが発生しやすかったということを伝える。 = 自動化を図った * 資料自体を\n バージョン管理しやすく * プレーンテキスト形式のソースから\n Excelワークシートを自動生成 * 1. 設定項目の定義 * 2. 前バージョンのFirefox用の設定情報 * 3. 今バージョンのFirefox用の設定情報 * 初版はPythonに慣れた人が実装 == ノート 作業の負担を減らすために、「項目の定義の原本」と「そのお客さんの前のバージョンのFirefox用の設定情報」と「そのお客さんの新しいバージョンのFirefox用の設定情報」の3つを組み合わせてExcelワークシートを出力するようにした。 以上の前提を頭に入れた上で、スクリプトそのものを見てもらう。 = 読んでみよう1 build-xlsx-((*1*)).py\n (('note:ざっと見て概要を掴んでみよう')) == ノート 5分くらい時間を取る。 熟読はしなくていいので、「なんとなくこの辺はこういうことをしていそうだな」ということを読み取ってもらう。 各自で目を通してもらった後で、講師側で概要を説明する。 15-31:ライブラリの読み込み。 36-76:定数の定義。 81-98:ユーティリティ的な関数の定義。 100-142:ヘッダ行の出力部。 144-201:各項目の行の出力部。ここの中は詳細はあえて説明しない。後で受講者の方自身で探してもらうため。 202:凡例を出力。 207-245:スクリプトの実行時に、コマンドライン引数で指定された内容に基づいてファイルを探してきて読み込む。 = ポイント * 少数の短い関数 * 分かりやすい名前付け * 列番号はべた書き == ノート 書籍「リーダブルコード」に書かれていることに概ね合致している。 = 試しに探してみよう1 * 「...→...での変更」の列に\n 「新規」と出力する条件は\n どこで判定している? == ノート 正解は、173-186行目の部分。 「廃止扱いになっていなくて(173、176)、今バージョンのFirefoxの設定の中に情報があって(178)、前バージョンのFirefoxの設定の中に情報が無い(181)」場合に、「新規」と出力される。 = 試しに読んでみよう2 build-xlsx-((*2*)).py\n (('note:第三者が手を加えた物')) == ノート 5分くらい時間を取る。 熟読はしなくていいので、「なんとなくこの辺はこういうことをしていそうだな」ということを読み取ってもらう。 各自で目を通してもらった後で、講師側で概要を説明する。 大部分は先のバージョンから変わっていない。 大きく変わっているのは、以下の2つの部分。 117-159:ヘッダ行の出力部。 161-249:各項目の行の出力部。ここの中は詳細はあえて説明しない。後で受講者の方自身で探してもらうため。 ぱっと見で分かるとおり、各項目の行の出力部の行数がグッと増えている。 = 改修の経緯 * 要件が増えた * 比較対象の数が可変になった * 改修前:「Firefox 78」と\n     「Firefox 91」 * 改修後:「Firefox 78」と\n     「Firefox 91(デスクトップPC)」\n     「Firefox 91(ノートPC)」... * 行数は約1.2倍に増加 == ノート これはPythonに詳しくなかった講師が、要件に沿って出力を変えようとして、見よう見まねで改修を行った結果。 元々は「前バージョンの情報」と「今バージョンの情報」だけでよかったのが、「今バージョンの情報 デスクトップPC用」「今バージョンの情報 モバイル用」といった要領で、設定のバリエーションを複数管理しないといけなくなった。 = 試しに探してみよう2 * 「検証手順書対応番号」\n の列に出力する内容は\n どこで決まっている? (('note:さっきより大変なはず')) == ノート 正解は、211行目と220行目。 関数の引数としてchaptersというディクショナリが渡ってきていて、項目のIDをキーとして情報を取得している。情報が無ければ「省略」という文字列を規定の内容として使う。 それを245行目で出力している。 = リーダブルだった書き方がアンリーダブルに…… * 関数の数が少ない * 個々の関数が肥大化し、\n 全体像の把握が困難に * if-elif/elseでの条件分岐 * 階層が複雑化して\n 処理の実行条件を追いにくい == ノート 端的に言えば、プログラムの規模と書き方のバランスが崩れてしまった。 問題は複雑なのに、プログラムのほうは、単純な問題に適した書き方のままになっていた。 そのため、「幼児向けの絵本の中に突然、マンガのページが現れた状況」のようなアンバランスが生じている。 = 試しに読んでみよう3 build-xlsx-((*3*)).py\n (('note:大幅に改修した物')) == ノート 3分ぐらい時間を取る。 雰囲気だけ見てもらえればいい。 各自で目を通してもらった後で、講師側で概要を説明する。 かなり大規模な変更が加わっている。 メソッド・関数の数が、先のバージョンでは11個だったのが、28個と倍以上に増えている。 機能的にはほとんど変わっていないので、それまで11個のメソッドに詰め込まれていた意図を、慎重に解きほぐして、28個の細かいメソッドに分けた、ということになる。 98-128:列の定義が定数になった。各列について、「ヘッダ行に出る名前、列の幅、列に入れるべき内容を表すキーの文字列、列の表示スタイル」を保持している。これは、列の順番を入れ換えたり列の数を増減させたりするのを楽にするための設計変更。 133-202:ConfigurationSheetクラス。Excelワークシート全体の出力に関わるユーティリティ的な役割をする。ヘッダ行の出力も担当する。 204-351:ConfigurationRowクラス。各設定項目の行の出力を担当する。 353-393:元のバージョンでヘッダ行や各項目の行の出力部だったメインループ。2つのクラスのインスタンスを作って、それぞれのメソッドを呼ぶだけになっている。 クラスの詳細は説明せずに流す。 = 改修の要旨 * 複雑化した問題に合わせて「リーダブル」の基準を変えた\n (('note:何がリーダブルかは状況に依存する')) * 定数を使う部分を増やした * メソッド・変数のスコープを小さく * コードの行数は約1.5倍に増加 = 試しに探してみよう3 * 「検証済み」という列を\n 「検証手順書対応番号」\n の列の右隣に追加したい\n (セルの内容は空でよい)が、\n どこに手を入れればいい? \n \n \n == ノート いきなりこう言われても困ると思うけれど、コツが分かっていると意外とスルッと調べられる。 まず、ヘッダ行の「検証手順書対応番号」を探す。 これは121行目に書かれていて、VERIFICATION_COLUMNSという定数名から、どうやら検証に関わる列の定義らしいと分かる。 今度は「VERIFICATION_COLUMNS」を探す。 という風に、芋蔓式にメソッドの呼び出し関係を辿っていくのが基本。 ポイントは「全体はそんなに読まない」ということ。 = 試しに探してみよう3 * 「検証済み」という列を\n 「検証手順書対応番号」\n の列の右隣に追加したい\n (セルの内容は空でよい)が、\n どこに手を入れればいい? * →((*探し方のコツ*))がある * 改修前:((*全体*))を読んで覚えて探す * 改修後:((*必要な部分だけ*))読んで探す == ノート 講師が実際にやっている様子を見てもらう。 「VERIFICATION_COLUMNS」という定数を参照している箇所を探す。 171行目の「self._write_header_columns(VERIFICATION_COLUMNS, column_offset)」と、146行目の「self._write_item_columns(VERIFICATION_COLUMNS, format, column_offset)」で参照されている。 それぞれのメソッド名から、ヘッダ行と、設定項目のそれぞれについて、列を出力する物らしいと推測できる。 _write_header_columns の定義を見てみると、174行目に書かれている。 渡されたcolumnsというのが、VERIFICATION_COLUMNSに対応するようだと分かる。 処理としては、これをenumerateに渡して、全項目をループで処理し、columnsの要素の、ラベル文字列の部分を出力している。 _write_item_columns の定義を見てみると、252行目に書かれている。 こちらも、渡されたcolumnsというのが、VERIFICATION_COLUMNSに対応するようだと分かる。 こちらもやはり、columnsをenumerateに渡して、全項目をループで処理しており、その中で、_get_column_value という別のメソッドを呼んでいる。 _get_column_valueの定義を見てみると、258行目に書かれている。 keyで処理を振り分けて、選択肢の番号やタイトル文字列などを返すようになっているみたい。 引数で指定されたものがifとelifでのマッチング対象になっていなかった場合、空の文字列を列の内容として返却している。 以上の話を総合すると、、VERIFICATION_COLUMNSという定数で定義されている列の定義を1つ増やせば、「検証手順書対応番号」列の隣に「検証済み」という列を増やせる、と考えられる。 ある情報を見つけたら関連語句が分かり、今度はその関連語句で検索する、ということを繰り返していくのが基本戦略になる。 = つまり、ここでの「リーダブル」とは * 全体を一望するのが難しい\n 複雑なコードについて * 一度に読むことは諦めて * 必要な部分だけ読む という前提での「読みやすい」 == ノート 一つのプロダクトにずっと専任者がかかりきりではいられない。 詳しい事情を把握してない人も関わらないといけない。 という状況では、こういう方向になってくる。 これはOSSの開発プロジェクトでよく見られる傾向。 = ふつうのOSS開発者の日常 * ((*× イメージ*)) * コード全体を詳細・完璧に把握 * ノールックで修正 * ((*◯ 実態*)) * 全体像はボンヤリ把握 * 都度必要な部分を読み直して\n 調べ直しながら修正 == ノート OSSの開発では、今の説明のように、その都度ソースコードを調べながら作業することが多い。 = 自分で書いたコードでも、覚えてないのが当たり前! * ((*覚えておかなくていいように*))\n ((*する*))のがリーダブルコード * 書籍「リーダブルコード」\n 巻末の「解説」も読んでみて! == ノート そういうやり方で作業しやすいことを「リーダブルである」と言っている、とも言える。 今述べたようなことは、そのまま書籍「リーダブルコード」の巻末の「解説」に書かれている。 =   == ノート さて、リーダブルコードをどうやれば実践できるかについて、実際の例を使って説明した。 = リーダブルコードの実践 * 別の切り口でも考えてみよう * なぜ((*アンリーダブル化する*))\n のか? == ノート 今度は、少し切り口を変えてリーダブルコードの背景を説明してみる。 ベテランでもアンリーダブルコードを書いてしまうことはある。 なので、危険な兆候に早めに気付けるようになることが大事。 = アンリーダブル化する原因 (('note:考えてみよう')) == ノート 2人くらい受講者の人の意見を聞いてみる。 = アンリーダブル化する原因(例) * コードが((*状況の変化*))に\n 追従できていない * コードの規模や前提は\n ((*徐々に変化*))する * 気付かないうちに進行する! == ノート ジワジワ変わる物には気付きにくい。 ただ、気付いていても放置してしまいがちという部分もある。 = 状況の変化に気付けても、追従できない…… * 既存のコードを((*書き直せない*)) * 書き直して動かなくなるのが怖い * 「とりあえず動くように」で焦って\n 「書き足す」一辺倒になる * 冷静に見直す余裕がない (('tag:center')) →良くない兆候を見て見ぬフリ == ノート 他の人が元のコードを書いてくれた物を引き継いだとか、既存のプログラムからコードをコピー&ペーストしてきた、といったケースで起こりがち。 既存のコードを書き換えることに強い恐怖が働く。 講師自身のケースでは、Pythonに不慣れだった。 if-elseくらいは分かるので、その範囲で頑張ってなんとかしようとしてしまった。 それでいびつな状態になってしまった。 = ベテランはどうする? * 悪い((*兆候*))を見逃さない * 既存のコードを\n ((*書き直すのをためらわない*)) * 書き直さない方が、\n ((*後で痛い目を見る*))と知っている == ノート 自分には当初できなかったけれど、理想的なベテランのITエンジニアだったらどうしていただろう? という問いを立てたときの講師の回答は、こう。 = 悪い兆候に敏感になろう * ((*マメに読み返している*))と、\n アンリーダブルのなり始めに\n すぐ気付ける * 「アンリーダブルに\n  なり始めてる……?」\n →書き直しを考えるタイミング\n (('note:後回しにしない!'))\n (('note:(すぐやらないなら、せめてコメントを残す)')) == ノート ベテランだってほとんどの場合は物凄い神業なんか持っていない。 読み返してみて「アンリーダブルになってるな」と気付いた時に、そこから目をそらさないようにしてるだけ。 = ためらわずに書き直せるようにするために(1) * わけが分からないままにしない * ((*自分が今何をしているか*))を\n 正しく理解するよう努めてる * 不安が少しでも生じたら\n 立ち止まって考える == ノート これは抽象的な心構えの話。 なんといっても、きちんと問題と向き合って取り組むのが、ITエンジニアとしての真っ当なやり方。 「急がば回れ」ということわざの通り。 = ためらわずに書き直せるようにするために(2) * 便利な道具で補う * 自動テストで((*結果の同値性*))を保証\n (('note:自動テストは大事!')) * Gitで((*いつでも巻き戻せる*))安心感\n (('note:バージョン管理システムは大事!')) * ((*コードフォーマッター*))で\n 安全にコードを整形\n (('note:「リーダブルにする」はある程度自動化できる')) == ノート 心構えだけでなく、工夫も大事。 自力で何でもできれば格好いいけど、残念だけどそうはできないのが自分で、道具を使えばそこを補える、と考えて割り切ってる。 = 早め早めのリカバリー * なるべく早くリーダブルに直す * 書く→直す の((*サイクルを回す*)) == ノート 手遅れの状況にハマってしまったら、とりあえず頭を切り替えて、なんとか力業で乗り切るしかない。 せめて、乗り切った後で、次に同じことで焦らなくて済むように備えよう。 サイクルが極限まで速まったのが、「最初からリーダブルに書ける」状態。 = 参考:良い分割の仕方 * "良いコードとは何か - エンジニア新卒研修 スライド公開" * ((*https://note.com/cyberz_cto/n/n26f535d6c575#E0aBe*)) * サイバーエージェント社の新卒研修の資料 * 「凝集度と結合度」が参考になる == ノート 「リーダブルに直す」方法として、関数やモジュールを小さく分割することがおすすめされがちだが、どう分けたら良いかで悩む人は多い。 講師もよく悩む。 参考としてサイバーエージェント社の新卒研修の資料を紹介する。 「分割した後のコードがこうなっているのはまずい・こうなっているとよい」という例が示されている。 =   == ノート リーダブルなコードの実践例を紹介し、アンリーダブルなコードが生まれる経緯を考察した。 ここからは、より深く理解してもらうための演習の説明。 = リーダブルコードの練習 * コードを書く→読む→直す\n のサイクルを体験しよう = チームで開発してみよう * まずは、チームの中で\n コードを読みあってみる\n * 自分で書いた物を読むよりは\n 客観的に読みやすい * 交代で書く / 感想を述べ合う == ノート 書かれたコードを他のチームメイトは見て読むことになる。 読んで引っかかりを覚えたら、リーダブルに直す機会になる。 = 注意点 * なるべくポジティブな提案を * ◯「この書き方はリーダブルだね」 * ×「これはアンリーダブルだね」\n (('note:(粗をあげつらうだけなら誰でもできる)')) * ◯「こうした方がリーダブルじゃない?」 * チーム内で皆が納得できる\n リーダブルの基準を見つけよう * 既存の基準をベースにするのはアリ\n (('note:(例:書籍「リーダブルコード」の内容など)')) = 明日の後半戦 * (進捗次第では、\n  今日の後半の続きをやる) = 明日の後半戦 * 他のチームが\n 書いたコードを読んでみる\n (('note:(自分のチームの物を読むよりは客観的に読みやすい)')) * チーム間で感想を共有 * より多くの人が納得できる\n リーダブルの基準を見つけよう * 開発を継続してみる = 最終目標:コードを読む文化を持ち帰ろう * 「コードは読む物」\n という認識を持つ * 自分だけからチームへ * チームだけから全体へ = この後の予定まとめ * 本日の後半:課題を実装 * リーダブルコードを書く体験 * 明日の前半:実装チェンジ\n       →開発継続 * 既存のコードを読んで変更する体験 * 明日の後半:ふりかえり * リーダブルコードの基準を\n 共有する体験 = おさらい * 講座の目的? * リーダブルコードの必要性? * 講座でやること? = 講座の目的 * ((*リーダブルコード*))を\n ((*日常的に書く*))上での\n ((*基礎となる考え方*))\n を実践し、持ち帰る = リーダブルコードが必要な理由 * 既存のコードを読んで\n ((*素早く内容を把握したい*)) * 既存のコードに\n ((*素早く手を加えたい*)) * ((*開発速度*))を落としたくない = 変更コストと開発速度 # image # src = images/readable-code-reasonability.svg # relative_width = 90 == プロパティー : enable-title-on-image false == ノート 「リーダブルさより実装スピードを優先」と思って開発すると、すぐに開発スピードが落ちる 「リーダブルさを大事にしながら実装を進める」と思って開発すると、開発スピードが落ちにくい。 コードの読みやすさは中・長期的な視点で効いてくる。 = 講座でやること * コードを読む文化作りの体験 * チームの中でコードを\n 読みあってみる * チーム内でリーダブルコードの\n 基準を共有する * 他のチームともリーダブルコードの\n 基準を共有する = ここまでの説明 腑に落ちましたか? =   (('note:ここから先は、「まとめと次のステップ」の内容です')) = 次のステップ * もっともっとコードを読もう! = 次のステップの例1 * ゼミの他の人や\n 同じ学部の人が書いたコードを\n 読んでみよう * 研究の合間に * 論文執筆の合間に = 次のステップの例2 * 使うライブラリやツールの\n コードを読んでみよう * OSSのコードは\n リーダブルであることが多い * 「こういう結果を得るには\n  こう書くんだ!」と\n 実感を伴って読める = 次のステップの例3 * コミット単位で読んでみよう * コード全体ではなく差分を読む * コードの中身・設計の仕方ではなく\n コードの書き方・開発の仕方に注目する * リーダブルなコードを\n 見つけるのに使いやすい = コードを読む文化を後輩につなごう * ゼミの後輩にも「コードを読む\n 文化」に馴染んでもらおう * 皆さんが書いたコードが\n ((*資産として受け継がれる*)) * ((*口頭での詳しい説明なし*))でも\n コードを読んで伝わる情報が増える\n (('note:後輩に手取り足取り教えずに済む→先輩も楽になる')) = 新しい人との関わりが、リーダブルの基準の見直し機会になる (('tag:center')) これまで大事にしてきたことを\n 後輩と共有\n ↓ * 「もっとこっちの方が\n  リーダブルでは?」 * リーダブル基準の見直しのよい機会 = サポート * 今日の資料はすべて再利用可能\n (('note:https://github.com/clear-code/readable-code-workshop/tree/master/20210913'))\n (('note:( https://slide.rabbit-shocker.org/authors/Piro/ )')) * 迷ったら読み返せる = クリアコード * クリアなコードが大切 * クリア == clear == 意図が明確 * クリアなコードはリーダブルコード (('tag:center')) みなさんのコーディングライフで\n リーダブルコードが当たり前に\n なることを応援します! = 課題(宿題) * 過去作成した何らかの\n プログラムについて * ((*リーダブルになるよう編集*))する * リーダブルにした点を\n 1つ以上((*メモに書き出す*)) * 以下の3つを併せて提出する * ((*変更前*))のプログラム * ((*変更後*))のプログラム * リーダブルにした点の((*メモ*))