読者です 読者をやめる 読者になる 読者になる

鯨飲馬食

いろいろつまみ食いで勉強したことのメモ書き

免疫マップをつくってみた

2017/03/23 (木) にDevLove関西の勉強会に参加して免疫マップをつくってみました。

devlove-kansai.doorkeeper.jp

最初にファシリテーターの広瀬さんから、書籍

なぜ人と組織は変われないのか――ハーバード流 自己変革の理論と実践

なぜ人と組織は変われないのか――ハーバード流 自己変革の理論と実践

に書かれている免疫マップをつくってみるという今回のワークの目標と、その作成がメンタルに痛いものでアジア人には向かないと言われているが、安全な作り方があるのかに興味を持っているというお話があって、早速ワークに突入しました。

まず個人ワークで自分の「改善目標」とそれに対する「阻害行動」を書き出して、テーブルの隣の人とお互いのものを説明しあいました。ここまでは全然痛くなかった。

次に被験者になりたい人が立候補することになったのでせっかくの「素振りの場」なので手を上げました。手を上げた人を含めて4人一組くらいのグループに分かれて、被験者が改善目標と阻害目標について周りの人に一方的に説明する時間があり、その後幾つかの質問を受け答えをする時間がありました。

改善目標(私が最も改善するべき点を一つ上げるとすれば何?)
  • まわりから見て楽しんで仕事してる風に見えるようになる
    • not いそがしそう、not しんどそう
      • 後から聞くと、いそがしそうだったから聞けなかったということがよくある→話しかけやすくしたい
    • まわりの人も会社に来るのが楽しいと思える雰囲気をつくりたい
阻害行動
  • 誰かが困っていて、誰かがやらないといけないけど面倒で誰もやろうとしないことをすすんで拾ってやってしまう
  • 解決しない課題を解くのに残業してしまったり、課題のことを家でプライベートの時間にも考えてしまっていたりする
  • そういうことしてるから余裕がなくなっている

あとは後ろ向いといてと言われてメンバに背を向けて座り直させられました。そしてその後ろで、メンバは被験者が居ない体で(でも聞こえるように)好き勝手に言い合って阻害行動を深掘りしたり、その行間を推測したりして、その元になっている裏の目標を掘り起こすという時間になり、私が出した改善目標と阻害行動を元にメンバが好き勝手に話し合うのです。後ろ向いて聞いていると、「そんなこと考えてたら自分は悪い人だな。でも確かにそう考えてたかもしれない」と思ったり、「そういうわけじゃない〜」と言い訳したくなるような辛い事をズバズバ言ってくれていて、自分一人だとそこまで一気に掘り下げていけない所まで短時間で連れて行ってもらえました。

裏の目標
  • みんな楽しいわけでもないのに楽しんでいるようにふるまいつつ仕事する会社にしたい
  • 自分からメンバに話しかける努力をせずとも、まわりから話かけてもらいたい
  • 面倒な課題を自分でやっちゃうことで若手の成長の芽をつみ、自分個人の評価を上げたい
  • 面倒な課題があたかもないかのように取り繕って隠蔽したい

メンバに言われたことを元に裏の目標としてその場で書き出した内容は、かなり言われて凹んだ後だったのでマイナスの面が強くでています。
ワークではここまでだったので、残りの「協力な固定概念」は帰ってから考えました。

強力な固定概念
  • 同じ仕事をするにしても、つまらなそうにただこなすより、興味持って楽しんでやった方が発見あるんじゃないかという思い
    • でもそれって過去の自分の体験に重ねてしまっておりその人自身を見てないかも
  • みんなが成果を出せる領域へ早く連れて行かないといけない
    • まわりの成長を待って、まわりのスピード感に合わせてやってたら遅過ぎる。基準になるスピード感を再定義してしまいたい
      • と言いつつも実際はいそがしさに足を掬われてそこまでのスピードを出せていない現実があるかも
    • 綺麗なものを一切見ずに面倒なことばかり見てたらそれにのまれてしまわないか心配。先に美味しいところを味わってもらいつつ、目指していく目標地点を共有したい
      • 周りの力を信じる気持ちが足りてなくて隠しすぎているかも

