「サーチ・インサイド・ユアセルフ」感想:マインドフルネス/瞑想で生活が変わるかも

こちらの本を読んで、結構大きなインパクトがあったので、感想をブログにまとめることにしました。

きっかけ

先日参加した Regional Scrum Gathering Tokyo 2023 で聞いた伊藤さん・熊谷さんの講演で本を知ったのがきっかけ。

speakerdeck.com

セッションとしては、社内の噛み合わない議論と対立(空中線)を解決するために「アンガーマネジメント」「NVC (NonViolent Communication)」「マインドフルネス」の3つが役立つ、という話。 まずこれらのうち、自分はマインドフルネスが一番イメージができないものだった。 また、1年半ほど前から会社での役割が Individual Contributor からマネジメントに変化していた。つまり仕事の主な対象がコードから人・チームに移り、問題が長期戦になったり思い通りにならないことが増えた。結果、業務時間の内外問わず心が落ち着かないと感じていた。 その軽減につながるかも、という所からも知ってみたいと思った。 そこで、講演でも紹介されていた本を手に取った。

本の内容ざっとまとめ

  • タイトルの「サーチ・インサイド・ユアセルフ」はGoogle社内のマインドフルネス研修の名前。著者はその創始者の元エンジニア
  • 1章でまずマインドフルネスとは何か、を紹介
  • 以降の章では、マインドフルネスがどんなメリットをもたらすかを、具体的なエクササイズの方法を織り交ぜつつ紹介していく
    • メリットの例:心を鎮められるようにする、集中力を増す、創造性を増す、自信を増す、共感力を増す、愛されるようになる
    • エクササイズは内面に集中する瞑想が中心だが、会話やジャーナリングのように出力を伴うものもある

本を読んでの一番の気づき

ジョン・カバット・ジンはマインドフルネスを、「特別な形、つまり意図的に、今の瞬間に、評価や判断とは無縁の形で注意を払うこと」と定義する。(p.51)

マインドフルネスや瞑想で大事なことは注意力*1、という点が驚きだった。 というのも、自分は瞑想について、なんとなく「心を空っぽにする」「何にも意識がとらわれない無の状態」といった注意力をゼロにするようなイメージを持っていた*2。 しかしここがむしろ真逆で、マインドフルネスでは注意力は無くすのではなく鍛えていくものだった。 ただし判断や評価、反応や行動を廃した純粋な注意を、という理解で合っていると思う。

瞑想は呼吸に注意することで注意力を高める訓練、ということだった。 では注意力を高めてどうするのかというと、注意力を自分の情動や体の状態に向けていく。 これはアンガーマネジメントでも言われることだと思うが、怒りなどの衝動的な心の動きは 刺激 → 情動 → 行動 という順序で起きる。 評価抜きで情動に注意することで、その後の行動(これが怒りやストレス)を抑えていくことができる*3

これは考えてみると納得感がある。 本書でも述べられているが、仮に瞑想で心を空っぽにすることでストレスを軽減できるとしても、再現性がなくて、ストレスイベントが起きる → ストレスを感じる → 瞑想でストレスを減らす → また別のストレスイベントが起こる → ...、ということが繰り返されるだけで本質的に解決しない。 自分の情動から逃げず、理解しうまく付き合っていくことで、同じ出来事があっても動じにくくなる、というのはわかりやすいと思った。

全般、著者がエンジニア出身ということもあって表現がロジカルな傾向があり(著者も自分でそう書いていた)、自分には安心して読める印象だった。

本を読んでから実践してみたこと

マインドフルネスのメリットは複数挙げられていたが、自分としては特に次の2つが得たいと思った。

  1. 意のままに心を鎮める:仕事や生活の時間をより良い状態で過ごしたい
  2. 集中力:特に読書のときなど、気が散りやすいので改善したい

この2つが得られれば、大げさかもしれないが人生にも影響があると思う。 まだ本を読んで1週間しか経っていないが、これまでにやったことと、そこから得られた良い感触についてこの記事を終わりにしたい。

まずは、本に書いてある瞑想の練習をできるだけ毎日やっている。 呼吸に純粋な注意力を向けるよう練習するのだが、やってみると最初は自然な呼吸ができず、自然な呼吸になるよう意識してペースや呼吸量を調整したりしていた。 ここは、目的(注意力を鍛える)を理解した上で続ければ、徐々に上手くできるようになりそうな感覚はあるので、続けていきたい。

また本を読んだ直後に、生活の中、具体的には街中を一人で歩いているとき*4に自分が何を感じているか、にじっと注意してみた。 すると、人とぶつかりそうになったとき、あまり好きでない広告を目にしたとき、冷たい風が吹いてきたときなど、かなり頻繁に、ネガティブな心の反応が生まれていることに気づいた。 ここでちょっと冷静になると、冷たい風が吹いても、すぐ「あー寒い嫌だ」と思わなくても良い、と思えたのが面白かった*5。 冷たいし身体は震えるが、ダウンジャケットも着ているし危険を感じるようなレベルではない、と思い直すと、「嫌だ」という気持ち・反応をやりすごすことができた。 1.については、こういった気持ちのコントロールを色々な場面でできるようにしていくと良いのだと思う。

2.については、自分が読書中にどういうときに気が散るのかに注意し書き出してみた*6。 すると無限にあるわけでもなく、パターンがあった*7。 これを理解することで、気が散ったタイミングに気づきやすくなり、そこから再度本の内容に集中を戻したり、あるいは今は集中できないから休憩しよう、と判断したりしやすくなった。 正直な所、ここ数年、本を読んだが内容がほぼ頭に入っていない、ということが多く、自分の読書スキルに自信をなくし、嫌になりつつあった。 しかし集中できる見通しが見えてきたことで、「集中して読書をする」というチャレンジを込みで、改めて読書を楽しめそうと思えている。 さっそくTrelloを使って積ん読本を読む計画を立ててみると、半年ほどかかることがわかった。 ゲーム感覚で消化していこうと思っている。

