「やり方ではなく、成果にこだわる」で良いのか、考えた

うまくいっていれば問題ない?

ちょっと前から、例えば、 Modern Agileとか、スクラムお化けの話など、すごく大雑把に言えば、「やり方に注目しないで成果に注目しよう」みたいな話を聞くことが多い気がしています。

Scrumの奴隷という言葉もありますし、プロセス実施して満足するな、みたいな文脈だったり、自分たちに合った方法を自分たちで見つけましょう、とか、具体的に課題を解決していくことが大事なんですよ、みたいな文脈で同意する部分も多い一方で、何となく自分の中で違和感を感じる部分がありました。(念のため申し上げると、否定する気持ちは全くなく、理解しきれていない、というイメージです)

違和感を頑張って言語化すると、守破離とか歴史から学ぶとか、これまで先人が築いてきたものをちゃんと理解するのも重要で、「型があるから型破り、型がなければ型無し」と言われるように、基礎(?)を学ぶことで出来るようになる(学んだ方が出来る可能性が高くなる)こともあるのでは、という気持ちです。

もしくは、例えば、めちゃくちゃなフォームで170キロの豪速球を投げるピッチャーがいたとして、その人がいるうちは試合に勝てて良いですが、一般化された(確率的に多くの人にあてはまる)良いフォームを知っておくこともチームとして大事なのでは、という気持ちです。

このもやもやが、別のことを色々と学ぶ中で、Connecting Dotsした感じがあったので、その内容を徒然なるままに記載していきます。

人の学び方について学び始める

最近、教育心理学概論読書会を中心に、認知科学モティベーション理論、行動経済学など、人の捉え方や学び方について、色々と理論を勉強しています。*1

その中で、人の学び方や目的も時代に合わせて変化していることを知りました。

例えば、親と同じ仕事をするために技術を伝承することが重要な時代には、徒弟制度の形でその仕事の内容をじっくり学びましたし、産業革命以降は、親と違う仕事をする可能性が高いので、仕事のやり方を覚えるために、要求されたルールを正確に覚える力が大事とされてきました(テストで良い点取ろうの時代)。今はルールややり方もどんどん変わるので、学んだことから次の学びに繋げていく力(前向きな学び)が重要なのかなと思います。

Elastic Leadershipを読む

エラスティックリーダーシップ ―自己組織化チームの育て方の中に記載されていた、リーダーシップの手段がフェーズによって異なる、という観点が、自分の中でとても大きな気付きでした。

最終的には自己組織化されたメンバーで構成されたチームが良いよ、と思っている一方で、フェーズによっては、チームを指揮統制していく必要がある、という考えは、言われてみれば当然な気もしますが、いつまでも統制されるチームになってしまわないか、とか、自分で考えなくなってしまうのではないか(自分で気付く機会を奪ってしまうのではないか)、とか、どう進めるのが良いのかなーと色々と考えさせられました。

プロセス主義という言葉を目にする

社内でQCサークルという業務改善のようなプロジェクトにアサインされて、その運営の方から課題整理のやり方などプロセスを習う機会がありました。その中で、教育という側面から、プロセス主義で、QCサークルのプロセスを一通り実施してもらうことを重視していて、成果にはこだわっていない、という話があり、これが普段の、「成果が大事」とは逆に思えたので、とても印象的でした。

また、FacebookのFeedで、とある学校の校長が、プロセス主義を大事にして教育を行っている、という内容を目にしました。

教育という視点で、プロセス主義という言葉が出てくるのが気になったので、軽くネットの記事を散見してみると、短期的な視点の成果主義と長期的な視点のプロセス主義という対比をされているのをいくつか見かけました。

もちろんどれも、成果が大事ではないと言っているわけではないのですが、目的によっては、短期的な成果が出るよりもプロセスを学ぶことが大事という考えがあるのかなーと思います。

QCサークル活動の基本と進め方―あらゆる小集団活動に役立つ (はじめて学ぶシリーズ)

QCサークル活動の基本と進め方―あらゆる小集団活動に役立つ (はじめて学ぶシリーズ)

伸びる子の育て方

伸びる子の育て方

繋がった結果

唯一無二の結論、というわけではないのですが、フェーズとか目的によって、プロセスを学ぶって大事なんだろうな、が今の考えです。

