nashcft's blog

時々何か書く。

『再演 ~ 組織にテストを書く文化を根付かせる戦略と戦術 +α』メモ #t_wada

また過去に参加した勉強会のメモ。今回のは3/11ともはや半年以上前...
まあもう諦めても良かったのだけど、9月頭 (これも1ヶ月近く前...) にあったprivate methodの件だったり職場で最近あったお話だったりがこの講演で扱われた話題に関連している気がするなあと感じたので、折角だしと思って掘り出してまとめることにした。

connpass.com

togetter.com

www.slideshare.net

転送サーバおけるテストの自動化 · GitHub

メモは最後に貼るとして、講演の内容の内2点について、この記事を書いている時点までにあったことも含めて思っていることをまとめる。これらを書いていて、半年以上前の講演で、それもスライドに残されていることではなくその場で話されていたようなことやそれについてその時感じたことなのに、今でもこうやって覚えていてそれなりな量の文章を書けるのは、自分にとってこの講演がそれほど感銘を受けるもので、その後の自分の考えや行動の指針となっているのだなあとしみじみ思ったのであった。

リファクタリングとビジネス価値、日々の開発の関係

スライドにおける「リファクタリングのジレンマ」の部分。スライドにはこれだけしか書かれていないがこの会ではここを掘り下げた話があった。

リファクタリングを独立タスクにすると、そのタスクは着手されない

どういうことかというと、リファクタリングそれ自体はプロダクトに新たにビジネス価値を付与するものではなく、振る舞いを変えることなく内部を改善する「だけ」なので、「〇〇のリファクタリングを行う」とタスクとして独立してしまうと、他の新機能開発や機能追加・改修といったタスクに対して短期的な視点からエンジニアの自己満足とみなされたり早く顧客に新たな価値を提供したいというビジネス判断をされたりして後回しにされ続けてしまい、ついぞ着手されることなくTODOリストの奥底に沈み込んでしまう、ということだそうだ。
ではリファクタリングのビジネス的価値とは何かというと、中長期的に見て同様のタスクをこなすのにかかる時間を一定以内に保ち続けられるようにすることであるとのことで、つまりビジネスの進行に対するブレーキをプロダクト内に作らないようにすることと言える。

リファクタリングされないまま開発を進めてしまった場合どういうところに行き着くのか、というのは、最近だと以下の記事の「コードの乱れが生むもの」がイメージしやすく書かれていると感じた。

d.hatena.ne.jp

この記事によれば、行き着く先とは最早リファクタリングどころではなくなってまた一から同じシステムを作り直すという、開発にもビジネスにも多大なコストを支払わざるを得ない状況となる。計画された再構築でなければ実施しようとする頃にはそれをするにも心許ない、最悪実施すらできないような状況に陥ってしまっているというのは恐ろしい話である。

短期的には後回しにされがちだが、かと言ってしないままだとその内まずいことになるのでやらねばならないリファクタリングを、じゃあどうやって実施したらいいのかと言うと、日々の開発タスクの中に小さなリファクタリングをまぶして、日々の業務に改善を組み込んでいきましょう、とのことだった。

これは言われてみればなるほどその通りで、規模の小さい内に問題を潰せることができればその後起こりうる大きな問題 (大規模なリファクタリング) を未然に防げるだろうし*1、結局開発もリファクタリングもソフトウェアエンジニアとしてはどちらもコーディングタスクと見做すことができるので、あえて別タスクに分離して実施しなくても一緒にやってしまえばいいよな、と思う。
とは言うものの中々実行はされないもので、今の職場では割とカジュアルにリファクタリングを独立タスクにしていく傾向があり、見かねて一度「そういうのは良くないよ、こういう風に日々の開発タスクの中で少しずつリファクタリングしていきましょう」ということをプルリクエストで実例を紹介しつつ訴えかけたことがあった。その時はあまり注目されてないなーと思ったのだけど、2, 3人ほどそれ以降実践してくれているのを観測したので、文化を作るきっかけにはなったのかなと考えている。