まとめ

元エンジニアが書いたマインドフルネス・瞑想の本を読んだら、「注意力が大事」というこれまでと真逆の理解が得られ、

  • ストレスが軽減できるかも
  • 読書を集中力の訓練と捉えて楽しめそう

という感触が得られた、という話でした。

*1:後から見たNetflixの「ヘッドスペースの瞑想ガイド」という番組でも同じことを言っていた

*2:チームのメンバーと雑談しても同じようなイメージを持っていると言っていた。同様の人はそれなりにいるのかもしれない

*3:まだ自分は実践できてはいないが、こう理解はしている

*4:本当は仕事中にそうしたいがまだ余裕がないので

*5:心頭滅却すれば火もまた涼し、という言葉も思い浮かんだ

*6:認知の動きを認知するので「メタ認知」という

*7:書き出したものをそのまま書くと:姿勢、ゲーム、明日の予定、気温、痒さ、飲み物、読書法、「これ読んで意味ある?」という疑念・筋トレ・掃除・本の内容に関係あるエピソード・「この章はあと何ページ?」と思う

「Google ITサポートプロフェッショナル認定」でネットワーク・セキュリティの基礎を学びなおした

2018年頃に公開されたGoogle ITサポートプロフェッショナル認定 (Google IT Support Professional Certificate) の中から、

の2つのコースを受講・完走しました。 仕事が終わった後の夜に進めましたが、合計してだいたい15日くらい(疲れてサボった日を含む)でさくっと終われてよかったです。

目的

前提として、分野のエキスパートになることが目的ではありませんでした。自分は出身は理系ですが、コンピュータサイエンスをこれまで大学などで体系的に学んだことがなく、一般的にこれくらいは知っておくべきだが、自分が今知らないことがあるように日々の業務で感じていたため、そこを埋めるために受講しました。その目的にとっては良い選択だったのではと思います。

内容

内容は基礎的で、既に知っていることも結構含まれていました。しかし、聞いたことや見たことはあるが理解が曖昧だった事柄、例えばネットワークであればレイヤモデルの各層でのパケットのヘッダ概要やTCPの3-way handshake、セキュリティならPKI (Public Key Infrastructure)などを改めて背景から理解できました。動画コンテンツには必要に応じてわかりやすい図が登場するので、多少仕事で疲れていても理解が進みやすかったのは良かったです*1。ただ、3年前に公開された講座ということもあり、一部内容は古くなっています(紹介されているサービスが既にクローズしてたり)。

同じ分野の書籍に比べると内容はかなり浅めで、取捨選択されていると思います("Supplemental Readings"として参考資料を挙げて「詳しくはこれ読んでね」というパターンも結構ある)。そのため、重要だが学習内容に含まれない事項も多分にありそうで、そこは今後都度学んでいく必要があると思っています。取捨選択の仕方は、Google監修だし偏ってはいないだろうと今回は盲目的に信じることにしました🙃。

自分は大体、各動画を2回ずつ見て学習してました。1回目で概要を掴んだあと、2回目にノートにポイントを書き写す、という感じです。リアル講義ではありえないことですが、動画がストップできるのが非常に便利でした。このへん、英語がもっと得意な人はもっとスムーズに学習を進められるのかもしれません。

効果

受講する前よりも、自分の中に大まかな分野の地図ができた感覚を持てています。例えば、以前から持っていたマスタリングTCP/IP徳丸本を読む障壁を低く感じるようになれて、「あー、ここは動画で見たあの話を詳しく書いてあるのね」と思えるようになりました。

補足など

動画・文章とも英語で、日本語字幕はありません。 ただ、発音は明瞭だし、英語字幕もあるので、ある程度英語ができるが、そこまでListeningに自信ない人(∋ 自分)は、その面でも勉強になるかもしれません*2

講座はCourseraという有名なオンライン学習サービスの一つでホストされています。 課金しなくても学習コンテンツ自体は見られます(修了証はもらえません)。このあたりの詳細は、以下の記事が参考になると思います:

www.kimoton.com

あと、講座全体がGoogleのITサポート職の採用が目的でもあるため、短い社員インタビューや模擬面接の動画が時々挟まってきます。自由に飛ばせますが、自分は面白いので一応全部見ていました。

むすび

基礎的な内容を時間取って勉強するのは久しぶりだったため、簡単ですが書いてみました。学び直しは今後もずっと続けていく必要がありますね。以上です!

*1:ネットワークは全般良い感じだったのですが、セキュリティの後半の方で、項目の羅列のような箇所が一部あると感じました。製作者も疲れてきたのかな?

*2:逆に言えば、とにかく内容に集中したい場合はイマイチかも、とも言える

useEffect()の第2引数の差分検出はdeepではない

内容はタイトルで尽きてます。 気になって調べたのでメモを残しておきます。

背景

  • React HooksのuseEffect()第2引数に渡す配列は、「この副作用が依存する値」を表す。
    • つまり、指定した配列に含まれる値が変化したときのみ、第1引数の関数が実行されるようにできる
  • では、変化の検知はどういう方式なのか?
    • 具体的には、配列やオブジェクトを渡した場合にdeepな比較をしてくれるのか?