例えば、成長途中のベンチャーなど、170キロのピッチャーに頼って今日の試合に勝つことがとにかく重要な場合もあれば、リーダー戦略を取っているような企業では、仮に今日の試合に負けても、しっかりと選手を育てる仕組みを作ることが大事だったりすると思います。

ただ、プロセスを学ぶのって、フェーズとしては、最初の(序盤の)ものなのかなと思います。あと、学んだことはそのまま使えるとかずっと使えるわけではないですよ、なぜなら世の中はどんどん変化していくので。最終的には、学んだものをそのまま使うのではなくて、学んだものをベースに、新しく自分たちに合った、時代に合った、やり方を作っていかなきゃ駄目ですよね。より良い方法を明らかにしていなかなきゃですよね。が、Modern Agileってことなのかなー、というのが今回感じたことでした。

つまり自分の視点が序盤にあったから感じた違和感だったのかなと。引き続き精進していきたいです。

*1:参照書籍

教育心理学概論 (放送大学教材)

教育心理学概論 (放送大学教材)

理解とは何か (コレクション認知科学 2)

理解とは何か (コレクション認知科学 2)

モティベーションをまなぶ12の理論

モティベーションをまなぶ12の理論

人は勘定より感情で決める ~直感のワナを味方に変える行動経済学7つのフレームワーク

人は勘定より感情で決める ~直感のワナを味方に変える行動経済学7つのフレームワーク

SeleniumでChromeのデバイス指定を使ってスマホ表示させる方法

SeleniumChromeのデバイス指定を使ってスマホ表示させる

SeleniumChromeを動かす場合、ChromeOptionsを使ってデバイス指定をすることができます。デバイスにスマホ端末を指定することで、スマホ表示でのSelenium操作が出来るようになります。

リファレンス

Seleniumデザインパターン & ベストプラクティス

Seleniumデザインパターン & ベストプラクティス

やり方

# mobile_emulation = { "deviceName" => "Google Nexus 5" }
mobile_emulation = { "deviceName" => "Apple iPhone 6" }
caps = Selenium::WebDriver::Remote::Capabilities.chrome("chromeOptions" => { "mobileEmulation" => mobile_emulation })
driver = Selenium::WebDriver.for :chrome, :desired_capabilities => caps

内容

Selenium::WebDriver::Remote::Capabilitiesクラスのchromeメソッドを使うことでChrome用のdesired_capabilitiesを作ることができます。このとき、chromeOptionsmobileEmulationを指定すれば、デバイスを指定することができます。

指定できるデバイスの一覧のようなものは特に見つけられなかったのですが、Chromeで使っているものはおそらく使えるのではないかと思います。ただし、名称一致は必要なようで、"Apple iPhone 6"は、"iPhone 6"ではエラーになりました。

システムテスト自動化 標準ガイド CodeZine BOOKS

システムテスト自動化 標準ガイド CodeZine BOOKS

関連記事

genius.hateblo.jp

genius.hateblo.jp

genius.hateblo.jp

SeleniumでChromeを起動した際に、デフォルトのダウンロード保存先を指定する方法

SeleniumChromeを起動した際のデフォルトダウンロード保存先を指定する

SeleniumChromeを使う場合に、デフォルトのダウンロード保存先を指定する方法が分からなかったので調べたメモです。

Win + Rubyで確認しています。

リファレンス

Selenium実践入門 ―― 自動化による継続的なブラウザテスト (WEB+DB PRESS plus)

Selenium実践入門 ―― 自動化による継続的なブラウザテスト (WEB+DB PRESS plus)

やり方

色々調べましたが、プログラム内で直接定義するやり方は分からず、Chromeで使うUserDataのファイルをプログラム内で指定することで実現しました。自分が普段使っているChromeのUserDataとは別のファイルを設定できるので、ファイルをリポジトリに入れて使うこともできます。

CHROME_USER_DATA_PATH = Dir.pwd + "User Data\Default" # 絶対Pathで指定

caps = Selenium::WebDriver::Remote::Capabilities.chrome("chromeOptions" => {"args" => ["user-data-dir=#{CHROME_USER_DATA_PATH}"]})
driver = Selenium::WebDriver.for :chrome, :desired_capabilities => caps

