yigarashiのブログ

学んだことや考えていることを書きます

VSCodeで複数リポジトリを扱うなら結局Open Recentコマンドが最強という話

大学時代から長らくEmacsで暮らしていたのですが、最近ついにVSCodeに完全移行しました。各種設定やプラグインのエコシステムが使いやすかったり、コマンドパレットの感じがEmacsに似ていたりしていい感じです。

そんなVSCode、基本的には1フォルダ1ウィンドウの世界観なので、大量のリポジトリを相手にするときに困りがちです。何も考えずにフォルダを開いていくとウィンドウが散らかって生産性がどんどん下がります。典型的な解決策としてWorkspace機能がよく紹介されますが、以下の理由から個人的にはあまりフィットしませんでした。

  • 仕事で扱うリポジトリは巨大なものが多く、それをひとつのウィンドウにいくつも押し込めてしまうと複雑すぎて手に負えない
    • ひとつのウィンドウで複数リポジトリのファイルを開けてしまうというのも厄介で今度はタブの管理が大変になる
  • Workspaceの設定は基本的に手動管理なのでリポジトリの数に対してスケールしづらい

解決策 - Open Recentコマンド

こうした問題意識から解決策を色々探していたのですが、最終的にVSCodeにデフォルトで入っているOpen Recentコマンドが一番良いという結論に至りました。デフォルトのショートカットキーはおそらくCtrl-Rで、コマンドパレットからもFile: Open Recent...という名前で参照できます。実行すると以下のように最近開いたフォルダをインクリメンタルに検索できます。

VSCodeのOpen Recentの例

【追記】デフォルトでOpen Recentを使うと現在のウィンドウが置き換えられてしまって微妙に望みの挙動にならないので、設定からWindow: Open Folders In New Windowをonにして使っています。

あとはお好きな移動キーで開きたいフォルダのところまで移動して選択するだけです。既にウィンドウが開かれていれば一瞬で移動し、まだ開かれていなければその場でウィンドウが開きます。個人で使う場合は以下のように運用しています。

  • Macなら)VSCodeのウィンドウ用のデスクトップをつくる
  • 完全に新しいリポジトリを参照する時はリポジトリ直下でcode .としてウィンドウを開く
  • 開いたウィンドウは全て専用デスクトップ領域に放り込む
  • 一度でも開いたことのあるリポジトリはOpen Recentで移動する

このやり方は以下の点で気に入っています。

  • 1ウィンドウ1フォルダなので作業のコンテキストを区切りやすい
  • リポジトリの名前さえ覚えていれば一瞬でウィンドウを切り替えられる
  • ウィンドウがあるかないかを気にせずにリポジトリを移動することができる
  • 一度開くだけで検索対象になる上、インクリメンタルサーチなので、リポジトリの数に対してスケールしやすい
  • 各ウィンドウのターミナルが必ずリポジトリ直下をカレントディレクトリとして開く

ゴールデンウィークで開発環境整備に取り組む方も多いかと思われます。ぜひご参考ください。

『SCRUM MASTER THE BOOK』を読んでスクラムマスターを何もわかっていないことがわかった

読み始めるまでの話

社会人として働き始めて2年、Webアプリケーションエンジニアとしてコードを書きながら、プロジェクト管理やスクラム開発の運営に積極的に取り組んできました。特にここ1年は、偉大な先人が築いたスクラム開発の土台を引き継いで、実質的にスクラムマスターのような活動をしてきました。

この「実質的に」「のような」というのが最近の課題で、プロセスの改善やチーム運営の民主化、自己組織化の促進に取り組んでいるものの、自分も周囲も何を以ってスクラムマスターとするかが曖昧で、はっきりとスクラムマスターを名乗ることができていません。とはいえ、ハイパフォーマンスな開発チームを組織する上で重要な役割を果たせているという点に関しては、ある程度の合意が取れており、そうした役割を再生産していくためにも、明確に名前をつける機運が高まっていました。そこで、改めてスクラムマスターの定義を確認して議論を整理しようと考え、社内でおすすめされていた『SCRUM MASTER THE BOOK』を手に取りました。表紙には「『スクラムマスターは何をすれば良いのか』に答えてくれる本」とあり、頼もしい限りです。

学んだこと

スクラムマスターをひとことで表すと「アジャイル開発の導入とチームの自己組織化を支援するサーバントリーダー」ということになりそうです。特に本文では自己組織化の部分が重点的に書かれており、チームに気づかせ、チームに解決させるという考え方が様々な切り口で登場します。つまりスクラムマスターは、障害物を取り除いて回る秘書ではなく、チームを信じて行動を促すコーチであるということです。変化を起こし続けることが重要で、遊び心やポジティブさも重要な資質として書かれています。また、チームの成長を志し長期的な成果を求めることから、デリバリーの責任を負うべきではないとされています。