結論

  • Deepな比較はしてくれない。
    • 値が全く同じでも、別オブジェクトなら変更検知される。逆もまた然り。
  • とはいえ、通常はprops, stateくらいしか指定しないだろうから、同じオブジェクトだけど値が変わった、みたいな状況はほぼ気にしなくて良さそうではある

関連記事

実験

useEffect()の第2引数に

  1. 数値をプロパティに持つオブジェクト
  2. 数値を要素に持つ配列
  3. プリミティブ数値

を渡し、各数値をインクリメントしつつ1秒おきにrenderする。 直接innerTextを書き換えるような副作用を仕掛けておき、差分が検出されるか調べた。

実際、オブジェクト・配列については中身の値が変わっていても副作用が実行されなかった。

コード確認

  • React v16.13.1 を読んだ
  • Reactのコードをちゃんと追うのは時間がかかりそうなので断念した
    • 当たりを付けた上で、DevToolsでデバッグして実際に該当コードが実行されていることを確認した

useEffect()の実装

  • React内部では、ReactCurrentDispatcher.currentというグローバル変数を状況に応じて差し替えながら処理しているらしい
  • それを元に追いかけると、useEffect()の実体がupdateEffectImpl()という関数であることがわかる
  • 差分検知はareHookInputsEqual()がしている
    • 第2引数(配列)の各要素のbefore/afterの値をis()関数で比較している
      • このへん
      • is()関数はObject.is()Polyfill、ざっくり言うと===による同一性比較だが、+0-0は異なるがNumber.NaNNaNは等しいと判定する。
    • 各要素を再帰的に見るような処理は存在しないことから、deepな比較はしないことがわかる

RSGT2020初参加レポート

先週、RSGT2020ことRegional Scrum Gathering Tokyo 2020に初めて参加してきました。*1

既に多くの方がレビューなど投稿されていますが、今後のためにも、自分なりの所感などをまとめておきます。

いまの自分の立場/ロール

大阪で、kintoneというBtoBプロダクトの新機能開発をしています。

チームはスクラム開発を取り入れており、自分は開発チームの一員です。 CSM認定を取っていますが、スクラムマスター専任ではなく、スクラムに関して多少詳しい立場から気になったときに可能な範囲でアドバイスをする、といった立場です。

あとチームの特徴としては、マルチサイト(5拠点+在宅)で、常時リモートでモブプログラミングをしています。

RSGT2020・総評

参加してよかった!

たくさんのインプットや勇気づけをもらえました。特に「今年どんな活動をしていこうかな?」とぼんやり考えていたのが、講演や対話を通してより明確化できました*2

一方、仕事でスクラムをやっていく上で、自身の現状と今後のロールについて再考する機会ともなりました。一言で言うと、「なんか中途半端だけどどうしよう?」という漠然とした危機感が沸いています。

いまの自分のロールは開発チームの一員、スクラムマスターではない。リソースも開発チームとしてほぼフルコミットしている。アジャイル/スクラムに興味はあるし、チームの改善などを主体的に考えているつもりではあるが、リソースは明らかに割けていないので出来ていないことも多い*3アジャイルコミュニティにもどっぷり浸かれてなくてもったいない感じというか。ここはまだ結論出ていないので、継続して考えていく。

また、スクラム/アジャイルのマインドを改めてアップデートできました。セッションや会話を通じて、「こういう時はこうすれば良いんだよな」といった考えの確信が強まった感じ。

セッション別の所感など

Day 1

Keynote: The Ten Bulls of the Scrum Patterns / James Coplien

Coplien (Cope) さんは、自分が受けた認定スクラムマスター研修の講師で、親近感がありました。

内容は、十牛図 (The Ten Bulls) という、禅宗の教えを広めるためのポンチ絵になぞらえて、スクラムの習得の道を説くというもの(最近出版されたA Scrum Bookという、スクラムのパタン・ランゲージ本の冒頭と同じ)。

いわゆる「守破離」を別の切り口で表現したように感じた。 スクラム(=牛)を習得しようと、まずはその姿(=スクラムガイドにあるルール、カタ)を追い求めるが、最終的にはチームの全体性 (Wholeness)、自分たちがどうあるべきか、といった無名の質 (Nameless Quality) に従う自然な営みになっていく。結局「スクラムとはこうやるものだよ」という具体的な姿かたちは無い、という話。

学びとしては、「スクラムの形式にとらわれてはいけない。自分がどうあるべきかを常に考え、それに従い続けることが重要」ということ。これはスクラムパターンの一つ The Spirit of the Game と同様だと思う。 よく考えてみれば当たり前のことなのですが、Copeさんが言うとなぜか印象に残るのが面白い。 改めて意識しようと思いました。

あとは講演を聴きながら、「自分やチームはいま、スクラム習得の過程のどのへんにあるだろうか?」と考えていました。 そういえば最近、「スクラムだからどうこう」という話をあまりしていないな、とは思う。でも、チームの全体性や無名の質みたいな悟り(?)に到達できているかといえば自信はない。

これはスクラムパターンを参照して、自分のチームの現状を確認する良いタイミングでは、と思ってA Scrum Bookを購入しまいました。いやはや思うツボですね。

コーチズクリニック(及部さん @TAKAKING22)

1on1でアジャイルコーチに相談できるとても贅沢なコーナー。

スクラムで重要とされるチームの全体性と、個人の評価の兼ね合いをどうつければ良いか?」という問いを、及部さん (@TAKAKING22) にぶつけさせてもらいました。 最近読んでいたモブプロ本(下記)で解説書かれていたり、去年のScrum Fest Osaka 2019のキーノートで知っていたということで指名させてもらいました。