内容

Selenium::WebDriver::Remote::Capabilitiesクラスのchromeメソッドを使って、Chrome用のdesired_capabilitiesを作ることができます。

ChromeOptionsを使うことで、色々な細かい設定ができるのですが、ここでUserDataのパスを指定して、自分の好きな設定を読み込ませることができます。そのため、あらかじめChromeの操作でダウンロード保存先を希望のものに変更しておいて、それで生成されたUserDataを使うことで、SeleniumChromeを起動した際のダウンロード保存先を任意のものに設定することができます。

ちなみに、ファイルとして置くのは(ダウンロード保存先を指定したい場合には)AppData\Local\Google\Chrome\User Data\Default\Preferencesのファイルだけで良いようでした。Macの場合には~/Library/Application Support/Google/Chrome/Default/Preferencesにあるようです。(Macの動作確認はしていないです)

Seleniumデザインパターン & ベストプラクティス

Seleniumデザインパターン & ベストプラクティス

関連記事

genius.hateblo.jp

genius.hateblo.jp

genius.hateblo.jp

SeleniumをChromeで動かす方法

SeleniumChromeで動かす

Seleniumでブラウザ操作を行う場合、firefoxであれば特に追加設定なく動かせるのですが、他のブラウザを使う場合には、ちょっとした設定(環境構築)が必要です。

ここでは、Chromeのやり方について記述します。

実践 Selenium WebDriver

実践 Selenium WebDriver

環境

私はWin7Linux(Amazon Linux)+ Rubyで動作確認しました。

おおまかな流れ

  • chromedriverのファイルを取得する
  • chromedriverが使えるようにする
  • 必要に応じてプログラムを修正する

chromedriverのファイルを取得する

こちらからダウンロードできます。

Seleniumを動かす環境に合わせて、ダウンロードしてください。

chromedriverが使えるようにする

WinとMac,Linuxでちょっと違います

Winの場合

C:\RubyXXX\binなど、Rubyがインストールされているディレクトリにchromedriverのファイルをコピーしてください。

Mac,Linuxの場合

以下のような感じでシンボリックリンク作れば実行可能になります。

ln -s $HOME/selenium/chromedriver /usr/local/bin/chromedriver

必要に応じてプログラムを修正する

:firefoxみたいな感じでブラウザ指定しているところを、:chromeとするなど、必要に応じてプログラムを修正します。

driver = Selenium::WebDriver.for :chrome

その他注意

基本的には、ブラウザ間の違いはSelenium WebDriverが吸収してくれると思うのですが、動かなくなる場合もあります。

例えば、私はFirefoxのプロファイルで書いていた部分をChrome用に書き換えるのに苦労したり、FireFoxでは押せていたボタンがChromeでは押せなくなって、Driver操作の記述を変更したりすることなどが必要でした。

Selenium実践入門 ―― 自動化による継続的なブラウザテスト (WEB+DB PRESS plus)

Selenium実践入門 ―― 自動化による継続的なブラウザテスト (WEB+DB PRESS plus)

関連記事

genius.hateblo.jp

genius.hateblo.jp

genius.hateblo.jp

画像DIFFを取得するimgdiffというgemを作りました

コマンドラインで使えて画像DIFFが取得できます

https://github.com/bonbon0605/imgdiff

前から作りたいと思っていたのですが、一念発起して作成しました。

rubygemsの登録もしたので、gem install imgdiffで使えます。

CLIにて、

$ imgdiff 対象のイメージ1 対象のイメージ2 (出力先)

という感じで使うことを想定しています。

同じところは黒くなるので、黒くないところが差分になります。 画像の差分取得には、rmagick(ImageMagick)を使っているのですが、 色の差の絶対値を取得しているようで、同じだったら差が0で黒、ということみたいです。

参考にさせて頂いた情報

この2つのサイトのおかげで、rubygemsに登録する勇気が持てました。多謝。

コマンドラインツールにするのにThorを使いました

はまったところ

コマンドラインツールのテストの書き方

CLIでのコマンド実行を前提としていたので、どうやってテストを書くのか良く分かっていませんでした。 コマンドライン引数も渡すし、、、ということで、

system "bundle exec imgdiff A.jpg B.jpg"