このあたりが印象に残った主張で、他にもスクラムマスターとしてうまくやっていくための様々なスキルが解説されており、どれもためになるものばかりでした。また、スクラムマスターのレベルについても解説されており、開発チームだけでなく、プロダクト開発や会社といった大きなスコープにスクラムマスターとして働きかけていく道筋も提示されています。

自分の振る舞いと考え方を改める

この本を読んでまず真っ先に思ったのは「自分はスクラムマスターではなかった」ということです。チームメンバーとスクラムマスターを兼務する場合の罠に見事にハマっており、目の前の問題を自分で解決することばかり考えていました。自分がプロセスを改善していれば物事が勝手にアジャイルになると信じていましたが、SCRUM MASTER THE BOOKの教えはそれとは全く逆のことです。アジャイルなチームとアジャイルなデリバリーの仕組み - yigarashiのスクラム開発ブログではプロセスとチームというようにアジャイル開発の構成要素を分解したように書いていますが、実際はプロセスのみに注目していたところに、チームや文化に着目する視点が与えられて考えが広がったのでした。チームを信じ、チームが成長することを喜べるようになる必要があります。

そうした流れから、単にアジャイル開発プロセスを整備するだけの人から、チームをアジャイルにするスクラムマスターになろうとしています。そのために振る舞いや考え方を意識的に変化させています。さしあたって以下のようなことに取り組んでいます。

  • ポジティブな雰囲気を作る
    • ふりかえりではポジティブな話題に着目しやすいようにアクティビティを調整する
    • ネガティブな話題ではポジティブな言い変えをしたり良かった面をコメントする
  • チームに任せる
    • なんとなく解決策を持っている時もチームがアクションを出せないか待つ
    • 議論が多少白熱してもチームで収束させられないか様子を見る
    • ※そもそも解決したがりで解決策を思いつきがちなために今のキャリアを歩んでいるので、この部分は本当に難しいです。しばしば自分で解決策を喋りまくってしまうことがあります。また一方で、自分でも解決策がわからないことは正直に言った上で議論を始めるなど、丸投げとコーチングを履き違えないように気をつけています
  • オーナーシップを委譲する

こうした取り組みを行う一方で、自分が専任のスクラムマスターになりたいかということも考えさせられます。今のところ答えはノーで、どちらかというとエンジニアリングに関わり続け、エンジニアリングマネージャーとして技術面のリードやピープルマネジメントに手を広げられると面白そうな気がしています。偉大なスクラムマスターとしてのスキルをサブセットとして取り込んで、より面白いキャリアを歩みたいものです。

アジャイルなチームとアジャイルなデリバリーの仕組み

最近少しずつアジャイル開発の解像度を上げようとしていて、それが少し整理されてきたのでざっくり考えていることをまとめてみようと思います。

アジャイル開発をやりたいと言った時、取り組むべき領域は大きく2つに分けられると思います。それはデリバリーの仕組みとチームの2つです。そしてそれぞれに段階や考えるべきことがあると思います。

アジャイルなデリバリーの仕組み

アジャイル開発の大筋は、作ったものに対していち早くフィードバックを集め学習しプロダクトに反映することです。そのために、スクラムをはじめとしたイテレーティブな開発プロセスによって軌道修正の機会を増やしたり、CI/CDの整備のようなエクストリームプログラミングのプラクティスによってイテレーティブな開発にかかるコストを小さくしたりといった、仕組みの整備が広く語られています。アジャイル開発というとまずこういった話題を思い浮かべる人が多いでしょうし、実際に型から入るというのは現実的なやり方だと思います。

この仕組みのスコープをさらに2段階に分解できます。ひとつは、ユーザーへのリリースはビッグバンだが、ステークホルダの間でのみ内々にフィードバックループを回してイテレーティブに開発を進めている段階です。これは開発環境へのデリバリーという意味ではアジャイルなデリバリーができていますが、ユーザーのフィードバックの反映という意味ではアジリティを獲得できていません。これまではこの段階を乗り越える方法論が不足しており、多くのアジャイルチームが伸び悩んだのではないかと感じています。