ここまで厳しい視点での深掘りを短時間でやるのは一人では無理だったと思うので、同じグループの3人の方々には感謝の気持ちがいっぱいです。勇気出して被験者立候補してよかったと思います。
こうして作った免疫マップを元にどうやって改善していけばいいかは宿題で、正直どう進んでいけばいいかのイメージは持ててない状態。ただ、主催の中村さんから、「会うたびに顔が暗くなっていってますよ」と言われているので改善すすめるのは必須かと思ってます。まずは読むの厳しいと言われていた冒頭の本を読みつつもう少し考えてみようかな。

マージ済みのコミットハッシュからプルリクエストを開く

コード見てもわからない情報(その変更が必要となった背景や、数ある実現方法の中からどうしてその方針で変更したのかなど)がコミットメッセージにもJIRAのチケットにも書いてなくて、当時居た人をなんとか探して聞きまわる、そして大概は昔のことなので忘れている…といった状況が当たり前だったのが一年半くらい前。最近はチームメンバが積極的にJIRA のチケットに要件確認のやりとりや、調査のメモなど、どんどん情報を残してくれるようになり、新しめのコードについては人の記憶に頼らなくても情報収集できるようになってきた。

次に課題として出てきたのが、チケット見てもコメントがたくさんありすぎて、要点を抽出するのが大変という問題。チケットクローズするときに最後のコメントとしてサマリを残してますーという声は一部からあったけど、まわりを見渡すと、最後にやったことの記録があればまだ良くて、スプリントの終わりに閉じ忘れてたチケットを急いで閉じる時とか、何も書かずに閉じちゃってることもしばしば。

通常の導線上でサマリを残す一手間をゆるく強制できないかなと思っていて、こないだの上司との 1 on 1 で相談してみたら、少し前にレビューの効率悪いのをどうにかしようという文脈でプルリクエストにサマリを書くという話をしていたのを思い出した。プルリクエスト出すときにやったことの要点と関連情報をちゃんと整理して書き、レビューアに十分な情報を出した上でレビュー依頼することで、より深いレビューをしようという話だった。リリースのために必要な一つのステップとして自然に開発フローに組み込めるのでこれはよい。

コミットメッセージにはチケット番号を入れるというルールにしてるので、次の開発や不具合調査のときに git blame すれば、過去にそのコードを変更したチケットを開くのは簡単にできて、さらにJIRAとBitbucketを連携させているから自動的にチケットからプルリクエストへのリンクも張られていて、プルリクエストに書かれたサマリに到達できる…。待てよ、毎度毎度リンクを辿るというのは面倒だな。GitHub の pull request 開くやり方は見たことあるし、自分たちが使っている Bitbucket でも同じようにできるよねと思って、元ネタ

qiita.com

を見直してみると、マージコミットを取ってくる所までは流用できるけどその先を hub に任せてるのでそちらは何とかする必要があることがわかった。

ここで先日 OSS Gate というイベントに参加して刺激をもらっていたのが効いた。

oss-gate.doorkeeper.jp

やってみたらよいということで実装してみて期待した動作になったので、初めて PyPI へ登録するところまで勢いでやった。

github.com

以下は開発の流れのメモ。こういう水面下でばたばたした情報も残しておくと後で何かの役に立つかもというのを OSS Gate に参加して思い出すことができた。

  • BitbucketでもGitHubと同様に、マージボタンのコミットメッセージにプルリクエストの番号が定形で入るのは知ってた。
  • それとリモートURLをパースしたものと合わせれば、プルリクエストの URL は生成できる!いけそうだ。
  • 外部コマンド実行して出力取るの、commands で簡単にできる。(deprecated と書いてあるのこの時点では見落としてた)
  • ブラウザを開くのどうしよう。macOS でも Windows でも Linux でも使えるといいんだけど。
  • open, start, xdg-open を叩くようなの書けばいけそうだけど面倒だなー。
  • webbrowser 使えばいけるやん。
  • 引数もかっこよく処理したいなー。 argparse 使っちゃお。
  • 説明読んでも add_argument の使い方分からないので試行錯誤して何とか解決 (わかりやすい チュートリアル へのリンクがあったのは後で気付いた)
  • commands は python3 だとなくなってる。subprocess で書き直そう
  • せっかくだし、PyPI にも登録しておこうか。
  • https://pypi.python.org/pypi の Register からユーザ登録と、Package Submission から PKG-INFO の登録を試みる。
  • エラーページに飛ばされる。pypi の調子が悪い?何度かやったらうまくいった。
  • Package Submission のページからリンクされていた、twine を使ってアップロード
  • pip install openpr でインストールできるようになった!