みたいに、外部コマンド実行の形で最初書いていたのですが、色々無理があるなと思い調べたところ

ImgDiff.new(["ImageA","ImageB"]).invoke(:method)

というような形で実行できることが分かりました。(newの引数の配列がコマンドライン引数になる)

travisのテストがpassしない

ローカルではテストがpassするのに、travisでテストが落ち続け、何故か分からずに嵌まりました。 DIFFの画像を保存する権限がないのかとか色々考えたのですが、解決せず、、、結局、全然違う理由でした。

テストとして、正解のファイルリポジトリ内に用意しておいて、 それと比較して同じものかどうかで結果が正しいと判断しようとしていたのですが、 (出力結果「ImageC」は用意してある「ImageD」と同一のファイルだから正しい結果だ、というような考え)

どうやらリモートリポジトリの画像がちょっと期待しているものと違ってしまっていたようで、一致しなくてNGという内容でした。(正解が正解ではなかった)

なぜ違ってしまったのかはよく分かっていないのですが(劣化?)、「用意してあるファイルと同じ」という思想を改めることでpassできました。

rmagickでの画像差分について

こちらの記事を参照ください。

genius.hateblo.jp

パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)

rmagick(ImageMagick)を使って画像の差分を取得する

画像差分が取りたい

修正前、修正後の結果を画像比較できるとテストの安心感が高まりそうという気持ちから、rmagickの機能を調べてみました。

参考にしたもの

確認環境

  • ruby 2.2.3
  • rmagick 2.15.4

rmagickのインストール方法

Githubのwikiを参照下さい。

画像取得について

Seleniumで画像キャプチャを取ることを想定していますが、今回は画像取得については触れずに別で書こうと思います

画像の差分を取る

rmagickで画像を読み込む

まずはrmagickで画像を読み込むところから。

require 'rmagick'

img = Magick::Image.read('./sample.jpg').first

Magick::Image.readの結果が配列で返ってきますので、firstとしています。

画像がどのくらい違うか確認する:Magick::Image#difference

Magick::Image#differenceというメソッドがあるので、これを使うと同じ画像かどうかが分かるようです。リファレンスの説明にはCall the IsImagesEqual function.という記述がありました。

使ってみる

require 'rmagick'

img = Magick::Image.read('./sample.jpg').first

p img.difference(img)
# => [0.0, 0.0, 0.0]

同じだと0で返ってくるようです。

返ってくる値は何なのか

リファレンスを見ると mean_error_per_pixel,normalized_mean_error,normalized_maximum_errorの3つの値だと書いてありました。

画像系に詳しい人だともしかしたらこれで分かるのかもしれませんが、私にはよく分からず。

ImageMagick側を見れば分かるかな、ということでImageMagickをキーワードにググってみたところ、このページを発見。

曰く、

mean_error_per_pixel:
  This value is the mean error for any single pixel in the image.
normalized_mean_error:
  This value is the normalized mean quantization error for any single pixel in the image.
  This distance measure is normalized to a range between 0 and 1.
  It is independent of the range of red, green, and blue values in the image.
normalized_maximum_error:
  Thsi value is the normalized maximum quantization error for any single pixel in the image.
  This distance measure is normalized to a range between 0 and 1.
  It is independent of the range of red, green, and blue values in your image.

ということみたいです。

違いがあるかないかという点では0であるかを見れば良さそうで、どのくらい違うかはこの値を見て判断することが出来そう(出来そうだけどどうやったら出来るかはいまいち分からない)

実際どんな感じになるのか

適当な画像を用意して比較してみました

全く同じファイル

上述の通り [0.0, 0.0, 0.0]

同じファイルをサイズを変えて比較してみる
  • 640×480のGIFファイルを用意して320×240にサイズ変更したものと比較すると

[521.8944702148438, 0.0070885103517538675, 1.0]

ファイルの内容をちょっと変えてみる
  • 元々のファイルに「テスト」みたいな文字を加えたものと比較すると

[37.7886962890625, 0.0004698213267336715, 1.0]

  • 「テスト」の文字をとても小さくすると

[5.609326362609863, 5.188514493355332e-05, 0.9294117647058824]

  • 「テスト」の文字をとても大きくすると

[5286.41259765625, 0.07936659467536895, 1.0]

  • 背景色の色味をちょっと変えてみる

