罹患しました
7/3に発症して、2日様子を見ても熱が下がらないどころか手足が痺れて動かなくなってきたのでこりゃまずいと病院へ行ったところ、陽性と診断された。
そこから1日の待機期間を経て、今は都の用意した療養施設に移った(西新宿にあるアパホテルが1棟丸々療養施設になってる)。
施設までの送りは、これも都の用意した専用タクシーに乗って行けるようになっている。
療養施設に滞在中の宿泊費、食費、タクシー代などは都が負担するらしい。納税していてよかった。
発症から5日経って、熱はほぼ平熱、手足の痺れや吐き気もなくなり、おおむね快方に向かっている。よかった。
また、昨日まで喉が痛すぎて固形物が喉を通らなかったけど、今朝ひさびさに固形物を食べることにも成功した。
ただ、味覚障害が起きてるみたいで、味とにおいがしなかった。いままではウィダー飲んでるだけだったから気づかなかったんだよな。
なぜか水は甘い。普通のいろはす飲んでるのにフレーバーが付いてるタイプのやつに感じる。
早く味と匂い戻って欲しいな〜、今はとにかく塩味のあるものを食べたい。
QA経験の少ないチームが良い探索的テストを行えるようになるための障害と、ナイトメアヘッドラインゲームの紹介
私が現在所属しているスクラムチームでは、チームとしてQAを行えるようになるべく、定期的に勉強会を行っています。 QAチームや、スクラムのQA担当者(のようなロールを担ってしまっている開発者)に品質保証を丸投げするのではなく、全員が品質保証に取り組み、自信を持ってリリースできるようになることを目標にしています。
今回は、QA経験の少ないチームで探索的テストを行う場合の障害と、それらを改善するためのナイトメアヘッドラインゲームというレクリエーションについて紹介したいと思います。
開発者が探索的テストを行うために障壁となること
探索的テストでは、探索したい項目をチャーターに書き出して、そのチャーターを指針としてテストを行なっていきます。
良いチャーターを作ることが、探索的テストを成功させるための要点だと考えており、これを専門のQAチームではないチームが行う上で、障壁となるポイントが2点あると考えています。
- チャーターの抽象度が揃わない
- 「作る」発想から抜け出すことができない
順番に説明します。
1. チャーターの抽象度が揃わない
開発チームに限った話ではありませんが、主に複数人でチャーターを作るときに起こりがちだと感じています。
そもそも、良い抽象度のチャーターを作ることは難しいです。 具体的すぎるチャーターを作ってしまうと、テストできる範囲を狭めてしまうことになり、自由に探索できる探索的テストの利点を損ねてしまいます。 逆に、抽象的すぎると、今度はテストの焦点が定まらず、探索が終わった時に、結局何がしたかったのか分からなくなってしまうこともしばしばあります。
抽象的すぎることや具体的すぎることの問題もさることながら、それ以前に、QAに慣れていない開発者が探索的テストを行う場合、開発者間でチャーターの抽象度が揃わないことがより大きな問題なのではないかと思っています。 少なくとも抽象度が揃っているならば、テストをたくさんすることで具体的すぎるチャーターの問題をカバーしやすかったり、あるいはテストの途中でチャーターが良い指針になっていないことに気づきやすいと思います。
しかし、抽象度が揃っていないチャーターだと、チャーターのダブりや指針としての良し悪しに気づきにくく、探索は終わったけど結局何をテストしたかったのかわからない、探索したかったものを全て探索できたかもわからない、意味のないテストになってしまいがちです。
2. 「作る」発想から抜け出すことができない
メインで開発を行っているチームが探索的テストを行うとき特有の問題だと考えています。 開発者やPOなど、普段「作る」ことを中心に考えている人たちは、なかなか「脆弱性を発見する」や「何がいけないのか」といった考え方に切り替えるのが難しいです。 また、特に開発者は、テスト対象となる機能を自身が設計、実装していることも多いため、コードベース中心の発想から抜けきれず、結果として他の機能と組み合わせた時に発生するバグや、非機能要件を見逃しがちだと思っています(スプリントレビューで、何故こんなことに気づかなかったのか、という初歩的な誤りを指摘されるのはこのケースの見逃しが結構多いと感じます)
ナイトメアヘッドラインゲーム
上記2点を改善するための取り組みとして、ナイトメアヘッドラインゲームがかなり良かったので紹介します。
ナイトメアヘッドラインゲームは、Explore it!という探索的テストの書籍で紹介されているレクリエーションで、チームで行います。(私が知らないだけで、ひょっとしたら初出は別の文献かもしれません)
自分の関わっているソフトウェアが起こしてしまった最悪のニュースの見出しを考え、そこから探索的テストのチャーターを作っていくというゲームです。 ゲームをしていく過程で、チャーターの抽象度に関する認識を揃え、「作る」発想から「脆弱性を発見する」という発想に切り替える練習ができるようになっています。
手順を説明します。
1. メンバー全員で最悪のニュース見出しを考える
例えばこんな感じです。 - 機密情報を含むA社宛のメールがB社に届いてしまった - 顧客管理画面から出力した社員情報が実は全て間違っていた
Explore it!ではニュースのヘッドラインを考えることになっていますが、toB向けサービスだと考えづらかったりもするので、必ずしもニュースぽく書く必要はないと思います。
2. 出揃ったニュース見出しの中から投票で、最も深堀りたい見出しを選ぶ
過去実際に起きてしまったことがある障害や、発生した場合影響の大きいものを選ぶと、その後のブレストが活性化しやすいと感じました。
また、私のチームは新しく加入したメンバーが多かったので、新メンバーが以前の案件で経験した障害なども取り入れながら進めました。
3. 2で選ばれた見出しが、現実になってしまった場合の原因を考える
機密情報を含むA社宛のメールがB社に届いてしまった -> 複数タブを操作していて、誤操作をしてしまった
顧客管理画面から出力した社員情報が実は全て間違っていた -> 出力したCSVのヘッダが消えており、内容が1行ずつずれていた
4. 3で出された原因を探索できるチャーターを考える
- 複数タブで操作し、メールを誤送信してしまう方法を探る
- CSVをダウンロードして、行をずらしてしまう方法を探る
まとめ
実際にやってみた感想として、初めから良い抽象度のチャーターを作ることはかなり難しいと感じました。 ですが、少なくともチャーターの抽象度を揃えことと「作る」思考からの切り替えにはすぐに役立つものだったと思います。
さらに、実際にチャーターを作るところまでやり切ることができると、次に探索的テストを行うときの心理的ハードルがだいぶ軽くなるようにも思います。
すでに探索的テストを行なっているチームや、これから品質保証活動をやっていこうというチームは、レクリエーションの一環としてやってみると面白いかもしれません。
また、余談ですが、経験豊富なメンバーがいるほどナイトメアヘッドラインが面白くなるので、そのようなチームにはぜひお勧めしたいです。
参考
オブジェクト指向設計実践ガイドを読みました
Rubyで書かれているオブジェクト指向入門書籍として有名な、オブジェクト指向設計実践ガイドを読みました。
学習のモチベーション
最近の仕事で、そこそこ大きめの機能(申込機能のようなもの)のリプレースを設計したのですが、自身のクラス設計能力不足を痛感したことがモチベーションとなり、一度腰を据えて入門書から勉強してみよう、と思ったことがきっかけです。
目的として
- 要件変更、機能追加に柔軟に対応できるクラス設計を行えるようになること
- 何らかの障害が発生した際に、MTTRが短く、保守性の高い設計を行えるようになること
の2つを意識していたのですが、結果としては、当初の目的に非常に合致した、良い学習になったように思います。
本書の概要
タイトルにもある通り、本書はオブジェクト指向の入門書です。
オブジェクト指向の入門書は巷にあふれているように思いますが、中でも本書を特徴づけている点は、自分が思うに次の3点かと思います。
- 単に設計原則の解説だけでなく、その設計原則はどこから来たのか、なぜ原則を守るべきなのかについて、具体的な文献を示して踏み込んだ解説がなされていること
- クラスよりもメッセージに基づく視点を持つことの推奨
- コードサンプルがRubyであること
中でも、特に感銘を受けたのは、本書の中で、適切なオブジェクト指向設計を実現するために、メッセージに基づく視点を持つことが推奨されていた点です。
大まかにまとめると、設計の重点を、「クラスと、クラスが誰と何を知るか」から、「まず必要なメッセージを決め、それをどこに送るか」に移すこと。それによって、「まずクラスを決め、その責任を見つけ出す」アプローチから、「メッセージを送るべき相手は誰かを考えることから、隠されたドメインオブジェクトに気づく」アプローチをとることが推奨されているといったところです。
私自身、責任外のクラスにメソッドを定義してしまったり、必要なドメインオブジェクトに気づけなかった時は、大抵既存のクラスに意識を置きすぎていたことが原因のように感じていました。
本書で推奨されている、「メッセージに基づく視点を持つこと」は、それに対する1つの解決法だと思うので、今後積極的に活用していきたいと思っています。
また、Rubyのコードサンプルは一部古い箇所もありますが(デフォルト引数、キーワード引数を使わない冗長な方法が解決手段として解説される点など)、本質を損ねるほどのものではなく、自分で読み替えたので特に気になりませんでした。
まとめ
今後、コードレビューや設計を行う際に、何度も読み返すことになるだろうと感じています。
本書で説明された方法論を参考にしつつ、継続的にリファクタリング、リアーキテクチャを行って、設計能力を向上させていきたいと思います。
住んでいる街のボランティアスタッフになりました
会社の勉強会でプログラミングHaskell14章を読んだ
ついに14章まで来ました。書籍は17章までなので、もう少しで完走できる、、、!
勉強会を始めたのが去年の8月なので、なんとか今年の8月までに完走したいところ。
あと、このブログに適当に貼ってる勉強会のメモをちゃんと整形して、プログラミングHaskellを読むための本みたいにすることができたら良いなぁ。
14章で扱うテーマ
- モノイド
- Foldable
- Traversable
モノイド
集合が「集合の二つの要素を結びつける結合的な演算子」と「その演算子に対する単位元」を持つとき、その代数的構造をモノイドと呼ぶ
-- Haskellにおける型クラス宣言 -- モノイドの概念の抽象化 class Monoid a where -- 単位元 mempty :: a -- 演算子 mappend :: a -> a -> a mconcat :: [a] -> a mconcat :: foldr mappend mempty -- 単位元と演算子が満たさなければならない則(モノイド則) mempty `mappend` x = x x `mappend` mempty = x x `mapppend` (y `mappend` z) = (x `mappend` y) `mappend` z
mempty, mappendといった命名から、空のもの、追加に関する演算子を連想するが、2つのメソッドは、あくまで「モノイド則」を満たしていれば良い。
Foldable
リストに対する畳込関数の考え方を、型変数をもつ型へと拡張したもの
モノイドを使用して、あるデータ構造の中にある全ての値を1つにまとめるという作業がよく行われる
例えば、リストの場合(他にはTreeなどが紹介されていた)
-- リストの中の値を1つにまとめる関数 -- 空リストにfoldを適用すると、モノイドの単位元memptyとなる -- 空でないリストでは、「リストの先頭」と「残りのリストを再帰的に処理した結果」をmappendで連結する -- ※Monoidなのはリストの要素であり、リスト自体ではないため注意 fold :: Monoid a => [a] -> a fold [] = mempty fold (x:xs) = x `mappend` fold xs
「データ構造の中の値をモノイドを使って畳み込む操作」は、リストやTreeだけでなく、型引数をもつ型全般に対して抽象化できる -> この抽象化はFoldableと呼ばれる
class Foldable t where fold :: Monoid a => t a -> a -- リストの中の値を1つに畳み込むイメージ foldMap :: Monoid b => (a -> b) -> t a -> b -- リストの中の値に一つずつ関数を適用し、その結果を畳み込んで1つの値にするイメージ -- リスト用高階関数の一般化、初期値と二つの値を連結する関数を引数として明示的に渡すので、モノイドに関する制約はない foldr :: (a -> b -> b) -> b -> t a -> b foldl :: (a -> b -> a) -> a -> t b -> a
また、Foldableクラスではデータ構造の中の値を処理するためのメソッドがたくさん提供されている(null, length, elem ...etc)
特に、データ構造を平坦化しリストへ変換するメソッドtoList
は、リスト向けのメソッドを流用して実装を与えるために便利(Foldableのデフォルト実装にも使われている)
-- toList :: t a -> [a] -- いくつか例 null = null . toList length = length . toList maximum = maximum . toList sum = sum . toList
Foldableに抽象化することの利点は、Foldableクラスのメソッドを使って、Foldableであれば何にでも使える汎用的な関数を定義可能になったこと
-- リスト構造の中の値の平均を求める関数をFoldableに抽象化 average :: Foldable t => t Int -> Int average ns = sum ns `div` length ns -- 上記関数は、(Foldable型クラスのインスタンスである)リストと木の両方に適用できる > average [1..10] 5 > average (Node (Leaf 1) (Leaf 3)) 2
toListによりFoldableは必ずリストに変換できる
-- 以下と同等らしい toList :: Foldable t => t a -> [a] toList = foldMap (\x -> [x])
これは[x]がMonoidであることが暗黙に前提となっている。ここでのmappendは(:)となる(実験より) つまり、foldMap(\x -> [x])は、対象のFolable t x型の各要素を[x]に置き換え(つまり t [x]型にして)それをfoldしたものになり、これは結局[x]の全要素を(:)で連結した要素を返すということになる。
参考: https://hackage.haskell.org/package/base-4.15.0.0/docs/src/Data-Foldable.html#toList (わかりにくいというかbuildがわからない)
https://namc.in/posts/foldables-traversals/ 上の定義が例示されている。おそらく実質同じであると予想
Traversable
あるデータ構造を走査するという考え方を抽象化したもの
class (Functor t, Foldable t) => Traversable t where traverse :: Applicative f => (a -> f b) -> t a -> f (t b) -- 参考 class Functor f => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b -- fmap :: (a -> b) -> f a -> f b class Functor t where fmap :: (a -> b) -> t a -> t b
疑問としては「Traversableである必要あるの?foldrでいけるのでは?」
dec :: Int -> Maybe Int dec n = if n > 0 then Just (n-1) else Nothing instance Traversable Tree where traverse g (Leaf x) = pure Leaf <*> g x traverse g (Node l r) = pure Node <*> traverse g l <*> traverse g r > traverse dec [1,2,3] => Just [0,1,2] > traverse dec (Node(Leaf 0) (Leaf 1)) => Nothing -- 型が違う気がする(雑直感)fmapだと[Nothing]みたいになりそう。Foldableじゃないとどうなるんだっけ?
Traversableである型tが関手であるという制約は、Traversableが変換の概念を一般化したものであり、fmapが提供されているという事実を反映している(関手はデータ構造の中の各要素に関数を適用していくという考え方の抽象化) また、型tがFoldableであるという制約から、Traversableの値は必要に応じて畳み込めることが保証されている
Rubyによるデザインパターンを読んだ
デザインパターンのおさらいと再確認のために読んだ。
InterpreterパターンやSingletonパターンなど、今日ほとんど使われないパターンも紹介されている。
この本の良いところは、それらをただ紹介するだけでなく、使われなくなった理由、なぜ使われなくなったのか、といった点まで深掘っているところだと思う。
理解しながら読み進め、一通り写経したので、今後は辞書的に活用していきたい。