2017 = 9^2 + 44^2

昨年末の納会のビブリオバトルで自分が影響を受けた本として紹介するのに「ハッカーと画家」を久しぶりに読み返しました。表題にもなっている「ハッカーと画家」では、良いプログラマと良い画家の共通点として、ともに書く(描く)ことによって学ぶこと、よい作品を見ることで学ぶことが挙げられている。そして、書いたものをどんどん書き直して洗練していくこと、見たり使ったりする人の気持ちになって「共感」を得られるものを創っていくことの大切さを述べている。2000年代初頭に書かれたエッセイを集めたもので、十数年が経過してプログラミングをとりまく状況も変わってきているので若手が読んで共感できる内容がどれだけあるかはわからないが、少なくとも自分にとっては再度読み返して、変わらず大切なことに気づけたのでよい本だと思いました。納会のセッションでその気持ちがうまく伝えられたかは不明だけど。

昨年2016年の自分の振る舞いをふりかえると、書く前に考えすぎちゃって、口だけ動かして何も出来てないってことが結構ありました。そんなこともあり年末の悪あがきでちょっと書いて(古いフレームワークの上でやってて遅々として進まない状況だった事柄について、いっその事と思い車輪の再発明をして)みたら、わりと簡単に書けちゃうのがわかったり、書いているうちに改善すべき点が見えてきたりして、やっぱり口じゃなくて手を動かすことに注力しないと物事は進まないなというのを実感しました。2017年はどんどんコードを書いて、自分で使ったり周りの人に使ってもらいながらどんどん直して洗練しつつ、物事を前に進めていくことを自分への課題として課します。

今年もプログラミングを楽しみます!

ドメイン駆動で開発する:初期のラフスケッチから実装まで

devlove-kansai.doorkeeper.jp

参加から半月ほど空いてしまいましたが、2016/10/31 のDevLove関西勉強会で増田さんの話が聞けるということで定時ダッシュで京都まで行ってきました。増田さんのトークを聞くのは初めてでしたが、以前勉強会(の懇親会)で会った人が増田さんと一緒に仕事をしてすごい人だと話をされていたので楽しみにして参加したところ、終始生き生きした語り口にぐいぐい引き込まれました。

最初は、よくない設計、「でも動いている」という動いた駆動の開発から抜け出そうと試行錯誤をしていてドメイン駆動設計に出会い、それを実践していくうちに自然と良い設計ができてきて、業務を学びながら開発する楽しさ、対象領域の知識とコードが一致していることによるわかりやすさ、安心感が得られてきたというひとつの物語を聞くことができました。話の中で何度も「今も試行錯誤を続けている」という言葉が繰り返し出てきて、常に一歩先へ進もうとする姿勢に刺激を受け、自分も常にちょっとずつでも前に進んでいきたいという思いを持ちました。

勉強会の前の日に名前にまつわる過去の出来事をいろいろ思い出していて、どうしたら名前の重要性をまわりに伝えられるかなというのを考えながら参加して、帰ってから話の中で出てきたオブジェクト指向エクササイズにトライしてみて、やっぱりプログラミングは難くて楽しいというのを実感した。こういうのを自分で楽しみながらやってみると、説明してふーんで終わられるよりも体で覚えていけてよいのかな。

1分間マネジャーの時間管理

1分間マネジャーの時間管理 (フェニックスシリーズ)

