日々常々

ふつうのプログラマがあたりまえにしたいこと。

既存を正解とする減点ゲームへの違和感

オンラインミーティングとか、リモートワークとか、インターネットを介する話のメリットデメリットとかをみていると、「既存になるべく近づけよう」としているところに違和感を感じてる。

電子書籍もそうで、いかに紙をデジタルで表現するかに腐心しているように感じる。ページめくりのエフェクトなんて要らないのだけど、一時期ついてたりしたよね。紙っぽさにヒントはあるかもしれないけど、紙っぽくある必要はないはずなんだ。

差は減点対象となり、足切りとして機能している。減点ゲームをクリアしないと候補にもならない。そういう事実はある。けれど、足切りになりうる欠陥と、欠陥でないただの差異を混同している感じがする。

完全な代替なんてできないなんて認識は誰もがしているはずが、油断するとどこかに置いてきてしまいがち。ときに完全な代替ができることを宣言させられる圧力もあったりする。よくわからないけれど、そうでないと(それくらいでないと)進められないとかなんとか、言わせた側からはそんなよくわからない話も聞く。嘘をつかせる儀式に何の意味があるんだろう。

いくらデジタルにアナログっぽさを求めても、極限までアナログに近づけたデジタルはアナログの劣化コピーにしかならない。そんなことはわかると思うのだけど、それでも細かいところまでアナログっぽさを求めてる感じがする。

いくつかの理由づけはできなくはない。例えば「ただ変化を嫌っているだけ」とか。派生になるけれど「既存から変わるのを良しとしない人々を抵抗勢力と見立て、それを封殺するために既存を満たした上でメリットを強調したい」とか。後者は暴走品質を生み出す考え方の一つだと思う。

だからと言って妙案があるわけではない。 私は電子辞書と紙の辞書は両方使うけれど、使う場面は明らかに違う。仕事もリモートでいいものもあれば「これは対面だなぁ」と思って調整することも多々ある。漫画も紙、KindleiPhoneMacとまちまちで、今の所どれかに統一される気配はない。

きっと過渡期なんだろう。もしかしたら違うものが同じ名前で呼ばれているだけなのかもしれない。今「仕事」と言ってるものは、2種類(もしくはそれ以上)に分割され、仕事Aはリモートが当然で、仕事Bは対面が当然、みたいな感じになって、それらが「仕事」と一緒くたに呼ばれていたことなんて忘れられるのかもしれない。

とりあえずスクロールの漫画はとても良いと思う。あれは紙じゃ無理だ。できれば1話ごとに読み込みとかにせず、延々とスクロールさせてほしいもんだ。Kindleの合本も悪くないけれど、あれやるなら索引はちゃんとやってほしい。一昔前のゲームブックをデジタルで表現するなら読み進めながら文章が変化していく感じかなーと思ってたけど、よく考えたらそれってアドベンチャーゲームが済ませてるな。そこから少し捻ると、マニュアルとか技術書は利用者に応じて文章が変化していったりするといいよなーと思ったりする。読み飛ばすところもあるし、読む順番も人によって変わるのだから、その人の読みたい感じに構築されるようなのはどうかなーと。

ともかく、既存と比べて劣ってるところを何とかするのに注力するのはほどほどにしていいんじゃないかなー。コストに対して得られるものの上限は見えてるわけだし。対処するにしてもそのまま再現するんじゃなく、エッセンスを抽出して取り入れる方向で考えたいなと。

Tech Deep Dive #2 in Osakaに参加して思ったこと

techdeepdive.connpass.com

Tech Deep Dive #2 in Osakaに「ブログ書いてくれる人」の枠で参加したので、書きます。枠空いてたので一般参加枠でもよかったんだけど、なんとなく。

Webアプリに低レイテンシ・高可用性を求めるのは間違っているのだろうか reloaded

www.slideshare.net

再演ということでスライドは公開されています。reloadedということでいくらか更新/調整は入っていますが、大筋はこの通りでした。

話は「性能」と「可用性」をポイントとしていて、その背景に「クラウドにより技術がコストに直結する」という話は新鮮でした。性能が悪いと高価なインフラが必要になる。即時復旧やデータ整合性がコストに直結する。オンプレだと初期に固定されていたものが可変なパラメータとなったことで浮き彫りとなっている形ですね。

