エンジニア的なネタを毎週書くブログ

東京でWebサービスの開発をしています 【英語版やってみました】http://taichiw-e.hatenablog.com/

IntelliJ IDEAのSequenceDiagramプラグインが便利

この、SequenceDiagramプラグインで、
vanco.github.io

こんな感じのシーケンス図が描けます。
f:id:taichiw:20190301195001p:plain

素敵なのが、

  • 好きなメソッドから始められる
  • 深さが選べる

f:id:taichiw:20190301195116p:plain
点です。

コードレビュー時などに、深さ2でServiceクラスから始めると、私の好きな感じのコードになっているかどうかがよくわかります。
参考:持論:Application Layerがシンプルなコードは読みやすい - エンジニア的なネタを毎週書くブログ


以前は、こういったコードから自動生成された図は、ノイズが多くて使い物にならない…と思っていました。
けれども最近は、
「自動生成された図が汚い、ということはそもそもコードが悪いのでは?」
と思うようになってきました。

f:id:taichiw:20190301195934p:plain
例えばこれ。
右から左に呼び出しが伸びてるのが気持ち悪いです。
以前の私であれば、「この呼ばれているクラスを右に持っていければいいのに… ツールがイケていない!」と思っていたところでしたが、
そもそもこんな向きに呼び出しがかかる、元のコードが悪いのではないでしょうか。

こういう考え方になってきたのは、12月のJJUG CCでirofさんの「JIGを使った設計」を聞いたからなのかなぁ… と思います。

【極論ですけど】JavaでforEachを使ったら負けだと思う

正直使い所がわからないんですよね、forEach.
ログとかprintfとかする以外は…

コレクションを更新するな

未だに時々見るのがこういうコード。

List<X> list = new ArrayList<>();
適当なコレクション.forEach()
  .map(t -> list.add(t.getXXXX()));

これとっても良くない。

スレッドセーフでない

上記をあまり深く考えずにパラレルストリームにしちゃいますと

List<X> list = new ArrayList<>();
適当なコレクション.forEach().parallelStream()
  .map(x -> list.add(x.getXXXX()));

ArrayListに対する追加処理がスレッドセーフでないため、確率的に不具合が起きてしまいます。*1

パラレルにしなければいい…のですが、Project Lambdaの目的の一つがマルチコアへの対応なわけで、「parallelStreamを使って並列化できないものはラムダ式で書くな」くらいに私は思っています。(私の勝手ルールではありますが)

Lambdaで更新するの「関数」でない

Lambda式内では外で定義した変数に代入ができません。
これは、「状態」を極力排除することによって、バグの入り込む余地を減らすため… と私は認識しています。
ですが、上記のようにコレクションに対する操作や、Setterでの代入はJavaの言語仕様上できてしまいます。

…とはいえ、できるからやっていい、というものでもないと思います。

「リストを作る」と明確に書く方法がある

List<X> list = 適当なコレクション.stream() //parallelも可
  .map(X::getXXXX)
  .collect(Collector.toList());

そもそもこのように、リストを作るための書き方が用意されているのに、これを使わずに汎用的なforEachでなんとかする… というのは美しさに欠けます。

更新しないとなると、forEachは出力くらいしか使いみちがないのでは

上で書いたとおり、コンパイルエラーにはならない… とはいえ、Lambda式内で何かを「更新」するのは不適切と考えます。
そうなると、冒頭に書いたとおり、何かの出力くらいしか使いみちがないなぁ… ということになり、
よっぽどのことがない限り、「forEachを使ったら負け」だと思うのです。

*1:私が経験したことのある事象だと、「適当なコレクション」よりも多い要素がlistに追加されるという謎現象が発生し、その要素をgetするとnullが入ってる → ヌルポで落ちる というものを何度かみました。

"Rename Hackathon"をしてみました

チーム内で、お祭り的なイベントとして、"Rename Hackathon"というイベントをチーム内でしてみました。

ルール

  • 参加者は30分間ひたすらプルリクエストを送り続ける
  • 「リネーム」のみ許可。名前を変更する対象は、クラス、メソッド、変数名、パッケージ名 など何でも良い
  • 1つのプルリクエストは、1単語だけ直して良い。複数の単語を修正したプルリクエストはルール違反として却下*1
  • 30分後の「リネームタイム」の後に通常のコードレビューを行い、Approveされたものだけがマージされる
  • 同じ箇所を複数人が直した場合は投票でどちらを選ぶか決める
  • 最もマージされた数が多かった人は表彰