この講演でも話されていたことなのだけど、本当は上記のように大勢に呼びかけるような方法というのは戦術的には筋が良くなくて、一人ずつ伝授していくようにできる人を増やしていくという方法が紹介されていた。確かに、考え方や実践のためのスキルなど習得すべきことの性質や量を考えると個別にみっちり指導していく方がよく定着するだろうなと思う。とはいえ現実問題としてはそんな気長に構えていられないような状況もあるだろうし、まだ小さくリファクタリングする習慣を会得していない人のタスクに対してもどうにか改善を適用したいという思いがないこともない。
これに関してはプルリクエストによるコードレビューを行うとそれなりにカバーできるのかなあと考えている。講演でも戦術としてコードレビューが挙げられていて、人に見られるコードを書いているという意識を植え付けることで、コードの品質を向上させられるというような話があった。そのためには、単に実施するだけではダメで、レビュイーに自分の書いたコードやどのような変更を行ったかが見られ、その内容が評価*2されるということを認識させるように、レビュアーはきちんと見なければならないし、見ているという姿を表さないといけないが。

それで、どうやってコードレビューで日々の改善をカバーしていくかと言うと、レビューの際に改善ポイントをがあったら、普段からこういうのを見つけたらこういう風に直していきましょうねー、というのを指摘・解説しつつ、改善を適用して (してもらって) からmergeするようにする感じ。はじめの内はレビュアーのコストがかかるのは仕方ない。いわゆる投資というものである。人によってはこれでレビュアーが見つけてくれるからと完全に甘えてしまって改善を行わない人も出てくるかも知れないが、そうしたら素直に張っ倒して (比喩表現)、自らやるように導いていくなりする必要がある。
これも全体に〜という話だとレビューするプルリクエストの量が多くなって大変になってしまうが、改善活動を日々の開発に取り入れるならば、それの啓蒙や教育も日々の活動の中にまぶしていくのが良いのではなかろうか。

「設計の可動域を確保した」テストについて

前半の方で品質を上げるのはテストではなく設計とプログラミングという話があって、品質改善のために再設計を、それによって実装の変更を行うとして、その際にテストが実装に寄り添ったもの (= 実装をテストしているテスト) になっていると、これらの活動の足を引っ張るようになってしまう。なので、設計や実装の変更を後押しするような、実装と距離を持ったカバー範囲に遊びのあるようなものにして、カバー範囲内で変更を行う分には落ちないようなテストを作りましょう、という話だった。

それから時間が経って9月になって、private methodのテストと設計についての話が以下の記事を発端として話題になった。

su-kun1899.hatenablog.com

private methodにまつわる設計の是非は置いておくとして、この記事に対してt_wadaさんが紹介したQ&A記事において、質問者がprivate methodも実装した瞬間に実装のテストがしたいという発想のもとテストを書いてしまいたいな、と述べており、それに対してt_wadaさんは「プライベートなメソッドのテストを書く必要は 無い と考えています」と先に結論した上で、以下のように説明している。

繰り返すと、プライベートなメソッドや関数をテストする必要は無いと考えています。プライベートなメソッドは、実装の詳細であるからです。 (中略) プライベートメソッドに対するテストは内部の実装に対するテストになってしまうことが多く、そして内部の実装に対するテストはリファクタリングの妨げになりがちです。自動テストの助けを借りて積極的にリファクタリングを行いたいのに、その自動テストがリファクタリングの妨げになる。これはとても皮肉な状況です。避けられれば避けたいものです。
テスト「できる」ことと「するべきである」ことは異なります。リフレクションを使えばプライベートなメソッドのテストは「できる」のですが、そのテストはやがて実装改善の邪魔になりかねません。