よくあるWebアプリケーションの構成をあげて、性能や可用性の面でどこが問題となり得るか。ある程度簡略化された構成だからこそ、それぞれの場所と結合部分に何かしらの問題がありえることは当然かと思います。これはスライドの図がわかりやすいですね。

https://image.slidesharecdn.com/techdeepdivewebapplication-171222125018/95/web-15-638.jpg?cb=1513947306

ああ、うんあるある。

タイトルの「高レイテンシ」を話は、ECサイトでどれだけの人数の同時購入を短時間に捌けるかと言ったシナリオ。パターンとして2種類、10,000商品に購入がそれなりに分散される場合と、1つの商品に購入が集中する場合。スケールアウト/スケールアップが効きやすい前者と、スケールアウト/アップが効きづらい後者というわかりやすい対比ですね。ここにキャッシュとインメモリデータグリッドのアプローチがどのような影響があるかの実測値で示され、確かにそうなるよねーと。

アーキテクチャや計測結果から読み取れることの説明を通して「この場合はこういうことが考えられる、こういう問題が起こり得る」と言った話が入り、実践する前に知識として持てているとだいぶ違います。

事象を聞いていると、2014年のJavaDayTokyoでせろさんが話された 実例Javaトラブルシューティング! 〜稼働中のシステムを立て直した半年間の軌跡 の「DBがボトルネックなのに
 なんでAPサーバを
 スケールアウトするの?」とかを思い出しました。

https://image.slidesharecdn.com/anvg5ezbspmdzm9xoszp-signature-c1ca481fcc7191d99b160983ee307625e1b596fe1469c0d72735ca8985e1d83a-poli-141115014132-conversion-gate02/95/java-66-638.jpg?cb=1440743796

キーワード

  • 今の負荷は将来の負荷ではない
  • 障害の復旧時間を(事前に)測っていますか?
  • クラウドだと気軽にスケール頼りになっておかしくなる

もしもみなみんがアプリケーション開発者だったら

www.slideshare.net

前の話をうけて別の視点から、連続セッションだからこそできる話だと思いました。「構成を考えるのはできるけれど、実際作るのは難しい」とか、パフォーマンスの問題になったら「気軽に並列度をあげてたりすると、別のところに予期しない影響を与えるものだから徐々にしましょう」とか、「シャーディングは適切に分散されるように設計しないと効果がない」とか。

セッションはオペミスや性能などのよくある問題を挙げて、それに対する一般論の解決案が起こしうる問題の説明、理想的な解決や現実的な解決の提示という形で構成されていました。いくつかは踏んだこともあり、心当たりがありそうな参加者も多かったのが印象的でした。

インフラ側の言葉は初耳なものも多かった。

キーワード

  • 時間↓ = 処理量↓ / (速度 * 並列度)↑
  • まずは処理量をなんとかしましょう

感想

前提知識のハードルが高めに感じましたが、内容自体は一般的な知識の範囲で説明できるものだったと思います。それらを繋ぎ合わせてひとつなぎにしているところにこのイベントの価値があるのかなと思いました。ここで話された一通りを3時間程度で話せる気はしないので、素直にすごい。

一般的な知識の範囲とは言うものの、ギリギリいっぱいまで満たされていたと思います。ここから踏み込んだら現場の背景も全部入れないと的外れで「あー、うん、そうですよねー」な話にしかならない気がします。

と言うことで、経験が浅い人にこそ聞いてほしい話なんじゃないでしょうか。この知識がx年前にあれば、あの時もっと楽できたのになぁ、なんて。

以下、ポエム

仕事スイッチが入ってしまい、純粋に技術の話として楽しめなくなったポエム書きます。「Tech Deep Dive」に技術だけを求めた私の期待が外してただけなんでしょうけど、「おースゲー!なるほどー!」ってなりたかった。

事前にパフォーマンスを含めてアーキテクチャを検討する。もちろん重要なのですが、そんなの考えても仕方ないから今は考えないでおこうぜというバランスがあります。例えば「今はそれは考えない、何故なら・・・。このような条件が整ったら検討をはじめ・・・。それで間に合うように・・・・。」と言った判断を保留するアーキテクチャを採用する(かどうかすらも判断する必要があります)。このような「今は考えないでおこうぜ」という判断をするために、このイベントでの語られた知識は持っておく必要はありますが、その道具を常に振るうべきかというと否です。このバランスを取れない人にはミスリードになるのではないかと思ってしまいました。(そういうエンジニアは存在して、悪いとは言いづらいのだけど、そういう人が「エンジニアは話が通じない」と思われる原因になってるよなーというのもあって、もにょもにょ……)