[8371.1787109375, 0.021862748084959475, 0.17254901960784313]

  • 背景色の色味を強めに変えてみる

[30593.5625, 0.29990951254957343, 0.6313725490196078]

とりあえずの結論

はっきりは分からないのと、テストデータも手元のツールで簡単に使っただけなので、正確性があるかないが大変に怪しいですが、左端の値が一番感覚と近いかなぁという印象(違いが小さいと値も小さい)。本当は閾値みたいに使えたらと思ったのですが、ちゃんと使うには、今のところ値の意味を理解できていないがために難しそうです。もちろん、閾値ではなくて、「0じゃなかったら違う画像!」みたいには使えると思います。

差分を画像として確認する:Magick::Image#composite または Magick::Image#composite!

実際に、DIFFの結果が見たい、という場合にはMagick::Image#compositeまたはMagick::Image#composite!が使えます。DIFFを意識した重ね合わせができて、同じところは黒くなるので、黒くないところが違うところ、という形で確認できます。

使ってみる

require 'rmagick'

img_a = Magick::Image.read('./sample-a.jpg').first
img_b = Magick::Image.read('./sample-b.jpg').first

img_a.composite(img_b,Magick::NorthWestGravity,Magick::DifferenceCompositeOp).write('./image_diff.jpg')

compositeメソッドについて

  • 最初の引数は、合成する画像データです

  • 2つ目の引数は、画像を合成する位置の指定です。0,0とかでも良いですし、左上からであればMagick::NorthWestGravity、中央からであればMagick::CenterGravityのようにモジュール内に定義してある定数を使うこともできるようです

  • 3つ目の引数は、重ね方の指定です。Magick::DifferenceCompositeOpの指定をすると、同じところが黒くなって重なります。DIFFでなければ、別の値を指定して画像の合成ができるようです

ruby gem を作りました

genius.hateblo.jp

インタフェースデザインの心理学 ―ウェブやアプリに新たな視点をもたらす100の指針

インタフェースデザインの心理学 ―ウェブやアプリに新たな視点をもたらす100の指針

Seleniumデザインパターン & ベストプラクティス

Seleniumデザインパターン & ベストプラクティス

複数チームでのスクラムを改善するために、Nexusガイドを読んでみました

背景

現在、スクラムで開発をしているのですが、諸般の事情によりスクラム2チーム体制になっているため、ところどころスクラムガイドから外れた内容になっていることが気になっていました。

そんな中、スクラムの規模を大きくしていくための手法として、LeSSやNexusなどがあることを知り、知見を得るために、Nexusの内容をまとめたNexus Guideを読んでみました。

知りたかったこと

いきなりNexusを導入する、というよりは、現在2チーム体制で試行錯誤している内容に対して、改善のヒントがないかな、というイメージで読みました。主に知りたかったこととしては以下となります。

  • チーム間の情報共有
    • チームごとにプロダクトバックログを分けるのか
    • スクラムイベントは別々に行うのか、一緒に行うのか
  • プロダクトオーナーの人数/役割
    • チームごとにプロダクトオーナーが必要か
    • 全体で1人だった場合、チームとのコミュニケーションに問題がないのか
  • スクラムで定義されていないチームがないか(基盤チームのような、全体統括のチームが必要にならないか)

Nexus Guideを読んでみての所感

チーム間の情報共有

チームごとにプロダクトバックログを分けるのか

Nexusガイドによると、

複数のスクラムチームが、ゴールを達成する統合インク
リメントを構築するために、1 つのプロダクトバックログに取り組んでいる

とありましたので、プロダクトバックログは1つで良いみたいです。

あくまでも、1つのプロダクトバックログで優先順位を考え、それをどう実現するかを複数スクラムチームで考えて実施する、ということかなと思います。エリアプロダクトオーナーみたいな考えでは、エリアプロダクトオーナーはプロダクトバックログを分けて持つ、みたいなことを聞いたことがあった気がしたので(違うかもしれないですが)、どっちなのかなと思っていましたが、やはり1つで良いのだなという気持ちです。

ただし、実施する際には各スクラムチームの担当ストーリーを決める必要があるとは思いますので、そういう意味では、一時的に分割されたプロダクトバックログは持つようになるのかもしれないです。

