タイトル通りではありますが、先日、OSS上で挙がっているissueを取り組み無事リリースされるところまで行く事ができました。
コントリビュートと呼ぶには大分軽い内容ではありましたが、
例え、ほんの少しの変更でもコントリビュートはコントリビュートです。
自信持っていきましょう!(言い聞かせ)
という事で本記事はハードルを極限まで下げてご覧いただけると幸いです。
OSSへのコントリビュートは、最初、強いエンジニアしかやっちゃいけないものだと思い込んでおり、とても敷居の高いイメージでした。
しかし今回、世界的にも大規模で有名なPHPフレームワークであるLaravelでコントリビュートさせていただく事ができました。
実際にコミットした内容はシンプルなバグ修正でしたが、そこに至るまでの過程は非常に大きな学びとなりました。
本記事では、issueに取り組んで得た学びとコントリビュートまでの流れを共有させていただければと思います。
はじめに:なぜやろうと思ったか
LaravelのOSS活動に取り組んだきっかけは、
FWを使う上で「実際にどのようなことをやっているか」に興味を持ち、コードの追い方を理解することでした。
- バグへの対処: FWの「便利さ」の裏側にあるコアの設計思想やコードを追うことで、予期せぬエラーやバグに直面した際、推測ではなく確実な原因特定を行う道筋になります。
- 適切な使い方: コアの仕組みを知ることで、「この機能はなぜこのような仕様になっているのか」が理解でき、FWが想定する最適なパフォーマンスを引き出す使い方知るきっかけになったり、コードを追う癖がつきます。
今回のコントリビュートは、「FWの内部」に踏み込む第一歩の経験でした。
また、私はLaravelをきっかけにエンジニアになり、Laravelのおかげで今のエンジニア人生があり、Laravelをきっかけに世の中のプロダクトに関わらせていただき、そこで出会う人達と関わる事ができたと言っても過言ではありません。
私は、そんなLaravelにどんな形でもいいからほんの少しでもいいから貢献をしたいと思ったのも理由の一つでした。
そう、私は誰がなんと言おうとLaravelが好きなんです。
そして、OSS活動ってなんかかっこいい!というのも素直な気持ちでした。
何が言いたいかと言うときっかけはなんでも良いというお話です。
チャレンジしてみる敷居が低くなればと思います。
挑戦の第一歩:効果的なIssueの見つけ方
1. 自分でIssueを見つける:日常業務を「課題発見」の場にする
OSS貢献の第一歩として、GitHubのIssueリストから探すのではなく、日々の開発業務の中で見つける事です。
(と言っても、中々見つからない上に自分が見つけたものは既にIssueとして挙がっていたりするのですが....)
その上で、日常の開発業務の心持ちは大事だと思ったので、私が個人的に意識していた事を下記に示します。
「FWのエラーだ」で片付けない: 普段の実装を通じて、「なんかエラーが発生する」「この挙動、ちょっとおかしいな」と感じたとき、それを 「FWのバグ」で片付けずに、常に課題解決のチャンスである と念頭に置いてみました。
再現性の確認: エラーの原因を深掘りし、それがフレームワークのコアロジックに起因すると確信できたら、シンプルな再現コードを書いてみます。
これが後にPRに含めるテストコードの原型になります。
リポジトリを追いかける習慣: 毎日リポジトリを眺め、今どのコンポーネントでIssue報告が多いか、どの機能に手が入っているかというプロジェクトの「今」を追いかける事も意識していました。
2. Issueの選定・ラベルの見方
ラベルは、コミットをマージに導くための重要な手がかりです。
-
bugの優先: 機能追加(feature)よりも、現状の挙動を直すbugの方が、修正が正しければマージされる確率が圧倒的に高いと感じました。 -
good first issueの活用: これらは意図的にスコープが狭く、独立性の高いIssueとして選定されており、手をつけやすい内容だと思います。 -
古いIssueを避ける: 長期間コメントが途絶えているIssueや、議論が白熱しているIssueは、プロジェクトの方向性が定まっていない可能性があります。
また、誰かが進めているIssueを横取りしてしまう事の無いようにしっかりやり取りを追いかけるのが個人的には一番大切だと思いました。- 比較的新しく、議論が落ち着いているものが初回のIssue解決としてやりやすいと思いました。
コミットまでの具体的なプロセスと工夫
0. localで複数バージョン対応毎にLaravelプロジェクトを準備しておく
LaravelのCoreなframeworkリポジトリだけではなく、私の場合は普段と同じようにLaravelのPJもcloneして作業を進めていました。
ただ、一つ違うのはv10, v11, v12でそれぞれクリーンな状態で構築しておく事でした。
(v10以降でframeworkの思想が更新されているので個人的には必要だと感じました。)
コアなコミッターの方はもしかしたらそんな事はせずに「framewaork」リポジトリ内でテスト駆動で進めるのかもしれませんが、私は慣れていないのもあってやはり普段触っているLaravelのユーザー視点で軽く触れる環境があった方がやりやすかったので用意していました。
それぞれのverでわざわざ環境を用意した背景としては下記になります。
Issueが報告されたverで、まずはlocalでいつも通り書いて試せるようにしたかった。
その上で報告されているissueのverは現在時点で基本的にはv10以降のため、10以降の環境を用意してみました。
いつも触っているLaravelプロジェクトなので、再現するための過程は軽くコードを書くくらいで済むのと、再現するまでの体験が良かったです。
(本当は、テストコードを書いて試すのでも良いかと思います、私は初回のコントリビュートという事もあって自分が安心できる方法で再現する事を選びました。)
この後、詳細に記述しますが、ざっくり下記のように進めました。
- いつものLaravelプロジェクトのリポジトリでissue内容を再現させてみる。
- テストコードでissueのバグを再現させる。
(phpunitで本来の期待値のテストケースを作成しfailedを起こさせる)
※バグがissueの報告通りであれば、本来あるべき期待値をテストケースに追加すれば失敗するはずなので、この段階では失敗する事が期待です。 - laravel本体の処理を改修する。
- 「2」で記述したテストケースが成功するまでデバッグを繰り返す。
- testコードを全て実行し、AllGreanになったらcommitしpush。
いわゆるTDDの形で進めていました。
(とか言いつつ、CI上でしか発生しないエラーに遭遇した?ので、やり方が良くなかったのかもしれませんがCIで何度もfailedを食らってしまいましたが...)
最終的には、Laravel本体のframeworkリポジトリに加えて下記のような状態で作業を進めていました。
それでは、ここまでLaravelコントリビュート以前の個人的な事前準備の話だったので、いよいよ本題のコントリビュートするまでに直結する私が行なった手順を記述してみようと思います。
1. Laravelでコントリビュートするまでの流れ
OSSコントリビュートは、以下の流れが基本となります。
初めてのコントリビュートでは特に、Gitコマンド操作が戸惑いやすいポイントです。
(お恥ずかしながらupstreamとか今まで叩いた事が無かったです。)
準備フェーズ:Laravel本体のリポジトリをFork・Cloneする。
- リポジトリのフォーク: 公式リポジトリにアクセスし、「Fork」ボタンを押して、自分のGitHubアカウントにコピーを作成します。
-
ローカルへのクローン: フォークした自分のリポジトリをローカルにクローンします。
git clone https://github.com/YourGitHubID/framework.git -
リポジトリの登録: 公式リポジトリを
upstreamとして登録し、最新の変更を取り込めるようにします。git remote add upstream https://github.com/laravel/framework.git -
最新の変更を取り込む: 作業開始前に、必ず公式の最新状態を取得します。
git pull upstream 12.x # 例: 1.xブランチで作業する場合
作業フェーズ:ブランチ作成と改修
-
ターゲットブランチの選択とブランチ作成: Issueが報告されたバージョンに対応するブランチ(例:
12.x)から、作業用ブランチを作成します。git switch -c fix/issue-xxxxx 12.x -
バグの再現とテストコードの記述(TDDの適用):
- 報告されているバグを再現させ、失敗するテストコードを先に書きます。 「失敗するテスト」 は、ようはバグの再現をテストコード上で起こさせる感じです。
-
コアコードの改修: 失敗したテストが成功するように、
src/Illuminate/内のコアコードを修正します。 - テストの成功を確認: 事前に書いたテストコードがすべて成功することを確認します。
PR作成フェーズ:PushとPull Request
-
変更をコミット: 適切なコミットメッセージ(Conventional Commitsに従う)で変更を記録します。
git commit -am "Fix: [Queue] Fix CallQueuedClosure displayName serialization" -
変更をPush: フォークした自分のリポジトリにPushします。
git push origin fix/issue-xxxxxx -
Pull Requestの作成: GitHub上で、 フォーク元の公式リポジトリ(
upstream) をターゲットにしてPRを作成します。
EX. 【実録】PR #57881での具体的な修正内容
私のPR(#57881)は、キューに入れられたクロージャの取得ロジックを修正する内容でした。
対象Issue:#57597
修正の目的
IlluminateQueueCallQueuedClosure の displayName() メソッド内で、キューに入っているクロージャが SerializableClosure(シリアライズ可能なクロージャのラッパー)である場合と、そうでない場合とで、Closure名を取得する処理を正しく切り分ける必要がありました。
実際のコード修正(安全なクロージャオブジェクトの取得)
修正は src/Illuminate/Queue/CallQueuedClosure.php の displayName() メソッドで行われました。
// 修正後のロジック($this->closureがSerializableClosureなら中身を取り出す)
$closure = $this->closure instanceof SerializableClosure
? $this->closure->getClosure()
: $this->closure;
$reflection = new ReflectionFunction($closure);
修正の詳細:
-
型の判別:
$this->closureがSerializableClosureのインスタンスであるかをチェックします。 -
純粋なクロージャの取得:
SerializableClosureの場合は、getClosure()メソッドを呼び出して内部にラップされている本来のClosureオブジェクトを取り出します。 -
リフレクションの適用: このようにして安全に取得した純粋な
Closureオブジェクトに対してのみ、ReflectionFunctionを適用し、Closure名生成に必要なメタデータ(情報)を取得するようにしました。この修正により、キューのチェイン処理で
SerializableClosureが使われた場合でも、エラーなく正しくキューを取得できるようになりました。
なんだか小難しく解説してみましたが、やってる事は三項演算子に変えただけです。
その判断をする為の調査には時間を必要としましたが、ここでお伝えしたいのが案外難しい事をやっていなくても少なくとも「貢献はできる!」という事でした。
OSS活動でのコミュニケーションと振る舞い
1. コミュニティでの適切な振る舞い
-
作業開始の宣言(I'll take this.): 修正したいIssueを見つけたら、必ずコメント欄で 「私がこのIssueに取り組みます (I'll take this. / Working on this.)」 と宣言すると良いと思います。
他のコントリビューターの方との作業重複を防ぐ目的と、自身の対応意欲を示す事ができると思います。
※英語で表現している理由は、コミュニティの中での公用語が英語だからです、私は英語がからっきしなので全て翻訳してやり取りをしていました...笑 -
横取りしない: 他の人が既に作業を宣言している場合は、絶対に横取りしてはいけません。
倫理的?なお話だと思いますが、誰かが進めてるのが明確なのにも関わらず進める事は失礼になる場合がありますので、もしIssue内のやりとりを見て進捗が出ていない場合でチャレンジしたい場合は事前に声かけすると親切だと思います。 -
質問は具体的に: 質問する際は、何を試したかを具体的に示し、簡潔に意図を伝えると良いと思います。
これは、普段のお仕事の場においても同じ事が言えると思いますが一般的で常識的な事だとしても敢えて意識する事が適切なコミュニティでの振る舞いに繋がるのだと感じます。
2. コミットメッセージの書き方とリリースノート
Laravelでは、コミットメッセージのルールが決まっています。
これは単なるルールではなく、プロジェクトの効率を高める為です。
特に、リリースノートを見るとその意味が分かりました。
上記はLaravel v12.40.2のリリースノートのスクショですが、私のコミットが以下のように記載されています。
[12.x] Fix CallQueuedClosure::displayName after batch chain (#57597) by @CreareWorks in #57881
このように、コミットの種別(Fix)や影響範囲(CallQueuedClosure::displayName)が明確になっているおかげで、ユーザーは **「このバージョンで何が修正されたか」をリリースノートで瞬時に理解できます **。
コミットメッセージの品質は、プロジェクトのリリースノートの品質に直結します。
プロジェクト規模から見たCI/CDの学び
Laravelのような大規模なプロジェクトでは、CIの網羅性も流石に手厚かったです。
- 互換性の網羅性: 自身のテストケースが追加された多数のテストケース、複数のデータベース環境、そしてサポート対象の全PHPバージョンで並行して実行されるにも関わらずCIの実行完了までが短く感動すら覚えました。。
-
品質保証のレベル: CIのログを追うことで、ソフトウェア(FW)がどのように高い互換性と品質を維持しているかという、 大規模開発における品質保証(QA) の世界を覗く事ができます。
(ここまでやるのか、と素直に感動しました、でもこれだけの大きなPJだとそうだよなぁと納得もしました。)
普段、敢えて見る事が無いと思うのでCIのスクリーンショットを載せておきます、ご覧いただくと感動の共有ができるかと思います。
OSSコミットのメリット:活動の可視化
1. Publicな活動実績を示せる
企業案件で書いたコード(commit履歴)はNDA(秘密保持契約)により公開できません。
しかし、OSSへの貢献はすべて公開された活動実績となり、GitHubプロフィールで公に示せます。
- ポートフォリオとしてのOSS活動: 「世界的なプロジェクトで、他開発者と協力して品質基準を満たしたコードを書ける」という証明となり、現職でのアウトプットに活かしたり、転職やフリーランス活動における技術的な信頼性を高めます。
※正直、今回の私のcommitくらいではその証明にはなりません。
コアなコミッターと呼べるくらい、常にOSSをwatchし貢献し続ける事がその証明になるのだろうなとは思っております。
とは言え、今回は挑戦機会のハードルを下げて、トライしてみる事とそこから得られる学びを発信したく執筆させていただいております。
2. 開発者としての自信とモチベーション
「OSSコントリビューター」という聞き映えは、単純にカッコいい!笑
冗談っぽく言っていますが、こういう動機でも全然いいと思うんです。
やらない善よりやる偽善(某有名漫画)と同じ精神で動機がなんであれ貢献する事には価値があると思います。
commitされれば、動機はなんであれ沢山の人が知らずの内に、自分が書いたコードの一部を動かして使う事になります。
エンジニアとしてこんなにロマン溢れる体験は無いと思っています。
自分の書いたコードが百万人以上の開発者が使うFWの一部になるという経験は、開発者としての自信を付けさせてくれると思います。
この自信は、日々の業務への取り組み方にも良い影響を与えると思いますし、自己学習的な側面にも繋がると思っています。
最後に
Laravelに限らずコントリビュートする際は、必ず公式のコントリビュートガイドラインを参照しましょう。
これがプロジェクトへの敬意と、これまでのコントリビューターへの敬意にも繋がると思います。
- Laravel公式コントリビューションガイドライン: Laravel Contribution Guide
そして、私のように一歩踏み出す事に勇気を要する方々に、
まずは簡単なものでいいから挑戦してみるきっかけになれればと思っております。
ここまでご覧いただきありがとうございました。