しかし最近、データ駆動戦略のような方法論の台頭によって、その壁を乗り越え、ユーザーへのデリバリーも含めた全ての開発プロセスアジャイルにする方法論が広く示されたように思います。それこそがアジャイルなデリバリーの仕組みにおける2段階目で、本番へのリリースを繰り返して、ユーザーの行動データを元に仮説検証を繰り返し目的不確実性に対処していくやり方です。このあたりは『DMM.comを支えるデータ駆動戦略』を読んだ - yigarashiのスクラム開発ブログでも少し書きました。

アジャイルなチーム

デリバリーの仕組みにおける段階を進めるのと半ば独立に、チームのアジリティという軸があるように思います。つまり、チーム自身がどれくらい素早く変化に適応し成長できるかの度合いです。自己組織化と言い換えてもよいでしょう。これは見逃されがちですがアジャイル開発における非常に重要な要素だと思います。

仮にアジャイルにデリバリーするための枠組みが整ったとしても、チームがアジャイルでなかったとしたらどうでしょう。たとえば、最初に腕力のある人が開発プロセスやテンプレートを整備しまくって、それをただ使っているという状態です。それではより大きな変化が訪れた時に対処できず、徐々に衰退していきます。変化とは、ライブラリが更新されたり、新しいツールの導入を迫られたり、新サービスが台頭したり、技術革新が起こったりといった、サービス開発に訪れるあらゆる変化のことです。我々は、仕組みを育てるのと同時に、こうした変化にアジャイルに対処するマインドを育てなければいけません。そのために、ふりかえりやスクラムマスターによるティーチング・コーチングといった支援が重要になるでしょう。

また、アジャイルなチームを育てる上で、組織構造の問題が如実に現れるのではないかと考えています。というのも、チームのアジリティは、デリバリーの仕組みに比べてスケールしづらいように感じるからです。ある程度の人数でもアジャイルなデリバリーの仕組みを運用することは現実的で、バックログの流量が増えさえすればある程度スケールします。しかし文化を育てるという活動をスケールさせるのは難しく感じます。最終的には、チームが関わるプロセスをアジャイルカイゼンできるようになりたいわけですが、そのプロセスに関わる人が多く多様であるほど難易度が上がります。例えば、ふりかえりの参加者が15人にもなると、ふりかえりの手法を毎回変えて試すといった大胆なファシリテーションは望めません。この点から、アジャイルなデリバリーの仕組みは運用できているが、長期的に見るとアジャイルなチーム作りに支障があるので、チームや会議体を再編しましょうといった議論が可能になるかもしれません。

まとめ

アジャイル開発を大きく2つの領域に分けてそれぞれ解像度を上げて深めてみました。これらは完全に独立というわけではなく、どちらかというと豊かな相互作用を起こす関係にあると思います。仕組みによって考え方が変わったり、アジャイルなマインドが育つことによって仕組みの整備が進むといったようにです。今回の議論をもとに、ひとつひとつのカイゼンアクションを尖らせつつ、アジャイル開発を根気強く育てていきたいものです。

embulk gem installはインストールに失敗してもexit statusが0になる

表題の通りです。Embulkでプラグインをインストールする際にはembulk gem installコマンドをよく使うと思うのですが、このコマンドはパッケージのインストールに失敗してもexit statusが0になりません。本家のissueを見ると、この現象はembulk gemembulk bundle以下の全てで発生するようです1。現在stableのv0.9では直らず、v0.11で直るだろうと書かれています。

これで何が起こるかというと、DockerでEmbulkを動かしている時に、コンテナを動かすまでイメージビルドが失敗していることに気がつけません。つい最近その事故に遭って、依存パッケージのアップデートで依存関係が壊れてembulk-output-bigqueryのインストールに失敗していました2。日時のデータ転送が全て失敗した通知で異変に気付き、ログを追いかけて行った結果embulk gem installがサイレントに失敗していたことに気付きました。幸い復旧は用意だったので、exit status 0は勘弁してよ〜とキャッキャしながら対応しました。エラー自体は以下のような文言がこっそり出力されているという感じです。

ERROR:  Error installing embulk-output-bigquery:
  representable requires Ruby version >= 2.4.0.

イメージが壊れていることに気づけないのは困るので、以下のようになんとも味わい深い1行をDockerfileに加えて応急対応をしました(実際は他のいくつかのgemも||でつないで指定しました)。

RUN bash -c 'l="$(embulk gem list)" && if [[  ! $l =~ "embulk-output-bigquery" ]]; then exit 1; fi'

せめてGemfileとかを使って管理すると良いかもしれないな〜と思いながら放置してあります。差し当たり報告でした。頑張っていきましょう。

