ベタープログラマ第二部の感想
第二部「練習することで完璧になる」を読みました。
読んだのは15,16,17,19,22章。15章は16,17章を読んでたら「我々のチームの三つの規則」として触れられてたので。
15章 規則に従って競技する
いろんな規則があるけれど「自分達で決めた規則が必要」とあります。規則は自分達のためにあるってやつですね。著者のチームでは以下の3つの規則が挙げられています。
- 単純に保つ
- 頭を使いなさい
- 変わらないものはない
これらの規則についてはそれぞれ別の章で語られていて、15章は規則のありかたが書かれています。この章で重要なことは、要点の囲みにも挙げられているこれだと思います。
曖昧で記述されていないチームの「規則」に頼らないでください。暗黙の規則を明示して、コーディングの文化を統制してください。
たまに見るのが、暗黙の規則が勝手にあると思い込んで「こうしないといけないと思っていました」「こうするものじゃないんですか?」というの。自分の書いたコードを説明するのを阻む、思考停止の臭いがします。こういうのを私は「呪い」って呼んでて、その解呪の方法の一つかなと。そもそも自分達で自分達のための規則を作れるなら、そんな呪いにかからないと思うけど。
16章 単純に保つ
KISS(Keep It Simple, Stupid)のことかと思ったし、確かにKISSについて書かれてました。けど単純とは何かってところに踏み込んでます。昔似たようなこと書いたなーと思ったので引っ張り出してみる。
コードに凝れば凝るほど、コードはシンプルになり、言われるような "凝ったコード" から離れていくと思う。
さて、本では単純さを「誤った種類の単純さ」と「正しい種類の単純さ」の2種類に分けています。
「誤った種類の単純さ」は、物事を単純に捉えてしまうことで「極度に単純化されたコード」を生み出し、逆に物事を複雑にしてしまうことになると書かれています。これは心当たりがある方も多いでしょう。そして「正しい種類の単純さ」を保つためにどのようなアプローチをとるかが挙げられています。
コードのエントロピーも増大していくものです。ですが、増えるのを放置して手に負えなくなったらスクラップ&ビルドする以外にも、抑える方法はあるはずです。私の場合、例えば修正前後のコード量を見ます。コード量が想定以上に増えていないかってところですね。なんなら機能追加してもコード量が減っていなかったら、余計な複雑性を作り込んでいないかを確認します。数字はあくまで指標の一つでしかないので、使い所に注意は必要ですが。
17章 頭を使いなさい
この章は16章を前進させる規則ですね。曰く「Keep it simple, stupid. don't be stubid.」と。愚かになるのは典型的なギークの問題であr、コーディングの専門家でもやってしまうのだから、仕方ない。でもそれは愚かなままでいていいと言う意味ではありません。ちょっと時制に込められた意図が読み取りづらい章だなーとは思いましたが、勇気を出して失敗を認めて誤りを直そう、とかそんな感じです。愚かなコードや設計をしたからといって、「自分を失敗者と考えないでください」とあります。失敗はしていい。
「愚かなコードを書かないでください。」「不注意にコードを書かないでください。」などと書かれていますが、これはちょっと難しいかなー。書いたコードを落ち着いて見直して、直したい。私は。
さて、この章で抜粋したいのはこの文章。
あるコード部分に取り組むときには、その形と構造に関して意識的な決断を行なってください。コードを所有してください。そのコードに対する責任を持ってください。
「意識的な決断」や少し前に使われている「説明責任」などは普段使っている言葉に近くて共感を持てました。 コードをどまんなかに // Speaker Deck とかで言ってるつもりです。
19章 コードを再利用するケース
再利用のケース4種類が挙げられて、それぞれについて考察されています。一つ目の「コピー&ペースト」はすごく嫌ってますね。ウェブ上で見つけたコードをそのまま貼り付けるな、とかも書いてます。なんか微妙な感じのコードがコミットされてる時とか、それをググったらコメントまで一致するのが引っかかってくるとか、たまにありますね。ああ言うのをやめてほしいなーってのは同意。でもコードのコピペ自体は私はよくやるんだよなぁ。昨日もやったし……。
22章 凍結されたコードの数奇な人生
コード凍結に対する、どのようなものがコード凍結と呼ばれるものにあるかが整理されています。もちろんブランチ戦略による凍結のない世界についても語られ、凍結しない世界を目指してほしいとも書かれています。
この辺はいまだに過渡期だなーと思います。凍結せずにできているもの、凍結させずにできないかと試行錯誤しているもの、凍結ありきから変化しない動こうとしないものも多くあり、その中には凍結しないことがメリットにならないものもあります。とはいえもはや凍結ありきの世界観ではないので、対応できる術は持っておかなきゃでしょうね。
第二部の感想
普段言ってることに近いこともあってか、正直薄味でした。どの章も短めだったし。他の人に説明するときの語彙は増やせたかなーくらいで、この部に書かれてることから新たなことを見出すのは難しい感じでした。見落としてるだけかもしれないけれど。
一方、ここまで書いているようなことを「はいはい、あのことねー」とならない方は読めば知の高速道路に乗れるかもしれません。エッセイだけあってちょっと他の本には書かれてないものなので、お勧め。
ベタープログラマ ―優れたプログラマになるための38の考え方とテクニック
- 作者: Pete Goodliffe,柴田芳樹
- 出版社/メーカー: オライリージャパン
- 発売日: 2017/12/15
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る
どう書くか迷う正規表現
1文字かつ2種類の文字にマッチさせる正規表現を書こうとする時、 [ab]
と (a|b)
で毎回迷ってるなーとアンケートとってみた。
[ab] って書くか (a|b) って書くか迷うのだけど、みんなどっちで書いてるんだろう。
— irof as a レガシー (@irof) 2018年3月9日
[ab]
が優勢。迷ってると言っても深刻なものじゃなく、書こうとしてから [
か (
かどっちを先に打つか指運に任せてる間に入るノイズ程度のものですが。
私の中のそれぞれの主張
指が勝手に打つまでに私の中のそれぞれの派閥がどういう主張をしているかを書いてみる。
[ab]
派の主張
記述が少なくて済むから効率的。文字数が少ないから読みやすい。数が増えても足すだけでいい。キャプチャしたいわけじゃないのに ()
を使うのは変。 ()
が入っているとそれを使っている箇所を探すことになる。(
や )
にマッチさせたいことは多いので、なるべく使いたくない。
(a|b)
派の主張
orなんだから |
を使いたい。キャプチャされるのは別に大した問題にならない。[ab]
だとぱっと見 ab
の2文字に見える。 ()
で囲んでる部分が可変なのがわかりやすい。
現在の選択
どうも (a|b)
を使うことが多い。
r(o|e)gai
— irof as a レガシー (@irof) 2018年3月9日
たぶん指は or
感を出したいんだと思う。
迷わないパターン
2文字以上なら (ab|cd)
になります。 [ac][bd]
とか書くと意味が変わるから当然。これは3種類以上でも。
1文字で3種類以上なら [abc]
あるいは [a-c]
にする。 (a|b|c)
は面倒だし読みにくいから。と書いてみたけど、後者を書くこともないとは言えない。さすがに [abcdefghijklmnopqrstuvwxyz]
とは書かないし、4種類以上を (a|b|c|d|e)
とか書くことはないと思う。飛び飛びの1文字複数種類だったら [irof]
とか並べるしかないと思うけど、そういうのを書かなきゃいけない状態は心当たりがない。
結論
どっちでもいいや。
「あるエンジニアがプログラムを紡いでいく様を見てみる」ライブコーディング・リプレイ
あるエンジニアがプログラムを紡いでいく様を見てみるでしたライブコーディングで言ったことや言わなかったことを書いてみます。
意識してるのは「コードをどまんなかに」です。
……あ、このスライドのブログ書き忘れてた。
スライド中の「えらぶ」はだいたいIDEの機能を指します。なのでライブコーディング中に使用したIDEの機能も挙げようと思います。基本的にデフォルトのつもりだけど、vimとの兼ね合いで変更してるのもあるので、そこはごめんなさい。あとMacです。今回はメソッド抽出とかクラス間移動とかダイナミックなのがなくて地味だけど、便利な子たちなので使ってあげてください。
リプレイ
今日の公開コーディングはスゴい新鮮だった🎵
— かわべ たくや (@kawakawa) 2018年3月5日
コミット後のソースには、どこに悩んだのか、どこにこだわったのかは残らないのですね。
実際のコーディングを見させて頂く事で、気づかされる事が多かったです。ほんと有り難うございました🎵#DevKan
ってことなので、コミットを追いながらいってみます。私の意図はどの程度コードに残ってるのかなー。
■最初はREADME
会場で何をするかを話して、READMEに書いてみました。思ったよりもでなくて、参加された方もどう参加したものかと手探りだったんだと思います。
■プロジェクトを作成して動作確認
Javaアプリケーションを作るときにまずやってること - 日々常々 で書いた内容ですね。 見事に 動作しませんでした 。本番あるある。
原因はオフライン時のライブラリ解決です。いつも通りと言いつつネットワークの無い状態なのでいつも通りじゃない。普段と違うことをしたら詰まるのは当たり前です。てことでネットワーク繋げて再開しました。(数時間前にネットワーク切断して素振りした時は大丈夫だったんです。ほんとですよ?)
SpringBootのApplicationクラスを作って実行、動作した、よし。ってところで最初の10分で目標にしてたところをクリア。
使用したIDEの機能
- 前回 挙げたやつ
Alt
+Ctrl
+R
Mainやテストの実行
使用するものの選択
build.gradle
に何を使うかを書きます。今回はJava 8、Spring Boot 2.0とJUnit 5、Assert J。
Spring Bootを使うのは後でSpring Shellを使おうと思っていたと言うのもありますが、Isolating the Domainに準じて作るつもりだったので。
■パッケージ名変更
前段であがった「 todo
を doto
とtypoしている」の指摘はあえてスルーしてました。typoを直すよりも動作確認を優先したかったからです。仮にtypoを直したとしても動作しなきゃ無駄になるし、typoは動作に影響しないことは知っているので、後回しです。動作確認ができたので、ここでリネーム。もちろんリネーム後はアプリケーションを実行して確認します。
新たに使用したIDEの機能
Shift
+F6
ファイル名のリネームAlt
+R
前回実行したものの再実行
■初期モデル作成
環境ができたら「TODOリスト」に対して現在捉えてるモデルを書きます。 実装としてはYAGNIですが、ホワイトボードや紙に走り書きするのと同じ感じでラフにコーディング。
たまにクラス図を眺めてみたり。この時点で頭の中で描けないようなのは詳細に立ち入りすぎかなーとも思います。
「先に設計しますよね?」
「先にある程度考えてたんですか?当然、普段は先に設計しますよね?」と言った質問がありましたが、冒頭に挙げた通り「コードをどまんなかに」なので、これが初期設計です。その時点でわかっていることを雑にコードで書きます。ただし、この時点はモデルの関連だけ。基本的に操作は書きません。この日は控えめだけど、基本的には認識してるものは全部書くので、もう少し多くなる。かも。
強調しておきますが、これは「初期モデル」です。絶対変わるし、間違ってる。立ち止まって落ち着いて確認する時間は後から何度でも取ります。欲しいのはコードからのフィードバック。間違っても完成と思わないくらいにラフなのを、ささっと短時間で書きます。間違ってたとしてもいいんです。と言うか、間違って捉えてたことのフィードバックを得るために書いてるところがあります。
新たに使用したIDEの機能
■タスクが取れる
TODOリストなんで、タスクがみれなきゃ困ります。 テストから書きたいところですが、テスト対象クラスでテストへ切り替えのキーを押せばテストクラスを作ってくれることは知っています。必ずセットで作るのはわかっているので、テスト対象クラスから作ります。無理矢理テストから作っても遅くなるだけなので。クラスを作り、テストクラスを生成し、テストを書き始める。ファイルの移動も少なくてスムーズです。メソッド名が微妙だなーと思いつつ、REDからGREENへ、とにかく通す。
テストメソッドは忠実にアサートファーストで書きます。だけど先走って型をStringにしてしまって「あれ?」と思ってたら、うらがみさんに突っ込まれました。素直に直したら「感心してたのに・・・」と残念がられました。くそう。
新たに使用したIDEの機能
⌘
+Shift
+T
新規テスト作成test
+Tab
(LiveTemplate)テストメソッド作成Alt
+Enter
(空気読め機能)TodoService.new
+Tab
(Postfix)コンストラクタ呼び出しに変換
■メソッド名変更
スペルチェックで警告だされてるし、それほどこだわりのない名前なので、メソッド名を変えます。割れ窓理論信者なので、コードを健全な状態で保ちたいのです。警告が多いと気づかなくなるのが勿体無い。もしこの名前にこだわりがあるなら、スペル辞書に登録して警告を出なくします。警告が出ている状態はREDに近い扱いですね。
新たに使用したIDEの機能
- スペルチェック(勝手に波線が出てるだけ)
Ctrl
+T
(リファクタリングメニュー)メソッド名のリネームShift
+F6
でもいいのだけど、押しにくいのでCtrl
+T
で選ぶ(と言っても一番上なのでEnter押すだけ)ほうが早い
■タスクが登録できる(RED)
ツッコミがあったところ。賛否両論、と言うか賛はあまりない感じでしたかね。
TODOリストの肝であるタスク登録に着手します。ベイビーステップで行くこともありますが、これくらいならいけるだろうと大きめの歩幅でいきます。ダメだったら戻ればいいんですよ。
まず検証方法を考えます。先ほどとれるようになったタスクをユーザーが見る場合、何かしらの形で文字列化されるはずです。なのでタスクを文字列化したらタスク名が入っている、としましょう。
- assertThat(actual).isNotNull(); + assertThat(actual.asText()).contains("yarukoto");
で、これが登録できればいい。タスクを登録する。
+ sut.add(task);
タスクはタスク名をつけて作る。タスク名はさっきアサーションで書いたやつ。
+ Task task = new Task(new TaskName("yarukoto"));
アサートファーストで下から順番。アサート書いて、それを実現するための操作(タスク登録)を書いて、必要な準備(タスク生成)をする。
新たに使用したIDEの機能
「テスト変えちゃうの?」
元のテストを残さずに変更しています。タスク登録してない状態のアサーションがなくなりました。これで「タスク登録しないとnullかもしれない」と疑心暗鬼になれます。
変更してしまう理由はいくつかありますが、まず第一にタスク未登録でnullになるような実装はしないので変更前の検証がエラーになることはありません。基本的に初っ端に書いたクラスやメソッドを導くためのテストは、メンテに向いていません。テストリファクタリングをしてもいいのですが、落ちないテストに価値はありませんし、未登録時のふるまいなんかはそのうちモデルとテストが要求してきます。なので「未登録時はnullでない」程度のテストは維持せず変えちゃうわけです。
「ドメインモデルとしては……」
「asText
がドメインにあるのがおかしい」と言った指摘がありましたが、ドメインは自身が文字列で表現されるときにどうするかを知っていると思います。「メソッド名 asText
にドメイン感がない」と言ったところはあるかもしれませんが、妥当な名前が出てくればその時変えればいいです。この時点ではそれなりな名前で十分だと思っています。名前には閉じた世界の中での一貫性が重要で、その検討をするにはドメインの理解や登場するモデルの数が不足しています。この時点でメソッド名の検討は時期尚早。早く作ってフィードバックを得るのが良いです。材料が少ない状態で検討しても的外れになりやすいですし、何より分析麻痺になるのが怖い。よほど精通したドメインでもない限り、モデルが出揃ってない状態で検討できることではありません。
コードスメル: 知識がテストに書かれている
このコードは少し異臭がします。「TaskのコンストラクタがTaskNameを受け取る」と言う知識はテストクラスが持っているべきではないことでしょう。Taskインスタンスの生成方法を知る必要はありません。こう言うのがテストにあると、Taskを要求する全ての場所に同じコードがクローンされます。そこはかとなく臭いので、たぶん近い将来このコードはテストからは消え去ると思われます。でもまだ重複していないので、何かを考えるには時期尚早という判断をします。本当に複数箇所にクローンされるのか、生成のバリエーションが出ないか、などを少し待ってみてもいいかなー、と思ったり。もちろん油断すると大変なことになるので、臭いには気をつける。
■タスクを登録できる(GREEN)
さて、最速でテストを通しましょう。add
されたタスクをリストに保持し、asText
メソッドはタスクリストを文字列表現すればよい。タスクはタスク名を文字列表現できればよい。タスクの文字列表現は、とりあえず toString
でいいや、と。余計な色気は出さず、とにかく最速でGREENにします。(それでもなんか詰まった気がするけど、忘れた。)
実はこの時点でリストを持ち出すのはYAGNIで、テストを通すだけならフィールドで十分だったりします。でも気にせずリストにしちゃいます。フィールドだとnullが登場しかねないから避けたいと言うのもある。リストを導くために複数登録するテストにしちゃうのはありかもだけど、面倒なのでしない。
新たに使用したIDEの機能
⌘
+Alt
+F
ローカル変数のフィールド化
「テストの検証が厳密でない」
この実装だと実際の文字列は "[yarukoto]"
で、テストの検証は contains("yarukoto")
になっていますす。当然 isEqualTo("[yarukoto]")
と書いてもテストは通りますが、後者でないことに違和感があったようで、疑問の声がありました。
前段のテストで描いた未来は「登録したタスク名が読み取れる」です。登録した文字列が含まれてればOK。前後に何がついていようと別に困りません。困るのは "yarukoto"
を登録したのに "yarukoto"
って文字列が見当たらないこと。なので contains
を選択しています。Taskのequalsなどで検証していないのは、同じTaskかどうかにあまり興味はないことと、文字列化はアプリケーションに必要なことと、テストのためだけのhashCode/equalsは開発を加速しないからです。
「登録したタスク名が読み取れる」は、おそらく完成したとしても変わりません。テストはピンポイントで重要なところだけ抑えないと、テスト対象をコールタールに沈めて身動き取れないものにしてしまい、開発の足を引っ張ります。例えば仮に isEqualTo("[yarukoto]")
としてしまうと、テストが ArrayList
の文字列表現に依存します。本来そこに依存関係はないので実装詳細に寄りすぎる検証となり、例えば「改行区切りにしたい」となったら即死です。(「改行区切り」を実現したくなったら、複数件を登録して「改行区切りになるよー」ってテストを書くとは思いますが。)
現地ではうまく説明できなかったと思っていたのだけど、存外伝わってたようでほっとしました。
やってみて
ってところで、ちょいちょい説明しながら、ネットワークとキーボードのトラブルもあったけど、合計20分とちょっとかな?最後はSpringShellを紹介するのに使いました。
「説明できるのが良いコード」としょっちゅう言ってる私ですが、だいたいこんな感じで自分に説明(言い訳)はしてる感じです。無意識にやってるものも多いですが、たまに確認はしてます。 でも今回のイベントで他人に説明するのは面白かったです。思った以上に言葉が出てこず、伝わってる感じもしなくて。変なこと言っちゃってるなーと内心ドキドキしながらやってました。
そういえばスライドでは「説明する機会はない」とか言ってたけど、なんかその機会貰えましたね。ありがとうございました。
- 作者: Kent Beck,和田卓人
- 出版社/メーカー: オーム社
- 発売日: 2017/10/14
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
せっかく買ったし、久々に読み直そう。