結果

  • 参加者 : 6人(因みに…4名は日本オフィス。2名はインドオフィス。リネームタイムはリモートで同時に。)
  • 30分間に出された総プルリクエスト数 : 32
    • ルール違反 : 2件(複数箇所修正、パッケージを1回層削除)
    • 重複したため投票で却下 : 2件
    • コードレビューで却下 : 8件(まだ一部レビュー中なので増えるかも)
    • ということで残ったプルリクエスト数 : 12 ~ 20
  • 修正の例
    • クラス
      • 不要(しかも意味不明)な接頭辞や接尾辞の削除
      • 省略されすぎていて分かりにくい名前を展開
    • メソッド
      • booleanを返すcheckXXXX メソッドを isXXXXX メソッドに
    • 変数
      • i などの無意味な名前を 意味のある名前に

所感

楽しかった

お祭りみたいで楽しかったです。ある程度ゲーム性のあるルール*2をしっかり作ったので、数を競うのも楽しんでくれた感がありました。

コードがきれいになった

これはもちろん。本来なら最初のコーディング時に埋め込まれるべきでない名前もちらほらありましたが、どうしてもできてしまうのは避けられないもの。
また、開発者のドメインナレッジが増すことによってより良い名前を後から思いつくこともあります。
そういった、普段のもやもやを片付けられた良い機会でした。

初級者の学びに

32件中8件はコードレビューで却下ということで、たかだかRenameなんですが、意外と他の人の了承を得られていないんですね。*3
「良い名付けとはなにか」の学びができた、良い機会になったと思います。

*1:プルリクエスト単位でマージの可否を判断するため

*2:本来の目的は安全にリファクタリングをするため

*3:レビューは参加者同士の相互レビューです

「解決策から考える病」に名前をつけたい

…って、タイトルで名前つけちゃってますが。

どうしても人間、「なんとなく思いついたソリューション」に飛びついちゃうんですよね。

寝坊しまくってるから目覚まし時計を買わなきゃ…! ってなりがち。
f:id:taichiw:20190206235438p:plain

その前に、そもそもなぜ寝坊しているか? について考えないといけない。*1
f:id:taichiw:20190206235455p:plain

※上の画像は以前に書いたこちらの資料より。もともとKPTのやり方を説明した資料ですが、根本的な考え方は共通だと思います。

自分がこの考え方ができるのは… 弊社の入社前課題でもらったこの本のおかげっぽいです。
他にも色んな経験が元になっているのだとは思いますが、「なんで自分、こういう考え方をするようになったんだっけ?」と思案したときに、思い出すのはこの本です。

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

世界一やさしい問題解決の授業 [ 渡辺健介 ]
価格:1296円(税込、送料無料) (2019/2/6時点)


適当にググってたらこんなブログも見つけました。(Baseball Studyで知ってる方だ…!) nihonbuson.hatenadiary.jp

*1:実際のチームだと…「そもそも寝坊は悪いことなのか? 悪いとしたらなぜ?」まで一旦掘り下げたいところです

JavaのHashMapは無限ループを引き起こす

知らんかった。理由がぼんやりはイメージ湧くけどしっくり来てないので後でちゃんと調べ…たい。(だいたいやらないパターン…)

d.hatena.ne.jp
www.atmarkit.co.jp
wadahiro.hatenablog.com


回避方法はいろいろあるみたいだけど、ConcurrentHashMapをつかってスレッドセーフにするのが楽なのかな?
web.plus-idea.net

価値観ババ抜き大体験会!に参加してきました #devlove

悩めるオジサンの私が、自分を見つけるためにぴったりそうな価値観ババ抜き体験会に参加してきました。
https://devlove.doorkeeper.jp/events/85438

価値観ババ抜きって何?? というのを書こうと思ったのですが 冗長になる&講師じゃない自分が変に説明して誤解が広がっても嫌だな
ということで、上記のイベント告知 及び、コチラの公式サイトのリンクをご参照ください。
http://myvaluecard.com/

いきなり考えさせられる名札書き

f:id:taichiw:20190121225328p:plain
何この「どこから来た」って!
ストレートに社名を書こうとも思ったんですが、あぁ、なんかあるんだろうなこれ と 邪推し、こんな感じに。
f:id:taichiw:20190121225438p:plain
「『シングルプレーヤーとマネジメントの間』から来ました」って… 今見返すとすごく恥ずかしい。
でもこの手の、自分と対話するゲームするときはこのくらい、のっけから自分を晒していかないとですよね!

ちなみに、この名札は、開始前の雑談以降は全く使いませんでした(笑)

ババ抜きの結果

さて、最終的に5枚のカードが手元に残るのですが… 私の結果はこちら!!
f:id:taichiw:20190121225716p:plain

いやいやいやいや 「美」ってなんだよ、「美」って。よりによって俺が「美」って。
って思うんですが。