Q&Aの記事がちょうどこの講演 (再演) の3年前くらいで、その頃からリファクタリングを後押しするための実装に対して距離を保ったテストという考えがあって今日まで一貫してきてるんだなあという感動があったのはさておき、実装のテストを書いてしまうと実装を変更するたびにテストも改修しなければならないためリファクタリングのコストを無闇に上げてしまうことや、リファクタリングが振る舞いを変えずに実装を改善することだというところから、リファクタリングによって継続的にコードを改善できるようにするために実装に対して遊びのあるテストを書きましょうということについては私も同意したのだが、「実装のテストを書くべきではない」というのはちょっと違うのでは、という考えを話を聞いた当時に持った。
それについて、講演後に呟いた以下の一連のtweetが上記の話を受けての実装のテストに対する考え方を表している。

まとめると、この講演では書くべきでないとされた「実装のテスト」について、私は実装をしているまさにその時はそれぞれの部品が正常に動くことを確認するための一時的な、目的を達成したら (部品が期待通りに動くことが確認できたら) 捨ててしまう「寿命の短いテスト」としてであれば別に書いても良いと思っていて、実装後も再設計やリファクタリングできるように残すテストは設計・実装に関してカバー範囲に遊びのある「寿命の長いテスト」にする、というように使い分ければ良いのではということである。

ここでいう「寿命の短いテスト」= 「実装のテスト」は役割的には補助輪のようなものだし、書くべきではないというより書かなくてよい、言い方としては「後に残すべきではない」の方が適切かな? というのが私の意見となる。

まあ時間軸の視点を持ったところで、結局「寿命の短いテスト」は書かずに済むに越したことはないし、「寿命の長いテスト」だけ書きながら書き上げられるようなプログラムがいわゆる良い設計のプログラムと言えるのかも知れない。


以下当日のメモ (当日若干遅刻したので途中から)

*1:根本的な設計そのものみたいな問題はこの方法では対処できなさそう...?

*2:コードの評価であって、人事評価ではない。念のため

Read more

第3回 マイクロサービス アーキテクチャ 読書会 メモ

書くだけ書いて投稿し忘れていた当日書いたメモ。もう第4回も終わってるんだけど...

architect-club.doorkeeper.jp

スライド: 前半

www.slideshare.net

後半

www.slideshare.net

統合

  • 数ある選択肢
  • 統合から何を得たいのか?
    • 破壊的変更を回避する
    • APIを技術日依存に
      • サービス間の通信に使うAPIを特定技術に依存させない
    • コンシューマにとって単純なサービスに
      • 利用コストが高いと価値がない
      • クライアントライブラリ: 結合度が高まる
    • 内部の実装詳細を隠蔽
      • 変更コスト、破壊リスク、技術的負債
      • ECサイトに対する会員登録API,メール送信APIを公開するべきではない -> ドメインモデル貧血症

顧客とのinterface

  • 単純なCRUD: 登録、削除とか

共有データベース

サービス間でDBを共有しても良いか否か

  • 大規模な共有APIの場合
    • スキーマ変更によりコンシューマを破壊してしまう
    • データモデル、プロダクトの変更がむずい (ドライバの問題とかあるね)
    • ロジックが分散する: 凝集性の喪失
  • いかなる代償を払ってもDB統合を避けるべき

共有しない場合の手段

  • 特に言及なし?

REST vs RPC (Remote Procedure Call): Trade-off?

RPC

  • finagle, gRPC
  • ひどくはない
  • プロダクトを選ぶ

REST

  • HATEOAS
    • リチャードソンの成熟モデル level 3
      • level 0: the swamp of POX - URI と HTTPが 1to1
      • level 1: Resources - リソースが紐付いている
      • level 2: HTTP Verbs - methodを活用している
      • level 3: Hypermedia controls - RESTful、状態と振る舞いを表している
        • 次の振る舞いを返却してくれたりする

JSON or XML?

  • JSONにはlink controlがない -> HAL e.g. Spring HATEOAS
  • SIREN
  • JSON API
  • JSON-LD (linked data)
  • de fact standard はなさそう

コレオグラフィ

Coreography

the art or job of deciding how dancers will move in a performance; also : the movements that are done by dancers in a performance

マイクロサービスアーキテクチャにおけるオーケストレーションとコレオグラフィ

  • クライアントが直接サービスを呼び出すのではなく、イベントを間に挟んで、イベントが必要な各サービスを呼び出す
    • pub/sub
  • Zabbix, Zipkin