1分間マネジャーの時間管理 (フェニックスシリーズ)

  • 作者: ケン・ブランチャード,ウィリアム・オンケンJr,ハル・バローズ
  • 出版社/メーカー: パンローリング
  • 発売日: 2013/02/16
  • メディア: 単行本(ソフトカバー)
  • この商品を含むブログを見る

「上司捕まらない問題」で検索したらヒットして、部下の視点で役立つことがあるかもと思ったので読んでみた。この本では仕事をサルに例えて、本来部下が世話をするべきサルを引き受けてしまうことでお互いのためにならない状況を生み出してしまう危険性について説かれている。

結局のところ、自分の上司が捕まらない問題に関しては本書に書かれているような問題、つまり上司が部下のサルを引き受けすぎているために時間がないという状況ではなさそうという考えに至ったので、元々気にしていたことへの直接的なヒントは得られなかったが、一方で自分がまわりのチームメンバに対して余計なおせっかいをするサル収集家になってしまってないかが気になった。その後同僚から相談を受けたときの自分の行動を観察していると、相談受けるにあたって情報不足なときに引き取った上でこちらで情報収集してて、もし即座に(何の情報が不足しているかだけ伝えて)突き返していれば、自分が元々やってた作業も相談を受けた内容についても両方もっと速くできていたんじゃないか、次はもっとうまくやれるように一歩前進できたのではないかと思った。上司、部下の関係じゃなく上下関係のない同僚の間でも不要なサルの引き受けをして成長の機会を失わないように気をつけます。

ぷしゅ よなよなエールがお世話になります

ぷしゅ よなよなエールがお世話になります

ぷしゅ よなよなエールがお世話になります

ヤッホーブルーイングの商品はお酒売り場で見かけてラベルが目につくので気になっていて、でも実際に買って飲んだのはわりと最近。名前が面白くて、香りがよくておいしくて、僕にとっては飲むと元気になるビールという位置づけになっている。そんな会社のことを書いた本を見つけて、読み始めたら興味深くて一気に読み終えてしまった。

この本ではそのヤッホーブルーイングがどん底の時期から成長していく過程の話が書かれている。うまくいったことだけでなく、うまくいかなかったことも含め、やってみた結果を評価して、そこから何かを学んで次に進んでいったかが、当事者の井出さん(てんちょ)の視点から書かれている。大きな話題としてはファンを作っていくことと、同じ方向を向くチームを作っていくことが取り上げられていたが、会社が成長していく中で役割の移譲ができている点が、ふだん自分が悩んでいることと関連して興味をひいた。価値観を伝え、繰り返し繰り返し伝えていくことで、同じ方向を向いて仲間を先導してくれるメンバーをつくることに繋がっているのかなと思った。

どん底の時期の星野佳路さんとのエピソード「会社をたたんで釣りでもしよう」の、支えてくれる人の一言で奮い立たされた話も印象に残っている。読む前は知らなかったヤッホーブルーイングという会社自体のこと、商品の命名の裏側などもちらりと見ることができて、ヤッホーブルーイングのファンになりそうな気配。

STDIN に対する GetFileInformationByHandle

事の発端は、Windows 上で Emacs の M-x grep で platinum searcher を使ったときにディレクトリを指定しないと何も引っかからないのを調べてて、 cmd.exe ではディレクトリ指定しなくても検索結果が表示されるが、msys2 だとディレクトリ指定しないと応答なしになるのはなんでやろと思ったこと。

platinum searcher のソースを見て、 the_platinum_searcher/platinum_searcher.go at master · monochromegane/the_platinum_searcher · GitHub

if p.givenStdin() && p.noRootPathIn(args) {
    opts.SearchOption.SearchStream = true
}

のところにあたりをつけて、givenStdin() で呼んでいる os.Stdin.Stat() 挙動に注目した。まず

package main

import (
    "fmt"
    "os"
)

func main() {
    _, err := os.Stdin.Stat()
    if err != nil {
        fmt.Println(err.Error())
    } else {
        fmt.Println("success")
    }
}

というサンプルプログラムを書いてビルドし