スクラムイベントは別々に行うのか、一緒に行うのか

必要に応じて全員集まったり、代表者が集まったりする、というイメージかなと認識しています。全体で調整が必要なものは代表者が集まり、そうでないもの(例えば、スプリントレビュー)は、全員集まるという形みたいです。まあ、いつも全員集まったら色々なコストがすごいことになるので、代表者が集まる、というのも分かる気はします。逆に言うと、全員集まれるぐらいの人数なら、代表者を集まる必要もないのかな・・・・

私のそもそもの疑問としては、全く交わらずに別々でイベントを実施する、という可能性もあるかなと思っていたので、そういう意味では、相互の影響を気にしながらイベントを実施していく、という考えのようです。(そりゃそうだよな、という気持ち)

プロダクトオーナーの人数/役割

チームごとにプロダクトオーナーが必要か

これは、はっきりは書いてなかったと思うのですが、Nexusガイドの表紙にも書かれている図を見る限り、各スクラムチーム内にもプロダクトオーナーが書かれているので、各チームごとにプロダクトオーナーが置かれるように定義されているのかなと思います。(もしかしたら兼任という可能性もあるかもしれないですが)

ただ、仮に各チームにプロダクトオーナーがいるとした場合に、その役割がよく分からないんですよね。プロダクトバックログ複数に分かれるなら、(プロダクト全体ではなくて)スコープ限定した範囲での価値の最大化を考える人、みたいに捉えられるかもしれないですが、1つのプロダクトバックログだとすると、その持ち主の代わりに何が出来るんだろうかと思います。。。

全体で1人だった場合、チームとのコミュニケーションに問題がないのか

これは、良く分からなかったですね・・・

1人だと情報伝達や問い合わせへの回答が大変になりすぎないのかな、と思います。

プロダクトオーナーには、複数のスクラムチームで統合され
たプロダクトや作業の価値を最大化する実行責任がある。
プロダクトオーナーは、Nexus で作成する統合インクリメントの価値を最大化するために
、プロダクトバックログの順番とリファインメントに最終的な責任を持つ。そのやり方は、
組織・Nexus・スクラムチーム・個人によって大きく異なる。

などど書かれていましたので、プロダクトオーナーは引き続き、開発チームとうまくやらないといけないけど、具体的にどうするかはケースバイケースだから自分で考えて、ということかなと思います。

スクラムで定義されていないチームがないか(基盤チームのような、全体統括のチームが必要にならないか)

これはNexus 統合チームとして、思いっきり定義されていました。スクラムチームに共通する課題解決や仕組み作りについて、牽引していくチームなのかなと思います。

私のところでは、何となく開発チーム以外(プロダクトオーナーとスクラムマスター)で、こうした課題に取り組むか、プロダクトバックログに追加して、開発チームで対応するか、いずれかでしたので、Nexus統合チームのような存在を改めて定義していくのは良いな、と感じました。

ちなみに、Nexus統合チームに所属するのは、スクラムチームの人で良くて、かつ、通常のスクラムチームの作業よりもNexus統合チームの作業の方が優先される、と明確に定義してあるのが良かったです。(全体に関わるところだから、という理由のようです)

別の疑問(残件)

上記のこと以外にも、気になることが出てきましたが、まだ未消化です。

  • 作業がプランニングよりも順調でなかった場合の考え方
    • あるチームだけがNGだった場合、より優先順位の高いものがNGになる可能性もある。これをどう考えるか(仕方ない or 改善する必要あり?)
    • スプリントの途中で、あるチームが別のチームを手伝うようなことが発生するのか(あくまでも、チーム内で作業は閉じる?)

複数チームいると、どうしても責任分界点みたいな話になりがちな気がするのですが、考え方としては、チーム力を合わせてプロダクトを成長させる、というのが健全かなと。そのために、より優先順位が高い案件を実施中のチームがNGになりそうな際、自チームの達成を犠牲にしてサポートするべきか、あくまでも役割分担の中でのベストを尽くすべきか(駄目なら駄目で改善していけばよいので、変に複雑にしない)、みたいなことが気になりました。

まあはっきり書いてないということは、ケースバイケースで、どちらもで良いってことなのかな〜

エッセンシャル スクラム

エッセンシャル スクラム