orchestration vs coreography どちらを採用するべき?

  • coreographyはバッチみたいな大量の処理をするときに向いてそう
  • クライアントサービスはorchestrationでも良い?
  • 弊社サービスの構成はAPI gatewayというらしい
    • BFFかも?

req/res vs event-based

  • 同期モデルか非同期モデルか
  • SOAPとかRMIみたいな重いのよりJSONみたいな軽量のものを使いたいよね
  • level 3の採用は難しそうですねえ
  • まあ両方使うよね
    • ms間はevent-based
    • frontからはreq/res

色々なサービスを呼び出して結果的に1枚のHTMLに収めたい時

↓はアジェンダに当てはめるとここ?

UI

API合成

  • UIとサービス作成者の違い
    • 歩調を合わせるのは難しい
  • レスポンスの調整ができない

UI部品合成

  • SSRとか
  • wigetとか

API GatewayとかBFFとか

レガシーシステムやパッケージ製品ともやりとりする時の方法

↓はアジェンダに当てはめるとここ?

versioning

  • 最大限の見送り
    • フィールドの場所を曖昧にしておく (XPath)
      • 耐性のあるリーダー by Martin Fowler
    • ポステルの法則
  • 破壊的変更の早期把握
    • Consumer Driven Contract
      • 回避
      • 受け入れてコンシューマと相談
  • semverの利用
  • 異なるエンドポイントの共存
    • v1, v2がエンドポイントとして存在するけどロジックは1つ
    • テストの負担がやばいので3つ以上はやめとけ
  • 複数バージョンを同時に使う
    • 短時間ならまあいいかも
    • 「慎重に」

会の後

『自閉症の脳を読み解く: どのように考え、感じているのか』を読んだ

自閉症の脳を読み解く―どのように考え、感じているのか

自閉症の脳を読み解く―どのように考え、感じているのか

いつだったかTLでプログラマに向いてる人向いてない人の思考パターンについて会話しているのを見かけて、その中でこの本が紹介されていたので読んでみた。自閉症、というよりは人間の思考パターンに対する興味の方が強かった。
今月頭に購入してから時間を見つけてはちまちま読んでいて、今日の外出中に電車の中で読み終わった。

※ちょうどこの後目的地に着いて、昼だったので外食屋に入って以下をメモってた

思考パターンについて

本書によると人間の思考パターンには3つのタイプがあって「視覚的画像で考えるタイプ」「言語や事実で考えるタイプ」「パターンで考えるタイプ」があるのだそうだ。

8章「活躍の場を切り開く」の "対人関係のスキルを身につける" の項について

Gitを学ぶ

前の記事で勤め先のVCSをhgからgitに移行する機運があると書いたが、そちらの向きが強くなってきたのでgitについて少し真面目に勉強し始めていて、その中で良さげに感じた資料を一旦まとめることにする。

www.slideshare.net

このスライドはκeenさんのブログ記事で紹介されているのを見て読んだ。
gitで管理される情報の構造やユーザが操作するコマンドの裏側でどのようなことが行われているかをイメージしやすく、これを読んでおけば大体のことはこなせるようになるはず。
操作周りのスライドを読んでいて、hgを使っているのにrebaseやmqをガシガシ使っている人の多い弊社に本当に必要だったのはgitなのでは... と思った。

www.slideshare.net

最初のスライドに次に読むスライドとして勧められていた。所謂git-flow?
resetの部分だけ面倒だなと思ったので、これのresetしない版を普段やっているような気がする。
github-flow, gitlab-flowと他にもあるようだし、比較しながらブランチ戦略的なものを検討しよう。

Seven More Languages in Seven Weeks: Julia Day 1の演習問題をやった (ずいぶん前に)

間が空きすぎてしまった...
Day 1 の演習自体は前の記事を書いた後すぐにやってはいたのだけど
下書きを書いた後先に進めている間に詰まったか何かして公開しないまま投げてしまったようです (記憶が朧げ)
とりあえず書いた当時のまま晒すだけ晒しておこう...