embulk-input-mysqlでCommunications link failureになる時の原因と解決方法

最近Embulkを使ってMySQLのデータをBigQueryに転送する仕事をよくやっています。データに基づく意思決定を促進するための大事な仕事です。これをやっている時にたまに以下のようなエラーに出会います。

org.embulk.exec.PartialExecutionException: java.lang.RuntimeException: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
 
 The last packet successfully received from the server was 5,024 milliseconds ago.  The last packet sent successfully to the server was 5,020 milliseconds ago.

この記事ではこのエラーの原因と解決方法を紹介します。

原因

結論から言うとMySQL Connector/JのsocketTimeoutオプション1によるタイムアウトが原因です。socketTimeoutは、このオプションで指定された秒数を超えてサーバーからのデータ送信が確認されない場合にタイムアウトにするものです。以下のようなEmbulkの設定で簡単に再現することができます。この例ではsocketTimeoutを5秒に指定してMySQL側で10秒sleepしています。

in:
  type: mysql
  host: ...
  user: ...
  database: ...
  table: ...
  select: sleep(10)
  socket_timeout: 5

out:
  type: stdout

この場合はMySQLからのレスポンスが遅いためにタイムアウトが発生しています。Embulkで実際にデータを転送する際には、クライアント(つまりEmbulk)側の処理が遅いために次のfetchを実行できず、結果としてデータ送信が発生せずにタイムアウトする場合もあります。

とはいえ、デフォルトのタイムアウトは30分とかなり長めに設定されているので、このエラーが発生するのは極めてスペックの低い環境で実行している場合に限られそうです。また、今回紹介した原因とは別にCommunications link failureが起こるケースも様々あるようなのですが、今回はそちらについては触れません。

解決方法

原因が分かってしまえば解決するのは簡単です。socketTimeoutよりも短い間隔でMySQLからデータが送信されるようにしてやれば良いのです。以下のように様々な方法が考えられます。

(1) socketTimeoutを伸ばす

embulk-input-mysqlのデフォルトのsocketTimeoutは1800秒です2。当然これを伸ばせばタイムアウトしづらくなります。さらに0を指定するとそもそもタイムアウトしなくなります。とはいえ、TCPコネクションが確立されたあとにサーバーがダウンしたケースもsocketTimeoutでカバーされるという意見もある3ので、バッチ処理を適切に失敗させるために適度なタイムアウトを設定するのが無難かと思います。Fargateで実行して気づかずに永遠に実行していたといった事故が起こりかねないと予想されます(他にも失敗させる機構はあるかもしれませんが)。

(2) fetch_rowsを小さくする

MySQL Connector/Jでは一回の通信で何件のレコードをfetchするかを指定することができます。embulk-input-mysqlにもfetch_rowsというオプションがあり指定することができます(デフォルトは10,000) 。これを小さくすると、一回のfetchにかかる時間や、embulk側でfetchしたレコードを処理して次のfetchを行うまでの時間が短くなるので、タイムアウトしづらくなります。当然サーバークライアント間の往復数は増えるのでオーバーヘッドは増加します。

(3) Embulk実行環境のスペックを上げる

Emulk側で処理して次のfetchを行うまでに時間がかかっている場合に有効です。ECSで実行しているならContainer Insightsを見てリソースが100%に当たっていないかを確認すると良いでしょう。何件か実行した感覚としては、Embulkを実行するときはCPUリソースが足りなくなりがちです。

(4) Embulkの並列数を下げる

Embulkを実行する際は、Digdagのようなワークフローエンジンと組み合わせて並列に実行することも多いように思います。このとき、並列数が多すぎるとCPUを食い潰してタイムアウトが発生することがあるようです。ワークフローエンジンの設定で並列数を調整してみましょう。

(5) データベースの当たり前のポイントを確認する

インデックスが効かない異常なクエリを投げたりロックが取られていたり、DB側のリソースが圧迫されてデータを返せなかったりすると当然タイムアウトする可能性があります。データベースからデータを読み出す際の一般的な注意点は踏襲しましょう。

最後に

デフォルトのタイムアウトが30分とかなり長めなので、調べながらホントに起こってたのか?という気持ちにはなったのですが、ひとまず手元で実験した結果や一般的に言えそうなことをまとめてみました。基本的にはEmbulkというよりJDBCMySQLの知識といった様子でしたが、大規模データを転送する場面では特に起こりやすいエラーではあるでしょう。ちなみに、一番オススメの解決策は(3)で、クラウドで実行しているならインフラ費用で殴って解決しましょう。実行時間も短くなってお得です。