このカード、偶然にも最初の5枚に入ってまして、

「なんだこれ? すぐ捨てよう」

と思ったものの、

「でも、バグのないシステム、きちんと『切られ』た、読みやすいコードって、まさに『美』だよなぁ」

と思ったら 

「これはこれで 大切かぁ…」

と思い

気づけば最後まで残ってしまいました。

また、ゲーム中は各カードはあまり繋がりがなく、バラバラに5枚残った… と感じていたのですが、
よくよく見ていると…

なんとなーく 隣のカード同士の関連のようなものが見えてきました。
f:id:taichiw:20190121230148p:plain

まさかの、最後の1枚、「メイン」になったのは…

更にここから追加のワーク。残った5枚の中から1枚のメインと2枚のサブを選ぶことに。

「そら、この中からメインを選ぶんだったら『楽しさ』よ」

と思ったんですが、どうしてもしっくりこない。

どうしてもどうしても気持ちがそっちに行かず…

最終的に私が選んだのは… これ。

f:id:taichiw:20190121230842p:plain

「『美』しいシステムを作るには『知識』が必要であり、それを学ぶモチベーションとして支えるのが『楽しさ』」

という、「美」を頂点にしたピラミッドができてしまいました。


えええ…

これだと俺の中で一番大事なことが「美しい」ことになってしまうんですけど……

とはいえ、恥ずかしさとか、どう思われるかとか、そういうのを捨てて、自分に問いかけたときに、

上の3枚の三角が最も最終結果として「美しい」 と思うくらい「美」を意識してしまったんだからしゃーない。

もう一回やったらどうなるんだろう

今回は偶然にもはじめの手札に「美」があったのでこのような結果になってしまったのですが、もし「美」がずっと他の人に持たれていたり、あるいはずっと場にあったとしたら*1、全く違う結果になったのでしょうか。
その場合自分は何をメインに置いたのか…

時期をおいてまたやってみたいものです。

実際、平日の夜にやるのか休日にやるのかでも結果が変わるとのこと。
脳内がお仕事モードなのかプライベートモードなのか、あるいは、仕事モードの中でも何に時間や脳みそを取られているのか といった状況によってかなり結果が変わりそうですね。

最後にお手紙タイム

最後は、一緒にババ抜きをした他の3名に向けて、お手紙を書いて渡す というちょっと恥ずかしいけど嬉しい時間でした。
f:id:taichiw:20190121231527p:plain

*1:カードを取られるたびに自分で場から一枚好きなものを選ぶことができるのですが… 自分から「美」は取らないような気がします。それとも今回みたいに、たまたま目があってしまった後に同じ思考になって結局取るんだろうか…

あらゆる「システム」の挙動は入力と出力だけで説明できる

どんな粒度、どんな用途の「システム」も、挙動は入力と出力だけで説明できます。
f:id:taichiw:20190118235359p:plain

プログラムのサブルーチン(function, method, ...)

「引数」と「返り値」で挙動が説明できます。

public String greet (String name){
    return "Hello " + name + "!";
}

Microserviceアーキテクチャの、各Service

APIのリクエストとレスポンスで挙動が説明できます。
f:id:taichiw:20190119000332p:plain

Microserviceアーキテクチャ全体

内部すべてをブラックボックスと見て、一番外側のAPIのリクエストとレスポンスで挙動が説明できます。
f:id:taichiw:20190119000823p:plain
f:id:taichiw:20190119000848p:plain

Webページ

リクエストであるURL(+POSTで送られるテキストや画像などのリクエストボディ)と、返却されるHTMLやその他コンテンツで挙動が説明できます。
f:id:taichiw:20190119001413p:plain

でも、データ更新したりするでしょ? 入力と出力だけじゃ説明できないじゃない!

このようにシステムを見ると、「状態が更新される」ことも挙動の説明としてする必要があります。
f:id:taichiw:20190119002052p:plain

ですが、ここだけをシステムとして見ると… DBへの更新は「出力」の一種と見ることができます。
f:id:taichiw:20190119002118p:plain
このシステムは、APIのクライアントと、DB という2種類の外部システムに、出力をするシステムです。

プログラムのサブルーチンの場合も同様です。

class Person {
  String name;
  public setName(String name) {
    this.name = name;
  }
}

このコードは、このようにも捉えられますが
f:id:taichiw:20190119002532p:plain

nameはPersonから依存があるだけの別インスタンスなので、このようにも捉えられます。
f:id:taichiw:20190119002715p:plain

逆にDBからデータを読むようなシステムは、「入力が2つある」と捉えることができます。
f:id:taichiw:20190119002824p:plain