github.com

設問の中に5x5x5の配列を作れとあって、3次元以上の配列の作り方を知ってから「じゃあ4次元配列を作ったらどうなるんだろう?」と思って試した結果がこちら:

julia> Array(Int64, 5,5,5,5)
5x5x5x5 Array{Int64,4}:
[:, :, 1, 1] =
 4469252560           0  4469252560           0  4469252560
          0  4469251016           0  4469252560           0
 4469255624           0  4469252560           0  4469252560
          0  4469252560           0  4469252560           0
 4469252560           0  4469252560           0  4469252560

[:, :, 2, 1] =
                   0  78152697455975698193556471915367379232  0
     140278440639040  7867888721079460201     3556471915367379232  0
 5417930524443302683  2319735731304618779     3539894578555400480  0
 5129700148190993179  2319735477633097777     3539883321446117664  0
 7868170196776672027  2319735477633097777     5132187517736263739  0

[:, :, 3, 1] =
 0  0                    0  7868170196776672027  2319735477633097777
 0  0                    0  7815269745597569819  2319735477633097777
 0  0      140278440639328  7867888721079460201  2319735477633097777
 0  0  5417930524443302683  2319735731304618779  3556471770077798449
 0  0  5129700148190993179  2319735477633097777  3556471915367379232

[:, :, 4, 1] =
 3556471915367379232  20046507979529531  0  0                    0
 3556471915367379232                  0  0  0                    0
 3556471915367379232                  0  0  0                    0
 3539894578555400480                  0  0  0      140278440639616
 3539883321446117664                  0  0  0  5417930524443302683

[:, :, 5, 1] =
 5129700148190993179  23197354776330977773539883321446117664  0
 7868170196776672027  2319735477633097777          78302376827741  0
 7815269745597569819  2319735477633097777                       0  0
 7867888721079460201  2319735477633097777                       0  0
 2319735731304618779  3556471770077798449                       0  0

[:, :, 1, 2] =
 0                0  54179305244433026833556471770077798449
 0                0  5129700148190993179     3556471915367379232
 0                0  7868170196776672027     3556471915367379232
 0                0  7815269745597569819     3556471915367379232
 0  140278440639904  7867888721079460201     3556471915367379232

[:, :, 2, 2] =
 3539894578555400480  0  0  0      140278440640192
 6710417459114946848  0  0  0  5417930524443302683
        305851882267  0  0  0  5129700148190993179
                   0  0  0  0  7868170196776672027
                   0  0  0  0  7815269745597569819

[:, :, 3, 2] =
 7867888721079460201  2319735477633097777  3556471915367379232  0  0
 2319735731304618779  3556471770077798449  3539894578555400480  0  0
 2319735477633097777  3556471915367379232  1971786226191053088  0  0
 2319735477633097777  3556471915367379232           1194668379  0  0
 2319735477633097777  3556471915367379232                    0  0  0

[:, :, 4, 2] =
 0                    0  78152697455975698193556471915367379232
 0      140278440640480  7867888721079460201     3556471915367379232
 0  5417930524443302683  2319735731304618779     3539894578555400480
 0  5129700148190993179  2319735477633097777     6564943274667159840
 0  7868170196776672027  2319735477633097777                 4666417

[:, :, 5, 2] =
 0  0  0                    0  7868170196776672027
 0  0  0                    0  7815269745597569819
 0  0  0      140278440640768  7867888721079460201
 0  0  0  5417930524443302683  2319735731304618779
 0  0  0  5129700148190993179  2319735477633097777

[:, :, 1, 3] =
 2319735477633097777  3556471915367379232  18227  0  0
 2319735477633097777  3556471915367379232      0  0  0
 2319735477633097777  3556471915367379232      0  0  0
 3556471770077798449  3539894578555400480      0  0  0
 3556471915367379232  3556466417809240352      0  0  0

[:, :, 2, 3] =
                   0  51297001481909931793616772148839854368
                   0  7868170196776672027                      71
                   0  7815269745597569819                       0
     140278440641056  7867888721079460201                       0
 5417930524443302683  2319735731304618779                       0