半分スクラムと関係ない問いにも真摯に向き合っていただきました。 理想は、個人ではなく、チームとしての成果が評価される状態、ということに尽きる(例えば、プロダクトの売上が上がったら全員一律昇給)。 極端な例として、及部さんのチームは、チームごと楽天からデンソーに転職した*4けど、採用自体チーム単位での話だったので、評価についてもよりよい状態になったとのこと。 ここは会社に持ち帰って、何かの形で話題提起してみようと思いました。

あとは、チームの外部発信についてもエンカレッジしていただきました。やっていくぞ!

伊藤 宏幸・高橋 勲:特殊部隊SETチームの日常 - 技術と実験を融合した実践アジャイル術 -

https://confengine.com/regional-scrum-gathering-tokyo-2020/proposal/11774/set-

LINEのSoftware Engineer in Testチームの、プロダクト横断チームならではの実践例。

オンボーディングを促進するためのLearning Sessionはうちのチームでもやっていますが、 Learning Sessionにマネジャー的役割の人が入ることで、業務が説明しやすく人事評価にもプラスの影響があった、というのは経験したことなくて、なるほど、という感じでした。

Day 2

Keynote: Lost in Translation: The Manager’s Role in Agile / Michael Sahota

アジャイルスクラムの文脈で、マネージャーはどんな役割を担っていくべきか、という話。 Day 1のCopeさんのKeynoteが理想主義ならば、SahotaさんのKeynoteは徹底して現実主義で、その対比が非常に印象的でした。

組織階層・マネージャー不要論はよく言われるが、いきなり実践するのは危険な罠で失敗につながる*5。 また、自律的で自己組織的なチーム (Autonomous, Self-Organizing Team) も理想とされるが、いきなりは難しいと。

ではどのように個々のチームメンバーを変えていけばよいかというと、そこでいわゆるマネージャーが活躍できる。 マネージャーがマインドセットを変えることで、組織全体が変わる可能性が高まるのだ、と。 ここではXY理論が引用されていた(初めて知った)。

最後に、本題である「アジャイルにおけるマネージャーの役割とは?」の答え。 理想は、マネージャーも何かの形で、価値創造に明示的に関わる役割を持つこと。 具体的には、Product Owner, Scrum Master/Coach, Tech Lead/Architect, Organizational Coach といった役割につく。 しかしそれも一足飛びには難しいので、マネージャー/リーダーのマインドセットの変化から始めるべき。そのためにはトレーニングなどがあると言って結ばれた。

自分にとっては、内容的にも役割的*6にも、「なるほど明日からこういう風にしてみよう!」と思えるものではありませんでした。 しかし色々と考えさせられる部分は多かったし、実際に組織を変えていく過程は、理想の姿にいきなり改革するよりも、少しずつ変わっていくケースも多いんだろうな、とか思いました。