% go version
go version go1.7.1 darwin/amd64
% GOOS=windows GOARCH=amd64 go build stdin_stat.go
% ls stdin_stat.exe
stdin_stat.exe*

Windows7環境で実行すると msys2 では

$ ./stdin_stat.exe
success
$ echo hoge | ./stdin_stat.exe
success

cmd.exe では

>stdin_stat.exe
GetFileInformationHandle /dev/stdin: The handle is invalid.
>echo hoge | stdin_stat.exe
success

となった。次にもう少し詳細を見るため go/src/os/stat_windows.go の実装を参考にして

package main

import (
    "fmt"
    "syscall"
)

func main() {
    ft, err := syscall.GetFileType(syscall.Stdin)
    if err != nil {
        fmt.Println(err.Error())
    } else {
        fmt.Println(ft)
    }

    var d syscall.ByHandleFileInformation
    err = syscall.GetFileInformationByHandle(syscall.Stdin, &d)
    if err != nil {
        fmt.Printf(err.Error())
    } else {
        fmt.Println("GetFileInformationByHandle success")
    }
}

というサンプルプログラムを書いてビルドし、WIndows7 環境で実行すると、msys2 では

$ ./stdin.exe
3
GetFileInformationByHandle success
$ echo hoge | ./stdin.exe
3
GetFileInformationByHandle success

cmd.exe では

>stdin.exe
2
The handle is invalid.
>echo hoge | stdin.exe
3
GetFileInformationByHandle success

となった。 GetFileType function (Windows) によると 3 = FILE_TYPE_PIPE, 2 = FILE_TYPE_CHAR であり、 GetFileInformationByHandle function (Windows) によると pipe のハンドルを第一引数に指定するなとある。 ただし go の実装では os: fix Stdin.Stat() on windows · golang/go@ebd67ba · GitHub の対処により pipe なら GetFileInformationByHandle を呼ばないようになっている

github.com

したがって、上のサンプルで pipe に対して GetFileInformationByHandle を呼んでおり成功してしまっているがそこはどうでもよくて、気になる点は

  • msys2 で pipe に繋いでないときに STDIN が pipe と判定されているのは何故か?
  • cmd.exe で pipe に繋いでいないときに GetFileInformationByHandle が失敗するのは何故か?

の2点。ここで、C++ (Visual Studio 2015) で直接 Win32 API を呼び出すサンプルプログラム

#include "stdafx.h"
#include <Windows.h>

int main()
{
    HANDLE hdl = GetStdHandle(STD_INPUT_HANDLE);
    BY_HANDLE_FILE_INFORMATION info;
    printf("type=%d\n", GetFileType(hdl));
    BOOL result = GetFileInformationByHandle(hdl, &info);
    if (result == 0) {
        printf("failed %d\n", GetLastError());
        return -1;
    }
    printf("success\n");
    return 0;
}

をビルドして実行すると、msys2 では

$ ./ConsoleApplication1.exe
type=3
success
$ echo hoge | ./ConsoleApplication1.exe
type=3
success

なのに対し、cmd.exe では

>ConsoleApplication1.exe
type=2
failed 6
>echo hoge | ConsoleApplication1.exe
type=3
success

System Error Codes (0-499) (Windows) によると 6 = ERROR_INVALID_HANDLE なので go から呼んだ時の挙動と整合しているのが確認できた。

まとめ

syohex.hatenablog.com

に書かれている os.Stdin.Stat() の挙動について、go がその内部で呼び出している Win32 API GetFileType, GetFileInformationByHandle を C++ プログラムから直接呼んで確認した。

pipe を食わせていないときに cmd.exe で os.Stdin.Stat() が失敗することや、msys2 で pipe と判定されてしまうことは現状の go の制限事項と思う(アプリケーション側で windows かどうかで条件分岐しないといけないのは望ましい動作ではないと思う)が、go から Win32 API 呼び出ししてることに起因するのではなく、使っている Win32 API 側の動作に起因しており、改善するには呼ぶものを変えるとか呼び方を変えるとかが必要とわかった。どうしたら改善できるかはわかっていないけど。