例えば「10,000商品」と「1商品」と言ったシナリオがありましたが、これもECサイトならよくあるのかもしれませんが、そのプロダクトが扱う業務の性質としての取捨選択の上で成立するものです。少なくとも「1商品に注文が殺到し、それが同期処理でなければならず、待たせることがビジネスリスクだったり、本来得られるはずの利益を損ねる」ところまでが前提であり、そうでないものには無用の長物です。もちろんそんなことは当然の前提としているのだとは思うのですが、なんとなく「こういうのがあったらよくないですか?」みたいなところから銀の弾丸臭がすごく感じました。技術ってそういうものじゃない、と私は思ってます。

「インメモリデータグリッド最強」で話が終わるところにはなんとも言えません。この解決策が招く問題もあり、そちらを対処できなければ他のところに問題が移動するだけです。他と比べて重視しているものが違うという口頭でのやりとりもありましたが、それまで他のアーキテクチャでの懸念を挙げていたのにここだけ語られなかったところに疑問はありました。もちろん「でも、お高いんでしょう?」という話もありましたし、ハードウェアスペックがボトルネックにならないのも前提として提示されてはいますが、インメモリデータグリッドがパフォーマンスを発揮する前提条件がそもそも満たされないことも多くあります。認知度が低いのも確かに事実ではあるのですが、ミッションクリティカルシステム以外で使われない理由はそれだけではないわけで。

また気になったところとして、「アプリはインフラを意識したくない」というものの実現手段。疎結合は意識するところなのですが、「これで意識しなくていい」という方法がDAOの実装差し替えだとすると、これは多くの場合アプリケーションエンジニアの意識すべきレイヤーにあります。SQLを書かない世界(JPAでJPQLすら書かない場合などでしょうか)では妥当なのかもしれませんが、データベース設計などがアプリケーションエンジニアの守備範囲にある以上、意識せざるを得ない状況は変わりません。これを下手に「意識しなくていい」と言ってしまうと不適切に伝わる現場もあるのは悩ましいところですね。

金の弾丸は大抵の場合に有効です。適切なところに投資すれば、多くの場合にコストは抑えられるものです。一方、慎重になりすぎてか単にケチなのかはわかりませんが、このイベントで語られたようなものに一切手を出さず、独自技術症候群なのかなんなのかは知りませんが、力技で手当たり次第に場当たり対処して結果的に高くついているプロジェクトも多くあるのも悩ましいところです。

このイベントに限らず、アプリ/インフラと分けられた文脈で「意識しなくていい」「考えなくていい」という話はよくされます。でも「考えなくていい」というのはちょっと違うんじゃないかなと思いました。むしろ考えないためは知識を持つ必要があるはずです。知らずに領域を侵すことは頻繁にあり、それは知識が無いと止めるすべはありません。認知の外ってやつですね。知った上で、考えないでいいようにと言うよりも、邪魔しないように、適切に扱う必要があります。こういうものに「意識しなくていい」「考えなくていい」「勝手にやってくれる」となった瞬間に、なぜか「その領域の知識を持たなくていい」と解釈されるものなんだよね・・・違うよね。

とかなんとかだらだらと書きましたが、イベントも話もとても良かったとは思っています。長時間セッションで疲れたってのはありますが、続けて開催されるなら参加したいなぁ。

ベタープログラマ第二部の感想

第二部「練習することで完璧になる」を読みました。

読んだのは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の考え方とテクニック

ベタープログラマ ―優れたプログラマになるための38の考え方とテクニック

どう書くか迷う正規表現

1文字かつ2種類の文字にマッチさせる正規表現を書こうとする時、 [ab](a|b) で毎回迷ってるなーとアンケートとってみた。

