2年間OSSにコントリビュートしてみての振り返り

# はじめに

2022年も残すところ1日となりました。皆さんにとってどんな一年だったでしょうか？
私の今年の目標は「積極的にOSSにコントリビュート（貢献）する」でした。2年前、初めてOSSにissueとPRを上げた時に比べ、徐々に慣れてきたと感じています。

とはいえ、思い返すとやらかしてしまった場面も少なくなかったので、記憶が新鮮なうちに振り返りをまとめました。お恥ずかしい話ですが、反面教師として聞いていただければと思います。

（~~ちなみに題名を「コントリビュート」とカタカナで書いたのは、「貢献」という字面が少し恥ずかしかったからです~~）

# コントリビュートのきっかけ

業務で使っているライブラリで一部設定が効かない箇所があり、先輩から「せっかくだから修正のPRを出してみたら？」と勧められたのがきっかけです。

「新人（当時）なのに、コード量も多い人気OSSにコントリビュートなんてできるのか！？」と不安がよぎりましたが、見よう見まねで無事PRマージまでこぎつけました。

https://github.com/dapr/components-contrib/issues/756

https://github.com/dapr/components-contrib/pull/780

（コードの修正箇所が1行だけだったのも幸いしました。振り返ると、いわゆる「Good First Issue」[^good]だったと思います）

# どんなOSSにコントリビュート？

