テストコードを書くその前に
今日テストコードを書く前にどんな事を考えているかつぶやいたのでブログにも転記しておく。
テストで一番大事にしているのも(テストケースの)設計だったりする。
— uuuu.kt (@yushi_koga) April 8, 2020
闇雲に、探索的にテストするのではなく、テストケースの洗い出しやそれを実現しやすいテストコードの設計を最初にする。むしろテストの実施よりもそっちに時間かけてる。
— uuuu.kt (@yushi_koga) April 8, 2020
当たり前かな?と思ったけどできてる人以外と少ないと思ったので唐突につぶやいてみた。
— uuuu.kt (@yushi_koga) April 8, 2020
ふと思ったけど、割とみなさん自分が開発した内容把握してるからっていきなりテスト(コードを書き始めたり)はじめて無い?
— uuuu.kt (@yushi_koga) April 8, 2020
経験則的に、段取り八分はテストでも同じ気がする
— uuuu.kt (@yushi_koga) April 8, 2020
これは、単体テストに限った話じゃなくて、結合テストでも同様です。
— uuuu.kt (@yushi_koga) April 8, 2020
あ、ただTDDでコードとモデルを同時に育てるアプローチのときは違うかも。
— uuuu.kt (@yushi_koga) April 8, 2020
ただTDDでモデルに付いて学んだあとそのコードを捨てて、最初から清書する場合はやっぱりテストケースから考えるかな
— uuuu.kt (@yushi_koga) April 8, 2020
要約するとテストも戦略を考えて設計してから書こうね、というお話でした。
私が考えるファーストクラスコレクションを定義しておく意味や意義みたいなもの
私の考え
私は、ドメインオブジェクトのコレクションには常にファーストクラスコレクションを定義しておくと良いと考えています。
例えば、List<DomainObject>
という生の配列をクライアントコードに操作させる場合、あちこちのクライアントコードに同じ操作が(しかもちょっとずつ違うやり方で)書かれているという状況を招きかねないためです。
特に別の技術者が参画してきたときはまだコードの書き方を真似しきれないはずですから、その状況を引き起こしやすいでしょう。
例えば、List<DomainObject>
を許した場合
どういうことがおきるでしょうか。
同じ操作なのに書き方が違うものがあちこちのクライアントコードに散在しているとします。 影響をうけうるクライアントコードをすべて特定するのが手間です。 また、特定できたとして書き方が違うと本当にそこにも変更を反映すべきなのかどうか迷ってしまうかもしれません、それを調べるコストがもったいないと感じてしまいます。
また、別のケースとしては、属性に変更があったから操作に影響が出たのでとある処理の修正が必要といったときに、その変更が発散していろんなクライアントコードに波及してしまいます。
一方で、ファーストクラスコレクションを定義してそこに操作を集約しておくとどうなるか
操作を実装されるコード間の距離が物理的に近いのでそのコレクションを操作する時の書き方の統一感を期待できますし、何かを操作するコードが1箇所であることを期待できるので、変更の発散がかなり抑制できるはず1です。
開発に関わるエンジニアが少数しかおらず、その入れ替わりがないのであればまだ操作が存在しないコレクションまでファーストクラスコレクションとするのは短期的にはコスパは悪いかもしれません。
しかし、先述の理由から中長期的にはコスパ良いと私は信じています。
なお、ファーストクラスコレクションに限りませんが、私の経験則では、後からリファクタリングして対応するという方針にする場合、似たような処理(分岐)が2箇所で出てきたら黄信号(リファクタリングの必要が高い)、3箇所になったら赤信号(このタイミングでリファクタリングしておかないと高い確率でまずい状況を引き起こす)って感じています。
-
運が良ければ完全になくせるかもしれませんが、変更はどうしても波及するものです。しかし、その影響度合いを小さくすることは可能だと考えています。↩
クラスなどの名付けで抽象的にするか具体的にするかで悩んだら
OperationService
とするかSomeSpecificOperationService
で悩んだらSomeSpecificOperationService
を選びます。
ここって、確かに迷いがちなところですが、抽象度が高い名前をつけると責務が大きくなりすぎる傾向が強いので、具体的な名前をつけるほうが私は好みです。
- 具体的な名前をつける
- いろんな実装を進める上で他にもやらせたいことが増えてきて名前が不適切になる
- (1)で作ったクラス名を適切なものに改名するか、(それが思いつかなければ?)具体的な別の名前のサービスを定義するか考える
みたいな順番で考えることが多いです。
これも制約を強めるか弱めるかの選択肢の話に通じるものがあるのですが、
より具体的な名前 ⇒(用途の)制約が強い より抽象的な名前 ⇒(用途の)制約が弱い
と、私は考えています。
制約が弱いものを強めるのは後からやり辛いので、その2択で結果に大差がなさそうなら制約が強い方を選択するという原則
(自分でかってに言ってるだけのやつ)に従って、より具体的な名前をつけておく、という選択になる感じです。
私のコードレビュー観点について
仕事でまとめる機会があったので、ブログに残しておきます。
私が考えるレビュー観点は以下の4点に分類できます。
- 仕様
- 内部品質
- セキュリティ
- パフォーマンス
1. 仕様を満たしているか
- 仕様の漏れがないか
- 仕様の誤りがないか
2. 内部品質
- 変数名・関数(メソッド)名・クラス名に関してニュアンスを捉えた英単語を選択しているか
- コメントに嘘がないか
- 可読性を考慮できているか
- 込み入った処理の場合は、説明的変数や説明的メソッドを用いるなど
- ifやswitchを使わない、使っても深くネストしない(せいぜい2つまで)
- 使用している言語のイディオムや慣習に従った書き方ができているか
- パフォーマンスよりも可読性を優先してかけているか
- 可読性が高いコードであればボトルネックを突き止めるのは簡単なため、パフォーマンスの問題が起きてから最適化すれば良い、という考え方。
- 適切なデザインパターンを適用できているか
- typoがないか
3. セキュリティ
- パスワードの類の情報をハードコードしてたり設定ファイルに書き込んでいたりしないか
- 脆弱性のあるライブラリやフレームワークのバージョンを指定していないか
- メンテナンスが止まってしまっているライブラリやフレームワークを採用していないか
4. パフォーマンス
- 無意味に処理が遅くなる書き方をしていないか(Javaのfor文の中で+を使って文字列を結合する、とか)
- thread safeにも関わらず毎回インスタンスを生成していないか
- full scanになってしまうSQLになってないか
- 必要に応じてテーブルのINDEXを定義できているか
超簡単な内容ですが、簡潔にまとめるとこんな感じです。
Spring BootでMVCを自己署名証明書(オレオレ証明書)を使ってHTTPSでListenさせる方法
オレオレ証明書を使ってSpring Bootをhttpsで起動させてると、macがcatalinaでブラウザがchromeの場合にアクセスしてもERR_CERT_REVOKED
と表示されて先に進めない(警告を無視するオプションが提示されない)事象に出くわして4時間溶けました。
めちゃくちゃハマって辛かったので未来の自分と同じことにハマってる誰かのために記事を残しておきます。
何が起きていたか
macOSがCatalinaでブラウザがChromeだと動かないという事象が起きていました。
Spring BootでMVCをhttpsをlistenさせる方法はググるとたくさん出てくるので簡単だと思っていたのですが全然そんなことありませんでした…。
どうも、Catalinaになったタイミングでセキュリティが厳しくなった?っぽいです。
keytoolのオプションの組み合わせや設定値をいろいろ工夫してもうまく動かず途方にくれていました。
ググるとこんなのが出てきたりします。mojaveじゃ起きないらしいですが、私は試していません。
回避方法がないかkeytool localhost certificate chrome ”ERR_CERT_REVOKED"
あたりのキーワードでいろいろググってると、海外のどっかの会社のなにかのプロダクトで同じ問題がおきていたらしく、そのコメントのやり取りの中にこのコマンドで再作成すれば動くようになったぜ(超意訳)!ってのを見つけて、動かすために必要なkeytoolのオプションを特定することができました。
Spring Bootで自己署名証明書(オレオレ証明書)でHTTPS通信を受け付けるための設定
というわけで、それを踏まえた上で設定手順を書き出していきます。
動作確認環境
- Spring Boot 2.1.2
- 2.x系なら同じ設定で動くと思います。
- macOS Catalina 10.15.2
- mojaveなら再現しないっぽい…
自己署名証明書作成手順
以下のコマンドで作成できます。
$ keytool -genkey -alias localhost -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore /pass/to/your/keystore.p12 -dname "CN=localhost" -storepass yourpassword -ext ExtendedKeyUsage=serverAuth -validity 825
ポイントは次の2点です。
- 有効期限を825日以下にする
-ext ExtendedKeyUsage=serverAuth
のオプションを追加する ※X.509 証明書の拡張機能
この2点を突き止めるのにすごく時間がかかりました。1
なお、対話モードをスキップするために-dname
とstorepass
も入ってますがここはお好みです。
また、storetypeはPKCS12を指定していますが別にJWSでも問題なく動きました。
Spring Bootの設定内容
server: port: 8443 #任意のポート番号で良い ssl: enabled: true # sslのセクションがある時点でtrueがデフォルトなので省略可 key-store: /path/to/your/keystore.p12 key-store-password: yourpassword key-store-type: PKCS12 key-alias: localhost
たったこれだけでhttpsでlistenするように変わっているはずです。
おまけ
httpsでlistenする場合、加えてhttpsのみ通信を許可する設定をいれたくなるかもしれませんが、1.x系で存在していたsecurity.require-ssl=true
が2.x系では削除されているのでJavaで書く必要があります。
-
ググっては試すの繰り返し。証明書の仕様に詳しくないのでtry & errorが早いと判断した↩
ReactとVueのどちらを採用するか
掲題について、とりとめもなくメリット・デメリットなどを考えているので、メモを残しておきます。
執筆時点ではReactは経験が非常に浅く、事実や経験というよりはほとんどが思い込みや予想です。
React
メリット
- フロントエンドをうまく作るために頭が良い人たちが考えた設計や技法を効率良く学べそう
- シェア一位のため事例が多く、かつFacebookやTwitterといった大企業も採用しているため、大抵のことは解決方法が存在していそう
- React NativeがあるのでNativeアプリ作りたくなったときにReactのノウハウを流用できるはず
デメリット
- 位置づけがライブラリということもあり、react単体で実現できないことは別ライブラリと組み合わせて実現する必要があり、技術選定のための審美眼とどう組み合わせるかの設計スキルが必要(になりそう)
- 正しい設計や技法を知らないうちはそれを学ぶまでの学習コストを払う必要があるため、とりあえず動かすまででも相当な時間がかかりそう
イメージ
- フロントエンド専門あるいはフロントエンドからキャリアをスタートしているツヨツヨな人たちが好んで使ってる
- 同じデザインかつコンポーネントの分割方針も同じだったらコードも似てくる気がしている
- React書ける人ならVueとかAngularは楽にキャッチアップできそう
- Reactを使いこなせる人はつよつよなフロントエンジニア
Vue
メリット
- とりあえず一通り動かせるようになるまでの学習コストが低い
- htmlベースベースでSFCを書けるため、HTML/CSS/JSの基本的な知識がある人には書きやすい
- Vuex,Vue Routerといったコアライブラリを公式サポートしているので技術選定で迷いにくい
- Nuxt.jsというたいていのアプリでは使われる(コア)ライブラリが全部入りになってる便利なフレームワークがあるので技術選定や設定に割く時間を節約できる
デメリット
- とりあえず動かせちゃう方法がたくさんあるせいで、正しい設計にたどり着くまでのコストが高い。またそれが故に設計スキルが身につくまでに時間がかかりそう。
- TypeScriptとVuexの相性がイマイチ(らしい)
- 事例は増えてきているが、まだシェアが低い
- 個人(Evan You氏)がメインで進めているため、何らかの理由で彼が突然リタイアした場合に、その後のメンテが止まる可能性がある
イメージ
- デザイナあるいはバックエンドといったような隣の領域からフロントエンドに手を伸ばしてきた人たちに好まれる
- 同じ画面デザインかつコンポーネントの分割方針も同じでも、書く人によって全然違うコードになりそう
- Vueの経験があるだけでは、ReactもAngularをすぐキャッチアップ出来るとは限らない
- 意識的に設計スキルを磨いている人は別
どちらを採用するか
まず、大前提としてフロント開発自体の経験がない場合は、何を選んでも時間の試練に耐えうるものは作れないと思っています。
そのため、その状況だと何を選んだとしても割とすぐに捨てることになると思っています。
なので、敷居の低さを理由にまずはVueを採用するのがいいんじゃないかと考えています。
まずはVueで手早く価値を生み出せるものを作りつつ経験を積むべきな気がしています。
一通り作り終わればフロント開発の経験値がそれなりになっているはずなので、もう一度Vueで書き直した場合に今の課題を解決できるなら作り直すかどうかはおいといてVueを継続すればよいでしょう。
Reactでしか解決できないのであればReactで作り直すのが良いかと思います。
逆にフロント開発に強い人がチームにいるなら、好みで選んでしまって良いような気はしています。
価値を生み出すようになったアプリを、その後にフルスクラッチで書き直せる勇気や資金や時間が湧くかどうかはまた別のお話だと思ってます…
以上、私的メモでした。
レガシーをぶっつぶせ。現場でDDD!2nd 「インプット<アウトプット!」の参加レポート
レガシーをぶっつぶせ。現場でDDD!2nd 「インプット<アウトプット!」の一部と二部に参加してきました。
過去に多くの勉強会に参加してきましたが、その中でも特に学びを得られた強い実感がありました。間違いなく上位に入る満足度です。
学んだことを反芻して、記憶やスキルとして定着化を図らねばもったいないと感じたのでまとめたものを記事に残しておきます。
全体的に期待していたこと
DDDは(軽量かもしれませんが)自分なりにいろいろと実践してきており、実践と内省のループから考え方がなんとなく固まってきたので新しいブレイクスルーを得るために他の人の考え方を取り入れたいと思って参加しました。
あるいは自分の考え方があっているかの答え合わせができればそれはそれで自信が持てるといいなとも期待して参加しました。1
システム設計の中でのドメインモデルの役割を体感する
このセッションの内容とされていたこと
公式のタイムテーブルより引用します。
このワークショップでは、ドメインモデルをどのように実装に落とすかではなく、 業務フローやユースケースとドメインモデルがどのように関わり、 それらの変更がどのように伝搬するか、という全体の繋がりに焦点をあてます。
当日は、仮想的なシステム(の一部分)を題材として、 業務に変更が生じた際に何が変わるのかを、 モデルに対して付箋で変更を加えながら体験していただく予定です。
その場でワークショップを体験していただくだけでなく、持ち帰って、 自分たちのチームで実際の業務を対象にして試していただけるようにしたいと考えています。
この説明文を読んで私は『業務分析からそのモデリングまでの一連の流れや手法が知れること』と『それを現実に近い例で訓練できること』を期待して参加しました。
そして、その期待はドンピシャでした。
実際にやったこと
以下のようなことでした。
- カスタマージャーニーに似ているらしい手法(一般に通じる名前はつけられてなさそう)を用いながら、現状の業務をヒアリングしフロー図に落とし込む。
- そのフローで出てきた単語(概念)や操作(振る舞い)を概念モデルとして図に表していく。
これだけ書き出してみると単純かつ簡単な工程で大した意義がないように思えますが、フロー図作成で得られた気づき・見落としをモデルに反映する、あるいはその逆というフィードバックループが存在しており、概念モデルに現実の例を当てはめても通用するような表現力が育っていくような印象を持ちました。2
また、そんな印象を持てるほどの表現力を持ったモデルであれば、その後の実装はだいぶ楽になるだろうとも思いました。
感覚的な話ですが、『気づいていなかったことに気づく、それが可視化される。それが単純に楽しい/嬉しい』という成功体験が『他にも同じようなものを見つけてアハ体験したい』というモチベーションに繋がり、正のループが回っていたように思えます。
今まで業務でやってきたことを振り返る
私は今まで業務分析では開発チームでドメインエキスパートにヒアリングし、その中でそれぞれが構築したメンタルモデルを(何らかの方法論を用いて共有しあう事をすることなく)それぞれがすぐにコードに落とし込む、というようなことを繰り返してきました。
しかし、フロー図とモデル図に表すことでタンジブルにし、他の開発メンバーの意見を聞きながら足りていなかった「何か」を相互に教えあい、それぞれの図に反映していくことでコードを書くよりも低いコストで先に合意に達しておく手段があるとしることができました。しかもそれが現実的にも可能そうな感触を得られることができました。
これに気づけたのはとても有意義なことでした。
今までは、フロー図やモデル図を書けるほど具体的なイメージが持てているのであれば、動かせるコードを書いてそこからフィードバックをもらったほうが早いと言う考えでしたが、図を用いてチーム内で合意を形成していたほうが認識のすり合わせコストが減り時間的トータルコストとしては下がるのかもしれない、また他人の力を借りてより高速に正解に近いメンタルモデルを構築できるのではないかという考え方に変わりつつあります。3
概念モデル/ドメインモデルは実装に素直に落ちるらしい
ところで、『概念モデルとドメインモデルは実装に素直に落ちる』4という意味にとれる説明がセッションの冒頭にありました。
これは私が仮説として持っている考え方だったので聞いたときは『お、おおぉっ?おまおれ?やっぱそうなんや!』と嬉しくなった瞬間でした。
概念モデルをコードに写し取る例考えて、登壇者に質問しに行きましたが、『ありえる実装だと思いますよ!』と言って頂けて少し自信が持てました。
成果物
得られたこと
- 図の作成のための議論の中で自分が持つメンタルモデルと他の人が持つメンタルモデルは当然ながら違うという再認識ができた
- そして、概念モデルの時点でもなかなか合意に至らないのに、それをやらずにコードを書いてしまえばそりゃみんなバラバラな書き方するよなと納得した
- 概念モデルなどの図を作るコストを払ったほうがいきなりコードを書くコストを払うより低く済む可能性を感じられた
- 概念モデルがうまく構築できていれば実装にも素直に落とし込めるという考え方を持っている人が他にもいることが知れた
その他の振り返り的なこと
あとから考えて面白かったのはかなりクラス図に近いものをドメインエキスパートに見せながら、多重度の話をしたり、関連の線をつなげたりして認識合わせをしていたことです。
今回私のチームの担当をして頂いた方はデザイナさんでしたが、デザイナである彼にこういう話し方が通じるのであれば完全なビジネスサイドの人にも教育すれば同じやり方が出来るのではないかという可能性を感じました。
一緒に仕事しているビジネス側担当者はPlantUMLを書いてみちゃうくらいこっちよりの感覚がある方なので、ぜひこれも試してみたいと思いました。
アクションプラン
フロー図とモデル図を試す
- (元になっているかもしれない)カスタマージャーニーを研究する
- このワークショップでやったことも踏まえて(1)を実践する(必要性を感じたら先にカスタマイズもする)。
- 現場で試す
- (3)で得られたフィードバックを元に、辞めるかカスタマイズ(を繰り返しながら)して運用を継続する。
- 繰り返し
概念モデルを使ってビジネスサイドと会話してみる
- ビジネスとの認識合わせにモデル図っぽいものを使ってみる
- そのために教育もちょっとだけ頑張ってみる
モデリングワークショップ 〜割り勘ドメイン編〜
このセッションの内容とされていたこと
これも公式のタイムテーブルより引用します。
"飲み会 会計時の割り勘をテーマにモデリングワークショップを行います。支払合計金額を人数で割る「割勘」は単純です。しかし上司なら比率多め、後から来た人や学生なら少額といった多少面倒な計算が伴います。また、幹事は負担ゼロか、同様に支払うのかのオプションもあります。
今回はこういった具体例でモデリング力を鍛えることが目的です。実際には、想定しているユースケースに対してドメインモデルを考え、すぐにコード上の型を作り(処理は後回し)チームメンバーと議論し、ワークショップを体験していただきます。
追記(11/21): 対象言語は時間的な都合で効率的にサポートできるJavaに限定させてください。Javaの言語的なサポートはスタッフが行います。不明な点はスタッフに確認してください。 また、当日はお題の公表と共に、モブプログラミング形式で5チームに分かれていただきます。その際にJavaが分かる方がドライバーとして必ずチームに含まれるように調整しますので安心してください。当日利用するテンプレートプロジェクトは運営側で用意したものを配布させていただきます。
追記(12/10): お題とひな型となるソースコードはこちらです。詳しくはREADME.mdをご覧ください。当日は2時間しか時間ないのでできれば事前にドメインについて考えてきてください。 ※スタッフのほうでもPCは持ち込みますが、ワークショップ参加予定の方々はPCはご持参ください。"
この説明文を読んで私は、書かれていること通りに『モデリング力を鍛える』体験が得られることを期待して参加しました。
期待したそのものではありませんでしたが、『モデルと実装を行き来することでモデルもコードも育つ』ことが現実にあることだという実感を得ることができました。
この強烈な体験が余韻として残っています。
実際にやったこと
以下のようなことでした。
- 想定している割り勘の文脈を登壇者よりざっくり教えてもらう
- (1)をもとにチームで用語(概念)の洗い出しと相互の関連やそれぞれの属性と振る舞いを洗い出す
- (2)を見ながら実装する
ちなみに、(3)のフェーズではドライバーを務めました。
が、『お前ファシリテータか?』と思われるくらいコードを書きながらペラペラ喋ってしまいました。
役割に徹することができず、ちょっとでしゃばっちゃったなと少し反省しています…けど楽しかったですw
今まで業務でやってきたことを振り返る
一部で書いたことと重複しますが、図で示す前にコードを書いていました。
コードを書いている途中で当然新しい気付きはありましたが、それを図にフィードバックしていなかったため、新しい概念を獲得したことを強く実感することなく、それにより別の箇所を改善できる可能性にきづけることもなく5コードをひたすら書いていた気がします。
しかし、モデルで可視化&共有し合うことを知ったため、コードで得られた気づきをモデルに反映する、あるいはモデルで得られた気づきをコードに反映する、そういったそれぞれで得た気づきを反映し合うループを試しつつチームで共有する有意義さを体験できました。
成果物
ドメインモデル
ソースコード
得られたこと
- モデル図とコードを行き来することでそれぞれの足りないところを補完できる、それを複数人で実施することで足りない観点を補え合える、という体験ができた
- モデリング時には見落としていた概念が、実装の中で浮かび上がってくるという体験ができた
- モデルとコードを行き来する価値や醍醐味を知ることができた
アクションプラン
- 開発チームでモデル図を作成する
- コードを書いてフィードバックが得られればそれをもう一度モデル図に反映する
まとめ
- 今回の開催意図である『アウトプットをするほうが学びになる』の通り、ワークショップでアウトプットすることで学びとその強烈な体験を得られました。
- 個人的には、他の人の考え方を取り入れたい、あるいは答え合わせをしたいという期待で参加しましたが、そのどちらも満たせました。
- 学んだことを研究しつつもなるべく素直に現場で実践して行きます。