[ab] が優勢。迷ってると言っても深刻なものじゃなく、書こうとしてから [( かどっちを先に打つか指運に任せてる間に入るノイズ程度のものですが。

私の中のそれぞれの主張

指が勝手に打つまでに私の中のそれぞれの派閥がどういう主張をしているかを書いてみる。

[ab] 派の主張

記述が少なくて済むから効率的。文字数が少ないから読みやすい。数が増えても足すだけでいい。キャプチャしたいわけじゃないのに () を使うのは変。 () が入っているとそれを使っている箇所を探すことになる。() にマッチさせたいことは多いので、なるべく使いたくない。

(a|b) 派の主張

orなんだから | を使いたい。キャプチャされるのは別に大した問題にならない。[ab] だとぱっと見 ab の2文字に見える。 () で囲んでる部分が可変なのがわかりやすい。

現在の選択

どうも (a|b) を使うことが多い。

たぶん指は or 感を出したいんだと思う。

迷わないパターン

2文字以上なら (ab|cd) になります。 [ac][bd] とか書くと意味が変わるから当然。これは3種類以上でも。

1文字で3種類以上なら [abc] あるいは [a-c] にする。 (a|b|c) は面倒だし読みにくいから。と書いてみたけど、後者を書くこともないとは言えない。さすがに [abcdefghijklmnopqrstuvwxyz] とは書かないし、4種類以上を (a|b|c|d|e) とか書くことはないと思う。飛び飛びの1文字複数種類だったら [irof] とか並べるしかないと思うけど、そういうのを書かなきゃいけない状態は心当たりがない。

結論

どっちでもいいや。

「あるエンジニアがプログラムを紡いでいく様を見てみる」ライブコーディング・リプレイ

あるエンジニアがプログラムを紡いでいく様を見てみるでしたライブコーディングで言ったことや言わなかったことを書いてみます。

意識してるのは「コードをどまんなかに」です。

speakerdeck.com

……あ、このスライドのブログ書き忘れてた。

スライド中の「えらぶ」はだいたいIDEの機能を指します。なのでライブコーディング中に使用したIDEの機能も挙げようと思います。基本的にデフォルトのつもりだけど、vimとの兼ね合いで変更してるのもあるので、そこはごめんなさい。あとMacです。今回はメソッド抽出とかクラス間移動とかダイナミックなのがなくて地味だけど、便利な子たちなので使ってあげてください。

リプレイ

ってことなので、コミットを追いながらいってみます。私の意図はどの程度コードに残ってるのかなー。

github.com

■最初は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に準じて作るつもりだったので。

■パッケージ名変更

前段であがった「 tododototypoしている」の指摘はあえてスルーしてました。typoを直すよりも動作確認を優先したかったからです。仮にtypoを直したとしても動作しなきゃ無駄になるし、typoは動作に影響しないことは知っているので、後回しです。動作確認ができたので、ここでリネーム。もちろんリネーム後はアプリケーションを実行して確認します。

新たに使用したIDEの機能

  • Shift + F6 ファイル名のリネーム
  • Alt + R 前回実行したものの再実行

■初期モデル作成

環境ができたら「TODOリスト」に対して現在捉えてるモデルを書きます。 実装としてはYAGNIですが、ホワイトボードや紙に走り書きするのと同じ感じでラフにコーディング。

f:id:irof:20180308171121p:plain

たまにクラス図を眺めてみたり。この時点で頭の中で描けないようなのは詳細に立ち入りすぎかなーとも思います。

「先に設計しますよね?」

「先にある程度考えてたんですか?当然、普段は先に設計しますよね?」と言った質問がありましたが、冒頭に挙げた通り「コードをどまんなかに」なので、これが初期設計です。その時点でわかっていることを雑にコードで書きます。ただし、この時点はモデルの関連だけ。基本的に操作は書きません。この日は控えめだけど、基本的には認識してるものは全部書くので、もう少し多くなる。かも。

強調しておきますが、これは「初期モデル」です。絶対変わるし、間違ってる。立ち止まって落ち着いて確認する時間は後から何度でも取ります。欲しいのはコードからのフィードバック。間違っても完成と思わないくらいにラフなのを、ささっと短時間で書きます。間違ってたとしてもいいんです。と言うか、間違って捉えてたことのフィードバックを得るために書いてるところがあります。

新たに使用したIDEの機能

  • Alt + Enter (空気読め機能)コンパイルエラーのフィールド型名から新規クラス作成
  • + N コンストラクタ作成
  • Tab 補完
  • + E 最近開いたファイル

■タスクが取れる

TODOリストなんで、タスクがみれなきゃ困ります。 テストから書きたいところですが、テスト対象クラスでテストへ切り替えのキーを押せばテストクラスを作ってくれることは知っています。必ずセットで作るのはわかっているので、テスト対象クラスから作ります。無理矢理テストから作っても遅くなるだけなので。クラスを作り、テストクラスを生成し、テストを書き始める。ファイルの移動も少なくてスムーズです。メソッド名が微妙だなーと思いつつ、REDからGREENへ、とにかく通す。

テストメソッドは忠実にアサートファーストで書きます。だけど先走って型をStringにしてしまって「あれ?」と思ってたら、うらがみさんに突っ込まれました。素直に直したら「感心してたのに・・・」と残念がられました。くそう。

新たに使用したIDEの機能

  • + Shift + T 新規テスト作成
  • test + Tab (LiveTemplate)テストメソッド作成
  • Alt + Enter (空気読め機能)
    • コンパイルエラーのローカル変数名からローカル変数定義作成
    • コンパイルエラーのローカル変数型名から新規クラス作成
    • コンパイルエラーのメソッド呼び出しから対象クラスにメソッド作成
  • TodoService.new + TabPostfix)コンストラクタ呼び出しに変換

■メソッド名変更

スペルチェックで警告だされてるし、それほどこだわりのない名前なので、メソッド名を変えます。割れ窓理論信者なので、コードを健全な状態で保ちたいのです。警告が多いと気づかなくなるのが勿体無い。もしこの名前にこだわりがあるなら、スペル辞書に登録して警告を出なくします。警告が出ている状態は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の機能

  • Alt + Enter (空気読め機能)
    • コンパイルエラーのコンストラクタ呼び出しから対象クラスにコンストラクタ作成
    • コンストラクタの仮引数名からフィールドを作成

「テスト変えちゃうの?」

元のテストを残さずに変更しています。タスク登録してない状態のアサーションがなくなりました。これで「タスク登録しないと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 の文字列表現に依存します。本来そこに依存関係はないので実装詳細に寄りすぎる検証となり、例えば「改行区切りにしたい」となったら即死です。(「改行区切り」を実現したくなったら、複数件を登録して「改行区切りになるよー」ってテストを書くとは思いますが。)

mdstoy.hatenablog.com

現地ではうまく説明できなかったと思っていたのだけど、存外伝わってたようでほっとしました。

やってみて

ってところで、ちょいちょい説明しながら、ネットワークとキーボードのトラブルもあったけど、合計20分とちょっとかな?最後はSpringShellを紹介するのに使いました。

「説明できるのがいいコード」としょっちゅう言ってる私ですが、だいたいこんな感じで自分に説明(言い訳)はしてる感じです。無意識にやってるものも多いですが、たまに確認はしてます。 でも今回のイベントで他人に説明するのは面白かったです。思った以上に言葉が出てこず、伝わってる感じもしなくて。変なこと言っちゃってるなーと内心ドキドキしながらやってました。

そういえばスライドでは「説明する機会はない」とか言ってたけど、なんかその機会貰えましたね。ありがとうございました。

テスト駆動開発

テスト駆動開発

せっかく買ったし、久々に読み直そう。

Javaアプリケーションを作るときにまずやってること

DevLOVE関西で実験的なイベントをさせてもらいました。

devlove-kansai.doorkeeper.jp

このイベントのおかげで、自分がアプリケーション作るときに最初にどうしてるかを確認できたので晒してみます。誰かの役に立つかどうかは知らない。あ、Macです。ある程度はWindowsでもいけると思うけど、ある程度だと思う。

なおSTSとかIDEのプロジェクト生成機能を使えば一発で済むことだったりする。宗教上の理由で使わないけど。

やってること

基本的にマウスを触る必要はないです。それぞれの詳細は次の通り。長く見えるかもしれませんが、(IDEAの起動とgradleのjarのダウンロード時間を除けば)全部で30秒くらいです。

作業ディレクトリを作る

% git init {作るもの}
% cd {作るもの}

mkdir {作るもの} して cd {作るもの} して git init することもあります。 たまにこの段階で README.md を作ってコミット、ライセンスファイルを作るときはGitHubで作ってからcloneします。

ビルドスクリプトを用意する

記憶かける程度ならそれで。

% vi build.gradle

だいたいこれくらい。たまに cat>build.gradle で書いたりもしますが、気分です。

apply plugin: 'java'
repositories.jcenter()
dependencies {
  testCompile 'junit:junit:4.12'
}

Javaのバージョンやエンコードの設定などが無いですが、一旦作ってから後で変えればいいと思っているのでこだわりません。

これよりも多く書く場合(作るものが決まってるとか)は次のいずれかになります。イベントでは「手元のテンプレートをコピる」をしました。

  • 手元のテンプレートをコピる
  • ググる
  • Spring Initializr

手元のテンプレートをコピる

% cp {テンプレートのあるとこ}/build.gradle ./

ググる

ググって出てきたの(主にGettingStarted)を切り貼りします。

MavenCentralRepositoryを検索

使いたいgroupIdとかがわかっている場合はこっちで検索。

https://mvnrepository.com/

Spring Initializr

https://start.spring.io/

便利ですね。

必要なディレクトリを作る

% mkdir src/main/java src/main/resources src/test/java src/test/resources

これをするエイリアスを作っています。前段のテンプレートをコピるのをあわせたのもあるので、そっちを使うことが多いです。

alias mkjavadirs='mkdir src/main/java src/main/resources src/test/java src/test/resources'
alias initSpringProject='mkjavadirs && cp {テンプレートのあるとこ}/springboot.gradle ./build.gradle'

と言う感じ。

IDEを起動して取り込む

% idea build.gradle

IntelliJコマンドラインランチャーを使います。 Tools / Create Command-Line Launcher から作れるもので、 idea コマンドで新しくプロジェクトが開けます。 コマンドラインラインチャーを作らないなら、IDEAを立ち上げてからOpenでbuild.gradleを選ぶのですが、若干面倒。

テストを書いて実行する

こんな感じのテストを作って実行します。

import org.junit.Test;

public class HogeTest {
    @Test
    public void test() throws Exception {
        
    }
}

即捨てるのでデフォルトパッケージです。確認したいのは環境です。テストが実行できる環境になっているかとか。

面倒に思えるかもしれませんが、テストの作成は以下でできます。

  • Projectの src/test/java ディレクトリを選択
    • [Command]+1 から上下キー、もしくはマウスクリックします
  • [Command]+n で新規のメニューを開いて [Enter]
    • 新規クラス作成のダイアログが出る
  • HogeTest [Enter]
    • クラスができる
  • [Esc]
    • エディタがアクティブになる
  • O
    • 新規行ができる。vimすばらしい。
  • test [Tab]
    • Live Templatesによりテストメソッドが生成される
  • test
    • テストメソッド名を入力
  • [Ctrl]+[Alt]+r [Enter]
    • テストが実行される

キーを押す回数は30回程度、今やってみたら12秒でした。( HgeTest ってtypoったけど。) これをサボると、たまに「なんかビルドできない」とかで悩んだりして馬鹿らしいので、ビルドスクリプトを切り貼りしたときや、環境の更新をした後などは必ずやります。

SpringBootアプリケーションの場合

テストではなく Application クラスで代用します。 起動すれば環境としては問題無いと判断できます。

コミットする

% git add .
% git commit -m"init"

いつでもここに戻ってこれるように。

この段階のリポジトリ

GitHub - irof/DevKan20180305 at a4ccd5d761ccb58cd66bdbec1516cfb2f41852c6

コミットとしてはこの2つ。

さらに前段のことや、周囲のことや、マルチプロジェクトの場合とか、とかとか

WEB+DB PRESS vol.98 「実践!イマドキのビルド環境」に書いてます!

WEB+DB PRESS Vol.98

WEB+DB PRESS Vol.98

Spring Boot Gradle Pluginの話(Spring Boot 1.5.x & Gradle 4.5)

Spring Boot 1.5.x とGradle 4.5 の話です。Spring Boot 2.0とGradle4.6が出たので、埋葬がてら書いておきます。 どんな感じで要らない話になったのかは最後に書いてます。

Spring Boot Gradle Pluginの機能

67. Spring Boot Gradle plugin

たいだい*1 だいたいリファレンスは先頭が重要です。なぜか読み飛ばされがち。コードがあったらそっち読んじゃうのはわかるけど。

The Spring Boot Gradle Plugin provides Spring Boot support in Gradle, allowing you to package executable jar or war archives, run Spring Boot applications and use the dependency management provided by spring-boot-dependencies.

  • allowing you to package executable jar or war archives
    • executable jarを作るための bootRepackage タスクが追加され、 jar タスクの後に自動的に実行されるようになる。
  • run Spring Boot applications
    • bootRun タスクが追加される。
  • use the dependency management provided by spring-boot-dependencies
    • dependencies のうち、Springが管理されるものはバージョンの記述が不要になる。

つまり、プラグインの機能は主に3つ*2ということ。

これからわかるのは、Spring Boot Gradle PluginはSpringBootアプリケーションプロジェクト(Mainクラスが入ってるやつ)には適切だけれど、SpringBootアプリケーションから使用されるモジュールにはdependency management以外は不要なので微妙ってことです。2/3を使ってないわけですからね。

単一モジュールで作っているなら問題にならないだろうけど、共通で使うモジュールとか作る際にうっかり使っちゃうと困ることになる。使わないもの( bootRun タスクがそれ)はまだいいのだけど、勝手に動作する( bootRepackage タスクがそれ)のが問題なのです。

起こりうる問題

たとえばこういう問題が起こります。

  • Main クラスがないのでビルドエラーになる。
  • (仮に最初のを回避したとして)依存ライブラリがjarの中に入る。

他にもあるかもしれないけど、知らない。

対処

  • (だめ) Mainクラスを作る
  • (だめ)bootRepackage.layout = 'MODULE'
  • bootRepackage をしない
  • Dependency management plugin だけ使う

それぞれ書いていきます。

Mainクラスを作る

Mainクラスがないからエラーとか言われるからってMainクラスを作るやつ。要らないもの作っちゃだめですね。

レイアウトをMODULEにする

Mainクラスがなくてもビルドできます。モジュールを作ってるんだからそれっぽく見えるかもしれませんが、jarの中に依存ライブラリがぶっこまれます。そんなjarいらない。無駄にでかくなるし。

bootRepackageしない

そもそもrepackageする必要がないので止めちゃいましょう。

bootRepackage {
    enabled = false
}

シンプルだし、プロジェクトによって使うGradle Pluginを変えなくていいというのはメリットかもしれません。技術スタックは少ない方がいいので。

でも使用しないタスクが入るところは微妙かも。

Dependecy management pluginを使う

Sprign Boot Gradle Pluginは中で Dependency management plugin を使っています。

GitHub - spring-gradle-plugins/dependency-management-plugin: A Gradle plugin that provides Maven-like dependency management functionality

コードではこんな感じでとりこんで、こんな感じspring-boot-starter-parent をインポートしてます。これをDependency management Plugin を使う形で build.gradle に書くとこうなります。

plugins {
    id "io.spring.dependency-management" version "1.0.4.RELEASE"
}

...

dependencyManagement {
    imports {
        mavenBom 'org.springframework.boot:spring-boot-starter-parent:1.5.10.RELEASE'
    }
}

これで余計なタスクも入らない。Spring Bootアプリケーションを作ってるわけではないのだから、たぶんこれでいいはず。

モジュールの独立性とか言ったら(bomとはいえ)SpringBootに依存するのはどうなのとかあるでしょうけど、バージョンの整合性を取るのは面倒だし、自分たちしか使わないモジュールならこれでよかろーです。

Spring Boot 2.0では

マイグレーションガイドに書かれている通り、Spring Boot Gradle Pluginからdependency managementは除外されています。

Spring Boot 2.0 Migration Guide · spring-projects/spring-boot Wiki · GitHub

あと変わったことといえば、Spring BootのリファレンスのSpring Boot Gradle Pluginの記述がスッキリしてます。(最初に出したのと見比べて見てください)

69. Spring Boot Gradle Plugin

リンクされてるSpring Boot Gradle Plugin Reference Guideがしっかりしたものになってる。あとAPIドキュメントもリンクされててて開きやすくなったのも嬉しい。

Gradle 4.6では

Gradle 4.6 Release Notes

BOMをインポートする機能がはいったのでDependency management pluginもいらない。 かも。まだ試してない。

リリースノートに書いてる通り、 settings.gradle に1行いります。

github.com

こんな感じ。

*1:

*2:Agent指定の何かとかエンコーディング変えたりとかもあるのだけど、これらは補助的なものなので書かれてないっぽい。