[:, :, 3, 3] =
 0  0                0  5417930524443302683  2319735731304618779
 0  0                0  5129700148190993179  2319735477633097777
 0  0                0  7868170196776672027  2319735477633097777
 0  0                0  7815269745597569819  2319735477633097777
 0  0  140278440641344  7867888721079460201  2319735477633097777

[:, :, 4, 3] =
 3556471770077798449  3539894578555400480  0  0  0
 3556471915367379232  5129935717922582816  0  0  0
 3556471915367379232                    0  0  0  0
 3556471915367379232                    0  0  0  0
 3556471915367379232                    0  0  0  0

[:, :, 5, 3] =
     140278440641632  78678887210794602013556471915367379232  0
 5417930524443302683  2319735731304618779     3539894578555400480  0
 5129700148190993179  2319735477633097777       20037711886507296  0
 7868170196776672027  2319735477633097777                       0  0
 7815269745597569819  2319735477633097777                       0  0

[:, :, 1, 4] =
 0  0                    0  7815269745597569819  2319735477633097777
 0  0      140278440641920  7867888721079460201  2319735477633097777
 0  0  5417930524443302683  2319735731304618779  3556471770077798449
 0  0  5129700148190993179  2319735477633097777  3556471915367379232
 0  0  7868170196776672027  2319735477633097777  3556471915367379232

[:, :, 2, 4] =
 3556471915367379232  0  0  0                    0
 3556471915367379232  0  0  0                    0
 3539894578555400480  0  0  0      140278440642208
        305904950109  0  0  0  5417930524443302683
                   0  0  0  0  5129700148190993179

[:, :, 3, 4] =
 7868170196776672027  2319735477633097777  3556471915367379232  0  0
 7815269745597569819  2319735477633097777  3556471915367379232  0  0
 7867888721079460201  2319735477633097777  3556471915367379232  0  0
 2319735731304618779  3556471770077798449  6710428716224229664  0  0
 2319735477633097777  3556471915367379232           1194875675  0  0

[:, :, 4, 4] =
 0                    0  51297001481909931793556471915367379232
 0                    0  7868170196776672027     3556471915367379232
 0                    0  7815269745597569819     3556471915367379232
 0      140278440642496  7867888721079460201     3556471915367379232
 0  5417930524443302683  2319735731304618779     1971797483300335904

[:, :, 5, 4] =
 4667227  0  0                0  5417930524443302683
       0  0  0                0  5129700148190993179
       0  0  0                0  7868170196776672027
       0  0  0                0  7815269745597569819
       0  0  0  140278440642784  7867888721079460201

[:, :, 1, 5] =
  2319735731304618779  0  0  0  0
  2319735477633097777  0  0  0  0
 -6917529027641081856  0  0  0  0
 -6917529027641081856  0  0  0  0
                    0  0  0  0  0

[:, :, 2, 5] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

[:, :, 3, 5] =
 140278443538256  0  0  0  0
 140278438868112  0  0  0  0
               0  0  0  0  0
               0  0  0  0  0
               0  0  0  0  0

[:, :, 4, 5] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

[:, :, 5, 5] =
 0                0  140278443539344                0  0
 0                0  140278438866256  140278438864720  0
 0                0  140278443538768  140278438868016  0
 0  140278443538896  140278443540944                0  0
 0  140278438867888                0                0  0

なんでや...
ちなみに5次元配列、6次元配列を作るとちゃんと0で初期化された配列が返ってくる(そこまでしか試していない)。

julia> Array(Int64, 5,5,5,5,5)
5x5x5x5x5 Array{Int64,5}:
[:, :, 1, 1, 1] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

[:, :, 2, 1, 1] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

[:, :, 3, 1, 1] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

[:, :, 4, 1, 1] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

[:, :, 5, 1, 1] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

[:, :, 1, 2, 1] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

...

[:, :, 3, 5, 5] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

[:, :, 4, 5, 5] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0

[:, :, 5, 5, 5] =
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0