組織変更して部長がいなくなってから起きたこと / 水戸将弥(サイボウズ

speakerdeck.com

弊社マネージャーの水戸さんのお話。内容盛りだくさん。 自分は中の人なので知っている話がほとんどだったけど、組織運営チームはこんなこと考えていたんだ、という気づきもありました。 組織を変えてからプロダクト/チーム間の人の流動性は本当に高まったと思うし、興味持つ方も多いんじゃないかな、と思います。

最高のScrumキメた後にスケールさせようとして混乱した(してる)話 / 藤村新(クラスメソッド)

www.slideshare.net

現場でスクラムをスケールさせて失敗した話。 内容も具体的な現場の話でもちろん面白いけど、それよりも何よりも、プレゼンの魅せ方が本当にすごかったです。 最初から最後までニコニコして聴かせていただきました。

Day 3

Closing Keynote: NEXT→ACTION / 高橋 一貴(株式会社チカク)

RSGT2011の実行委員長・高橋さんのお話。 資料は非公開とのことですが、noteに講演についてのポストがありました。

主な内容は、2010〜2015年頃のヤフージャパンでの活動のお話。 地道かつ大胆に組織を動かしていく話に心を動かされました。 自分がエンジニアを始めたのが2015年、CSMを取ったのが2018年なので、それよりも全然過去の話なわけで、 ヤフーの強さというか先進性も実感しました。

むすび

他にも講演を聞いたり、飲み会で喋ったり、Open Space Technologyに参加したり、色んなことがありましたが、一旦以上にします。

会場を見渡すと、経験豊富なアジャイルコーチがうようよいる中で、スクラムマスターですらない自分がいていいのか?と思いもしましたが、今年一年頑張ってまた来年も参加できたらと思います。

最後になりましたが、運営の皆様、お疲れさまでした!

*1:2019年にも参加したかったけどチケット争奪戦を甘く見ていたため敗退、今年こそはと発売1分で購入してぶじ参加できました。

*2:具体的には、自分/チーム両方のために、発信を増やしていきたい

*3:チーム外の問題へのアタックとか

*4:及部さんのDay 2の講演のテーマ

*5:ティール組織の成功例でも階層はあるし、アジャイル実践においてマネジメントのサポートが無いことがよく問題となる、という調査もある

*6:自分はマネージャーではない

ElectronのIPCをWeb APIっぽく使えるようなTypeScript実装の試み

背景

  • ElectronアプリをTypeScriptを使って書いている
  • やりたいこと:(1) IPC (Inter-Process Communication)をWeb APIっぽく使えるようにしたい、(2) 同時に、型情報を活かしつつ綺麗に実装したい
    • 一般的なWebアプリをイメージしている。Rendererプロセスがブラウザ、Mainプロセスがサーバーという対応
    • 色んな処理(CRUDとか)をRenderer側からリクエストして、非同期でレスポンスが返ってきたらRenderer側にメッセージ表示などしたい
      • 処理の種類に対応する複数の種類の"API"を実装(例:AddUserGetUserInfo、etc.)
    • IPCのChannelは1つ開きっぱなしにして、全ての"API"で共有する
      • "API"ごとにChannelを分けると、管理が煩雑になったため
+----------+                +----------+
|          |--- Request --->|          |
| Renderer |                |   Main   |
|          |<-- Response ---|          |
+----------+                +----------+

こんな風にしてみた

  • もっと良い実装があるかもしれないけど、現時点ではこれが良いのでは、と思っている方針
  • 一言で言えば、リクエスト/レスポンスの型定義をうまく書いてGenericsIndex typesの機能を活用することで、型情報の活用とボイラープレートの最小化を実現している、ということに尽きます。

"API"の種類

"API"名のunionとして定義

type IpcType = 'AddUser' | 'GetUserInfo' | ...

リクエスト・レスポンスの型

それぞれ別々のinterfaceとして実装する。 各"API"名がプロパティで、値がリクエスト/レスポンスの型になっているという形式. この形式にしている理由はすぐ次の節へ。

interface IpcRequest {
  AddUser: {
    name: string;
    age: number;
    ...
  };
  GetUserInfo: {
    id: number;
  };
  ...
}

interface IpcResponse {
  AddUser: number; // id
  GetUserInfo: {
    name: string;
    age: number;
    ...
  };
  ...
}

"API"のリクエスト側(≒クライアント)

ここが主に試行錯誤したところ。 "API"ごとにメソッドを生やさないようにしたかったけど、最初Genericsがうまく扱えなかった。

最終的に、IpcRequest[T]IpcResponse[T]と書くことで、"API"の種類に応じたリクエスト・レスポンスの型を引っ張ってこれることに気づいた。

class IpcClient {
  private readonly map: ResponseHandlerMap;
  private isOpen: boolean;

  constructor() {
    this.isOpen = false;
    this.map = new ResponseHandlerMap();
  }

  // どの"API"もこの共通メソッドでリクエストできる
  // GenericsとIndex Typesを使って工夫している
  public send<T extends IpcType>(
    type: T,
    req: IpcRequest[T]
  ): Promise<IpcResponse[T]> {
    return new Promise(resolve => {
      const id = uuidv4(); // ユニークなIDを付与しておき、どのリクエストに対するレスポンスなのか区別できるようにする
      this.map.set(id, type, (res: IpcResponse[T]) => resolve(res));
      ipcRenderer.send('MAIN_CHANNEL', type, req, id);
    });
  }

  // IPC Channelを開くために一度だけ叩いておく必要があるメソッド
  // ここは型が付いていない
  public open() {
    ipcRenderer.on(
      'MAIN_CHANNEL',
      (event: any, res: any, err: any, id: string) => {
        const { type, handler } = this.map.get(id);
        handler(res);
        this.map.delete(id);
      }
    );
    this.isOpen = true;
  }

  public close() {
    ipcRenderer.removeAllListeners('MAIN_CHANNEL');
    this.isOpen = false;
  }
}

// Mainプロセスから返ってきたレスポンスのハンドラ関数の型定義
type ResponseHandler<T extends IpcType> = (res: IpcResponse[T]) => void;

// レスポンスハンドラをidごとに保持しておくMap。
// JSのMapの薄いラッパで、こいつの中身は型をちゃんと扱っていない。
// IpcClientはできるだけ型のある世界にしたかったので、汚いものをこいつに押し付けている形。
class ResponseHandlerMap {
  private readonly map: Map<string, { type: IpcType; handler: (req: any) => void }>;

  constructor() {
    this.map = new Map();
  }

  public set<T extends IpcType>(
    id: string,
    type: T,
    handler: ResponseHandler<T>
  ) {
    this.map.set(id, { type, handler });
  }
  public get<T extends IpcType>(
    id: string
  ): { type: T; handler: ResponseHandler<T> } | undefined {
    const got = this.map.get(id);
    if (!got) {
      return undefined;
    }
    return { type: got.type as T, handler: got.handler };
  }

  public delete(id: string) {
    this.map.delete(id);
  }

  public get size() {
    return this.map.size;
  }
}

// 全般、エラーハンドリング系は煩雑なので省略しました

"サーバー"(Mainプロセス)側

工夫した点や実装はリクエスト側と同様なので読み飛ばしてもよいです。

// なんとなくclassとして書いている、classにする必要性は特にない。
// IpcController.initialize()を一度呼べばOK
class IpcController {
  public static initialize() {
    const handlerMap = new RequestHandlerMap();

    ipcMain.on(
      'MAIN_CHANNEL',
      async <T extends IpcType>(
        event: any,
        type: T,
        req: IpcRequest[T],
        id: string
      ) => {
        try {
          const handler = handlerMap.get(type);
          const res = await handler(req);
          event.sender.send('MAIN_CHANNEL', res, null, id);
        } catch (error) {
          event.sender.send('MAIN_CHANNEL', null, error.message, id);
        }
      }
    );

    // ここからずらずらと"API"の実処理を書き並べる
    // reqにはちゃんとIpcRequestの定義に基づいた型情報が付く
    handlerMap.set('AddUser', async req => { ... });
    handlerMap.set('GetUserInfo', async req => { ... });
    ...
}

// "API"のコントローラ関数の型定義にあたる。
// ResponseHandlerと同様
export type RequestHandler<T extends IpcType> = (
  req: IpcRequest[T]
) => Promise<IpcResponse[T]>;

// ResponseHandlerMapと同様、この中は型が付いてない
class RequestHandlerMap {
  private map: Map<IpcType, (req: any) => Promise<any>>;

  constructor() {
    this.map = new Map();
  }

  public set<T extends IpcType>(type: T, handler: RequestHandler<T>) {
    this.map.set(type, handler);
  }

  public get<T extends IpcType>(type: T): RequestHandler<T> | undefined {
    const got = this.map.get(type);
    if (!got) {
      return undefined;
    }
    return got as RequestHandler<T>;
  }
}

"API"を叩くには

Rendererのコードで以下のように書くだけ。型も勝手についてくるし、シンプルに書ける。

ipcClient.send('GetUserInfo', id)
  .then(userInfo => {
    // データを使った処理を書く。
    // userInfoには型が付いている
  });

"API"を追加するには

以下の通り。本質的なことを書くだけで済む。

  • 新しい"API"名をIpcTypeに追加
  • IpcRequestにリクエストの型情報を追加
  • IpcResponseにレスポンスの型情報を追加
  • IpcControllerの中に"API"の実処理を追加

考察

満足している点

  • API追加時にボイラープレートコードを書かなくて済む
  • 各種関数にちゃんとリクエスト・レスポンスの型情報が乗ってくれるので安心

不満/疑問が残る点

  • IpcRequestIpcResponseの定義が別々なので、同じ"API"の情報がコード上並ばないので可読性がよくない
  • 内部的には型無しで扱っている箇所があり、それは良いのかどうか?
  • Channelを1本開きっぱなしで使い回す、というのは良いのか?

その他

そもそもElectronのIPCをAPIっぽく扱いたい、というような需要はあるのか(あるいは筋が良いのか)どうか、よくわかっていません。

↓のようなレポジトリは見つかって、結構モチベーションは近そうなのですが、特に流行ってはいない様子。 github.com

APIっぽい、という話ではないですが、IPCをPromise的にシンプルに扱いたいというライブラリもあるようです↓ github.com github.com

結び

長々とお付き合いありがとうございました。 自分はElectron・TypeScriptのコードリーディングをあまりしてきていないので、典型的なオレオレ実装になっているのでは、という不安もありつつ書いてみました。 より良い実装が見つかれば、追記ないし別エントリを書こうと思います。

2019年ふりかえり

2020年明けましておめでとうございます。

まず、この投稿は個人的な内容になると思います。読んでいただけたら嬉しいですし、叱咤激励は大歓迎です。

自分は2015年、30歳のときにWebエンジニアを始めて今に至ります。 今年は、4月でWebエンジニア歴満5年、また年男であるなど、個人的に色々と節目の年だと思っています。 そこで、年を越してしまいましたが、いったん駆け足で2019年を振り返っておこうと思います(少なくとも今年の年末に自分が読み返せるように)

※「開発チーム全拠点全員集合会を企画・開催(9月)」について追記しました (2020-01-02)

2019年総評

色々とやってみた年。目標は達成できなかった年。

目標?

2019年当初は

  • 担当プロダクト(kintone)への貢献を3倍にし、2020年の給与が○○円(額は非公開)以上になる
  • 自分の強みを自信を持ってアピールできるようになる

という目標を置いていた。 2つ目は定性的だけど、ともあれ結果はどちらも未達成だった。

やってみたこと・イベント

ビジネスに近づく(通年)

これまで、基本的に「どうやって作るか(How)」にかなり注力してきた*1けど、そこを変えてみようと思った。「何を作るべきか(What)」、あるいは作ったものがどう売られていくか、といった所に視野を広げることで、エンジニアとしてステップアップできるのではないかと考えたため。*2

そのために、

  • プロダクトマネージャーや販売メンバーの定例に参加
  • 開発以外の本部のキックオフミーティングの見学
  • 営業メンバーによる導入相談に同席
  • 営業メンバーとランチ
  • 開発以外の社外イベントの見学やブース対応

など、普段の業務の延長線上からできることを探してやってみることにした。 結果、社内メンバーからは実際に受注・失注につながった要因などの生の話を聞けたし、社外の方からは、開発視点では負債に感じるような機能がズバッと要望に刺さる営業トークを見たり、逆に出来て当然なことができないプロダクトの弱みを突きつけられたりした(どちらも、普通に開発していては気づけなかったことだと思う)。

活動を通して得られたものとしては、一言で言えばプロダクトマネジメント視点だと思う。 うちのチームではスクラムを採用しているので、Product Ownerにあたる人(プロダクトマネージャー)がProduct Backlog Itemの優先順位を付けている。その際、普段どのような思考(工数やビジネスサイド、もろもろの調整を含む)をしているのか1年前よりもクリアになり、お互いに会話しやすくなった。

一方で、エンジニア(スクラムで言えばDevelopment Team)の一員としての限界も感じることになった。 具体的には、プロダクトマネージャーの視点に近づいたとはいえ、自分が新たに何かを生み出すという成果にはつなげられなかった(もちろんチーム開発を円滑に進めることには貢献している自信はある。また事実として、2019年のkintoneはこれまでよりも魅力的なアップデートを多くリリースできた。しかし、それを自分の成果というにはちょっと弱い感じがしている)。

自分が携わっているkintoneという製品はおそらく*3とても考慮すべきことが多く、少なくとも自分には、開発の片手間で良い機能などの提案はできなかった。 そもそもその意味で、Whatを考える人(プロダクトマネージャー)、Howを考える人(自分含む開発チーム)が分業する体制になっているわけだが、そこを越境して何かを生み出すことまでは、残念ながらできなかったと思う。

悲観的な書き方をしてしまったけど、考えてみると実際に投資した時間としては大したことない(週に2〜3時間程度)ので、単に覚悟の問題もある。

副業でデスクトップアプリを個人開発(通年)

とあるチーム内で使うためのデータ管理ソフトウェアを開発している。性質上公開できないけど、Windows向けデスクトップアプリ。 知り合いのつてで仕事を頂いていて、感謝。

得られたものは色々。

時間的には、本業の時間は減らさず、+αする形で1日平均1〜2時間ほど投資している。 ここで少し話が逸れるが、時間に関してはちょっとした危機感がある。 振り返ってみると、自分の時間はおおよそ

  • 平日
    • 日中:会社
    • 夜間:副業・娯楽・自己研鑽
  • 休日
    • 日中:家族・育児
    • 夜間:副業・娯楽・自己研鑽

このような構成になっている。 危機感は、自己研鑽に投資できてない(末尾に書いたのは、正直言って一番配分が少ないから)。 技術に触れていないわけではないが、副業の目の前の仕事(いわゆる緊急かつ重要な仕事)の実装・調査が主で、幅が広がりづらい。 娯楽を削れていないのは覚悟の問題。ここは2020年の課題だと思う。

同じオフィス(大阪)の同僚が2人増えた(6月・9月)

自分の勤務する大阪オフィスに、同じチームのメンバーが相次いで2人中途入社してくれた。 うちのチームは常時TV会議でモブプロをやっており、拠点間の壁は極力下げているものの、やはり拠点内のつながりは強い。 ということで、2人の入社直後は、同じオフィスの自分がメインでオンボーディングを担当したりしていた。

以前はチームリーダー的な肩書だったし、オンボーディング担当する上でさすがに不安は無かったものの、「何であんなこと言っちゃったかなー」とか、いつも反省したり悩みながら毎日モブプロ作業している感じ。 拙いオンボーディングながら、お二人とも順調に戦力になってくれていて嬉しいし、自分も精進せねばという気持ちにさせられる。

開発チーム全拠点全員集合会を企画・開催(9月)

自分自身の知識を増やす勉強に投資できていない分、せめて開発チームにとっての良い「場を作る」ことは意識するようにしていた。

その一番大きな結果として、東京・松山・大阪・広島からほぼ全メンバーが一同に介して、チームの問題を議論したり懇親したりする社内イベント(メンバーからは"Gathering Day"と呼ばれている)を企画・開催した。 主にやったのは取りまとめ的な役割と当日の司会業。 終わった後、メンバーから「良い会だった」と言ってもらえたのがすごく嬉しかった。

背景としては、うちの開発チームは40名(コードを書くエンジニアだけでなく、テストエンジニアやドキュメントのライターなども含む)を超える大所帯になっていて、チーム全体としての課題について議論する機会が減っていると感じていたこと。 また、拠点が分散しており、特に、東京とそれ以外の拠点とでは、人数など差が大きい*5。 後にも少し書くように、TV会議完備の環境なので理論上はコミュニケーション的にも問題ないはずなのだけど、どこか心理的な壁はできてくる(個人的にはこれは仕方ないと感じている。スクラムでもよくリモートは避けろと言われる)。 そこで、開発チーム全体としての改善を進めていける一助とすべく、物理的に集まる会を企画することにした。

社内の色んな方に助けてもらって実現したイベントだった。 アイスブレイクはチームワーク総研という部署のメンバーにお願いしたし、問題の議論はOpen Space TechnologyないしLean Coffeeという形式で進めたが、このあたりはアジャイルコーチチームにアドバイスをいただいた。 自分が大阪にいることもあり、チーム内でも色々助けてもらった。他部署につないでもらったり、懇親会準備を快く引き受けていただいたり。

成果として、大小色々なテーマについて議論でき、多くの人に満足してもらえた(アンケートを取った)ので、合格点といえる。 一方、実際に改善してプロダクトや開発プロセスが良くなったかというと、そこまで自信を持っては言えない。 取り組みを継続していったり、やり方を工夫する余地はありそう。

アメリカ出張(4月・10月)

4月に会社のUS拠点、10月に某社の訪問へ行った。

  • ここで具体的に書いて良いかちょっと微妙なのでボカすけれど(すみません)、10月の出張では、普段のリモートモブプロ開発作業での改善ヒントをもらって実践中
  • 英語である程度のコミュニケーションはできることが確認でき安心した。レアジョブで準備した話は別エントリに書いた

ブログを書いてみようと思った(11月)

きっかけは給与評価面談。

弊社では、市場価値が評価の一つの軸になっている。 平たく言えば、自分が転職したときの給与額くらいの評価になる。 「あなたの長所、強みは?」「どんな実績がありますか?」「どう貢献したい/できますか?」といった質問に明確に答えられる必要があるわけで、ありがたいことに同僚に壁打ちしてもらう機会があり、そこで全然答えられないという散々な結果になり割と凹む。

それ以来、「自分は何ができるのか?」を考えて悶々とし続けている感じ。 一つ確かなことは、少なくとも今の自分の場合、普段の開発作業を積み重ねても「これができます!」という良いアピールには結晶化できず、そこで悩んでいるということ。これを更にブレイクダウンすれば

  1. 実際にはできることは色々あるはずだが、それをうまく言語化する能力がない
  2. 普段の開発業務で得られる知識・経験の市場価値が低い

のどちらか、あるいは両方が原因だろう。

2.もゼロではないけど、1.も多分にありそう。1.が改善する方がポジティブに思える、ということで、1.の改善を試みているのが今。まずは、自分がエンジニアとして何ができるのかの証左としても言語化・発信力は必要だろうということで、ブログを書いてみようということにした。

もともと書くことは(上手ではないが)嫌いでもないので、いっちょやってみるかという気になった。あと、もし読んでもらえたら単純に嬉しいし。↓の本にも影響を受けている。

SOFT SKILLS ソフトウェア開発者の人生マニュアル

SOFT SKILLS ソフトウェア開発者の人生マニュアル

結局、人生はアウトプットで決まる 自分の価値を最大化する武器としての勉強術

結局、人生はアウトプットで決まる 自分の価値を最大化する武器としての勉強術

  • 作者:中島 聡
  • 出版社/メーカー: 実務教育出版
  • 発売日: 2018/09/22
  • メディア: 単行本(ソフトカバー)

しかし、読ませるブログ書くのって本当に難しい。 自分のコンテンツ力とか、色々考えさせられる。 考えさせられているだけでも勉強になってるので良いのだけど。

妻が復職した(5月)

数年ぶりに保育園・共働き体制に戻った。

子供2人には毎日癒しを貰いつつも、発熱時のお迎え・通院などは夫婦のうち行けるほうが行くしかなく、仕事などに一定の影響はある。

ここは自分の選択に伴う話なので、この制約とどううまく付き合っていくか、という話以上のことはない。

ゲーム

だらだらゲームしてる時間もあったので正直に書いておく。SEKIROは本当熱かったけどラスボスクリアしていない。

DARK SOULS III THE FIRE FADES EDITION - PS4

DARK SOULS III THE FIRE FADES EDITION - PS4

SEKIRO: SHADOWS DIE TWICE - PS4

SEKIRO: SHADOWS DIE TWICE - PS4

【PS4】DEATH STRANDING

【PS4】DEATH STRANDING

終わりに

2019年はおおよそ総括できたように思います。やはり言語化するの大事。2020年の活動についても考えていきます。

*1:もともとWebエンジニアになったのも、プログラミングという作業が好きで、それを職業にしたかったから。Web未経験で、開発言語のJavaもJSも知らない状態だったので、入社後はまず「作れるようになる」ための勉強で精一杯だった。さすがに4年も経てば余裕は出てくるものの、実装は楽しいしそこに目が行きがち

*2:今思えば手段と目的の結びつきがふわっとしているなと思うけど、当時は直感を信じていてそこは後悔はしていない

*3:他のプロダクトにしっかり関わったことがないので想像に過ぎない

*4:もちろん業務で得た知識・経験も生きている。Web一般とか自動テストとか

*5:半分以上のメンバーは東京に集中していたり、プロダクトマネージャーやデザイナー、ライターは東京にしかいないなど

git rebase -iとgit rebaseをちゃんと理解する

git rebase -iコマンドについてのメモです。 これまで、単に直前のnコミットをくっつけたり消したりして整理できる便利コマンドとして

$ git rebase -i HEAD~n

と叩いていましたが、なぜこれがrebaseなのかを理解していませんでした。

自分のこれまでの理解が狭かったという話で、同じような状況の方がどれだけいるのかわかりませんが、もしかすると役に立つかもしれないのでメモを書いてみます。

git rebaseで何が起こるのか

まず、git rebaseすると何が起こるかを理解しました。

そのためにちょっと腰を据えて https://git-scm.com/docs/git-rebase#_description を読んでみると、git rebase <upstream>は以下のことをすると書いてあります:

  1. 現在のブランチにはあるが、<upstream>には無いコミットが一時退避される
  2. <upstream>に移動
  3. 1.で退避したコミットを再適用

図で例を示します。 topicブランチ上でgit rebase masterすると

(Before)
          A---B---F---C topic
         /
    D---E---F---G master

(After)
                  A'--B'--C' topic
                 /
    D---E---F---G master

こんな感じで、確かに

  1. topicにはあるが、masterには無いコミット(AとBとC)が一時退避される
  2. masterに移動
  3. 1.で退避したコミットを再適用

となっています。

なお、Fはmasterにも含まれるので、1.の一時退避からは省かれます。 これは例えば、masterに入ったリファクタコミットをトピックブランチにもcherry-pickして取り込んだような場合にあたります。

git rebase -i との関係

準備ができたので、話をgit rebase -iに戻します。

簡単に言えば、git rebase -iするとエディタが立ち上がってコミットのリストが編集できますが、そこに表示されるコミットのリストが、前項の1.で一時退避されたコミット群そのものになる、ということが大事でした。

なぜこれまでこのことを意識しなかったのかな?と考えると、 git rebase -i HEAD~nのようなHEADからの相対指定の場合、rebaseする範囲にブランチの分岐がなく、Fのようなコミットがありえないため、rebase感が薄かったためかな、と思います。

例で考えてみます。master上でgit rebase -i HEAD~3すると、

    A---B---C---D master
  1. masterにはあるが、master~3(つまりA)には無いコミット(BとCとD)が一時退避される
  2. master~3に移動
  3. 1.で退避したコミットを再適用

となります。

これは、-iオプションを付けなければ、必ずBefore/Afterは同じ結果になることが想像できると思います(実際叩いてみるとわかりますが、Current branch master is up to date.などと表示されるだけです)。

-iオプションを付けることで、3.でにコミットログを改変する手順が追加される感じですね。

応用例・まとめ

以上を頭に入れれば、topicブランチ上でgit rebase -i masterと叩くことで、トピックブランチをメインブランチに追従させつつ、

  • トピックブランチのコミットをまとめたりして整理
  • 途中で入れた共通のcherry-pickやマージコミットを除去

という2つのことを一括して実行できる、とわかりました。 やむを得ずトピックブランチを長期間使わないといけないときに役に立ちそうです。

チーム開発でGitミスすると怖い、という気持ちは誰もが持つものだと思うので、このように仕組みからちゃんと理解することはやはり大事だなと思いました。