上記の [Dapr](https://dapr.io/) も含めて、

- 業務やプライベートで普段使っているライブラリ、ツール
- 勉強のために新しく触ったもの

が大半でした。バグやドキュメントのtypo等にハマった際に、それを修正するという流れです。

# 作業

以下の流れで作業を進めていくこと多かったです。

- 不具合の再現、本当に不具合か調査
- 不具合や課題点の整理
- issue作成
- 実装を読む
- テスト追加、実装修正しコミット
- issueを解消するPR作成

リポジトリが公式でコントリビューションの流れを規定している場合は、その流れにそって進めていきます。

ブランチやコミットメッセージの命名規則まできっちりルールが決まっている例

https://github.com/vmware/govmomi/blob/main/CONTRIBUTING.md

issue作成からPRマージまでの流れについては、こちらの本がとても参考になりました。

https://nextpublishing.jp/book/11725.html

あえてこの記事で再度流れをさらっても仕方がないので、以下個人的にハマった工程について記載します。

## issue作成前に不具合を再現させる

issueでの説明が分かりやすいように、不具合と関係ない枝葉の部分を省いていきます[^minimal]。main関数だけのシンプルなコードになるのが理想です。
~~再現しない場合、実はアプリでの使い方が悪かっただけというパターンも結構あります。ドキュメントは再確認しましょう~~。

## PRを作る前にプロジェクトの方針を確認する

「明らかにバグ」かつ「修正方法が明確」な場合を除き、勇み足で実装を弄る前にコミッターへ方針を相談した方が良いです。
修正しようと思っている内容がOSSの仕様に沿っていない場合、二度手間になってしまいます。

（例：「特定のバージョンでレンダリングができない問題」と思ったら、実は「サポート外のバージョンを弾くバリデーションチェックが効いていない問題」だった）

https://github.com/OpenAPITools/openapi-generator/issues/13971

## CLA(コントリビューターライセンス契約)にサインする

PR作成後にオンライン契約書にサインする必要があります。主に「自分のコードをオープンソースにすることに了承する」といった内容で、企業系のリポジトリに多いです。

CLAは読むのに時間がかかり、中途半端な状態で放置すると最初から入力しなおす必要がある場合もあるため、まとまった時間を確保しておきましょう。
（GitHub連携される場合サインは初回のみで済みます）

また、CLAがあるリポジトリではコミットに署名を求められることがある (`git commit -s`) ので、こちらも忘れずに。

# やらかしたこと
## テスト大事！

たとえ実装が1行でも、コントリビューションの手順にテストについての記載が無くても、変更した箇所のテストは可能な限り用意したほうが良いです。以下の件で痛感しました。

### 例：バグを直したつもりが別のバグが...

Daprの[gRPC Proxy](https://docs.dapr.io/developing-applications/building-blocks/service-invocation/howto-invoke-services-grpc/)が定期的に疎通不可になる不具合を修正した際の話です。

https://github.com/dapr/dapr/issues/4197

詳細は割愛しますが、原因はproxy内でコネクションを更新できない[^conn]ためmTLSが期限切れになることでした。ならば、コネクションを毎回張りなおせば解消できそうです。幸いコネクション生成ロジックを1行修正するだけで済みました。

修正(v1.7): コネクションを毎回再生成

https://github.com/dapr/dapr/pull/4205/files

ところがこの修正は別機能のバグにつながってしまい、2つ先のマイナーリリースまで引きずってしまいました :bow:
Daprではストラテジーパターンが多用されていて、上記の一見シンプルな関数は様々な場所と依存関係がありました。そのため、別の利用箇所でバグが起きてしまったのです。


バグ: Streaming RPCリクエストを同時に受け取った場合、片方のコネクションが切れてしまう

https://github.com/dapr/dapr/issues/4537#issuecomment-1130753918

修正(v1.8): コネクションに参照カウントを用い、利用中は消えないように修正（今度はテストも作成）

https://github.com/dapr/dapr/pull/4667

バグ: コネクション周りでメモリリーク

https://github.com/dapr/dapr/issues/4937

修正(v1.9): proxyでもリクエストのリトライ可能にし、毎回コネクションを張りなおす処理を削除[^conn]
（こちらについてはアイデアを追いきれなかったため、コミッターの方にPR作成いただきました）

https://github.com/dapr/dapr/pull/5275

issueで「いつ直るんだ！？」というコメントが来るたびに冷や汗をかきました...そして、いつも無償で粛々とOSSのバグ修正をされる方々には頭が上がりません... :bow:

## issue/PR被り

issueやPRを作成する前に、既に同じ内容のものがないか確認しましょう。特に忘れがちなのが **PRの重複**です。

### 例：PR被り

issueを立て、対応する修正のPRを作成した直後に、同じ内容のPRがあったことに気づきました。
issueしか重複確認をしていなかったため、issueに紐づかないPRを見落としてしまいました...

作成したPR

https://github.com/vmware/govmomi/pull/2583

以前からあったPR

https://github.com/vmware/govmomi/pull/2385

最終的には、元のPR作者の方に「CLAの関係からこのPRの方が早く進められそう」と助言をいただきこちらがマージされました。
（CLAや著作権についてもコメントで丁寧に教えていただき本当にありがたかったです）

## ドキュメントは全て目を通そう

プロジェクトによっては、コントリビューションのお作法が複数個所に書かれている場合があります。
リポジトリだけでなく、**issueやPRのコメントもきちんと目を通しましょう**。

### 例：PRの承認フロー

Kubernetesのドキュメント修正をした際の話です。

https://github.com/kubernetes/website/pull/38171

KubernetesはPRをボットが管理しており、コマンドでワークフローが半自動化されています。
PRがなかなかマージされないと思っていたら、私がレビュアーの割り当てコマンドを失念していたのが原因でした :pray:
(マージ後に教えていただきました)

見返すと、ロボット君のコメントにあるリンクにちゃんと手順が書かれていました。自動通知のコメントも読み飛ばしてはいけませんね... (意味があるからこそ通知しているので)

> Please refer to our [pull request process documentation](https://git.k8s.io/community/contributors/guide/pull-requests.md) to help your PR have a smooth ride to approval.

> Make sure that your pull request has an assigned reviewer (assignee in GitHub). If not, reply to the pull request comment stream asking for a reviewer to be assigned. This is done via a bot command (the bot may have suggestions for this) and looks like this: `/assign @username`.
>
> PRにレビュアーが割り当てられていることを確認しましょう。まだの場合は、レビュアー割り当てを依頼するようにPRのコメントに返信してください。これはボットのコマンドを通じて行われ、以下のような形式です: `/assign @username`。

https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#why-is-my-pull-request-not-getting-reviewed

## 書き方は基本真似する、でも冗長なのは書き直してよい

大勢で作るプロジェクトなので、我を張らず、なるべく現状の書き方に合わせた実装を心掛けました。
（命名のくせ、メソッドの長さ、mutable/immutableどちらが好きか etc.）

一方、レビューで簡潔な書き方への修正を提案されたこともあります。冗長だったので既存のものを真似する必要はありませんでした。

### 例：バリデーションチェックのテストケース

https://github.com/dapr/dapr/pull/4024

```go:before
id := ""
for i := 0; i < 59; i++ {
	id += "a"
}
```

```go:after
id := strings.Repeat("a", 59)
```

これは自分では線引きが難しいので、現状維持寄りで問題があればレビューで指摘いただくという気持ちで臨んでいます。

# OSS関連のイベントに参加

また、個人作業だけでなく、OSSに関するオンラインイベントにもいくつか参加しました。

## OSS Gate

OSSに参加したい人（ビギナー）を後押しするイベントです。ワークショップ形式で、半日かけて実際のOSSにissueを上げることで、コントリビューションの第一歩を踏み出すという内容です。

https://oss-gate.github.io/

動画

https://www.youtube.com/playlist?list=PLg41HKRyxE6gIvt3sM09REYvLYkRRkuaS

上記「[はじめてのOSSフィードバックガイド](www.amazon.co.jp/dp/B087J9HXH5)」はこの「OSS Gate」の活動をもとに書かれた本です。
（著者Piroさんにもお会いしました！）

今回私は、ビギナーの相談に乗る「サポーター」役で参加しました。
参加前は自分がサポートに回れるか不安だったのですが、進行の方々がサポーターもサポートしてくださるので気負うことなく参加できました。

## Social Hack Day

シビックテックのコミュニティ「[Code for Japan](https://www.code4japan.org/)」が主催する1dayハッカソンです。

https://hackday.code4japan.org/

解決したい課題を自分で持ち込む or 誰かのプロジェクトに参加しわいわい開発を進めていきます。
特徴的なのは、Social Hack Dayのテーマは大半が「続きもの」、回をまたいで継続的にプロジェクトが進められている点です。
（私は要領が悪く単発のイベントだと不完全燃焼になってしまうので、腰を据えて参加できる形式はとても有難いです！）

10月から3回参加し、プロジェクトのOSSにコントリビュートしました。仕事以外でチーム開発をしたのが初めてだったので、開発フローや機能の検討等の進め方が新鮮で勉強になりました。
また、「どこでどう使われるか」をダイレクトに感じられるので開発後の達成感が大きかったです。

# おわりに

以上、OSSコントリビュートの個人的な振り返りでした。
最終的に、今年は小さな修正も含めて50件ほどPRを作成できました。~~来年はもっと上手く進めたい...~~

コントリビュート前と比較して、以下のような心情の変化があったと感じています。

- 自分もOSSに関われる
- 「動かない」と嘆くより動くようにする方が精神衛生上よい
    - アプリ側のハックで無理やりライブラリのバグを回避するのは辛い
- コーディングスタイルは（趣味や仕事で触れるよりももっと）多様
    - OSS開発は新しい設計を学ぶきっかけになる

自分語りのようになってしまいましたが、少しでもOSSにチャレンジしたい方の参考になれば幸いです。
まだまだ未熟ですが、来年もOSSに少しでも恩返ししていければと思っています。

[^good]: PRが比較的簡単に実装できる、OSS初心者向けissueのこと。「good first issue」というタグをつけて、OSSに挑戦したい初心者が見つけやすくしているリポジトリもあります。
[^minimal]: OSSによっては、明示的に「再現する最小の例をissueに添付してください」という指示がある場合もあります。
[^conn]: その他のAPIはmTLS期限切れ時にリクエストのリトライとともにコネクションを貼りなおしていましたが、gRPC Proxyはstreaming RPCを使っている都合上リトライができませんでした（当時）。現在はフレームごとの再送ができるようになったため、単純なリトライ処理になっています。