jqとfzfで雑にJSONを探索する

tl;dr

タイトルにあるとおりだが jq と fzf で JSON を探索する Shell Script を書いた。
具体的にはある JSON ファイルを渡したら JSON Path が一覧され、fzf を使って JSON Path をインタラクティブに絞り込みながら値を preview するというもの。
百聞は一見にしかずというので gif を貼っておく。

動作している様子

背景

同僚が「フィールド名はわかっているけどわざわざ JSON Path を指定するのが面倒」という事を言っていたのでなんか簡単に JSON Path の抽出/絞り込みできないかなと思った。

本体

gist.github.com

使い方

自分は .zshrc に貼ってあるので適当に shell を開いて jpath <JSON file> のようにして使う。

おわり

ちなみに同僚が本当に求めていたものは jq '.. | .<field name>? | values' で十分だった。

引用: https://pmac-agpc.ca/project-management-tree-swing-story

RubyでStringのサブクラスをprotobufに渡せるようにした話

毎年機会を見つけては OSS に Contribute したいなと思っているのですが、今年もなかなか思うようには貢献できませんでした。
今回は protocolbuffers/protobuf に投げた Pull Request について紹介します。

TL;DL

  • Protocol Buffers の Ruby 実装として Google が公式に実装している protocolbuffers/protobuf がある
  • Protocol Buffers の string フィールドに対して Ruby の String を渡すことができるが String のサブクラスは受つけず、ランタイムエラーが出てしまっていた
  • String のサブクラスも受け取るように提案した PR が受け入れられた 🎉

protocolbuffers/protobuf とは

前提として Protocl Buffers とは Google が提案・利用しているデータシリアライゼーションフォーマットで、ILD を提供しています。
主に gRPC による通信で利用されています。protocolbuffers/protobufGoogle によって実装された公式ライブラリであり、2023年12月現在で C++ / C# / Dart / Go / Java / Kotlin / Objective-C / PHP / Python / Ruby 向けのライブラリを提供しています。 このライブラリを使うことで各言語で任意のオブジェクトを Protocol Buffers の形式にシリアライズすることができます。

今回は社内のマイクロサービス間通信をおこなっている箇所で Rails の gRPC Server を実装したところ、後述する問題に遭遇しました。

何が起きていたか

Protocol Buffers に定義されているスカラデータ型 は15種類あります。 具体的には符号付/符号なし整数型と浮動小数点型、そして文字列型、ブーリアン型、バイト型です。

今回 Ruby の protobuf ライブラリの文字列型(string*1 ) のフィールドに対して RailsActiveSupport::SafeBuffer を渡したところ以下のようなエラーが出てしまいました。

Google::Protobuf::TypeError: Invalid argument for string field 'foo_field' (given ActiveSupport::SafeBuffer).

どうあるべきか

ActiveSupport::SafeBufferXSS 対策として提供されているクラスで、HTML として出力する際に安全であるとマークされた文字列を表すためのクラスです。
実装を見てみるとわかるのですが String を継承したサブクラスとなっています。

module ActiveSupport # :nodoc:
  class SafeBuffer < String
    # ...
  end
end

github.com

String のサブクラスが string フィールドに渡せないのはリスコフの置換原則に反しているため修正するのが適切だと考えました。

修正

Ruby の protobuf ライブラリは FFI を用いて実装されており、本体はC言語で書かれています。今回の修正対象もC言語でしたが、運よく適切な関数 `rb_obj_is_kind_of を見つけることができ無事実装できました。
困りポイントとしては fork したリポジトリ上でうまく CI を動かすことができず、本家に Pull Request を投げて実行してもらうまでは CI 通るか確認ができなかったのが大変でした。

github.com その後 Reviewer と何度かやり取りがあり、Pull Request 上は Closed になっていますが 34908e2 で無事取り込まれました 🎉

感想

今回の内容とあまり関係ないのですが、今までは Pull Request の Description やコメントでのやり取りなど英語でのコミュニケーションに課題を感じていてあまり積極的になれませんで。
しかし ChatGPT をはじめとする AI を使い、AI に一度レビューしてもらうことである程度自信をもってやり取りすることができるようになったと感じました。
これを機に2024年はもっと積極的に OSS 活動していきたいです。

*1:この記事中では String と表記した場合は Ruby の文字列クラス、string と表記した場合は Protocol Buffers の文字列型とします

ISUCON13 振り返り

2023-11-25 に開催された ISUCON13 に参加した記録です。
次回以降に向けてやったことなど振り返っていきます。

メンバー

4月頃大学の編入同期に声かけたら快諾してくれた。

  • メンバー1 ISUCON 初参加。普段は Ruby on Rails を使ってバックエンド開発をしている。
  • メンバー2 ISUCON 初参加。普段はセキュリティエンジニア。Web 開発はほとんど未経験。
  • 僕 ISUCON は2年ぶり2回目。普段は Web エンジニア。

今回はメンバー1・2にアプリケーションをメインで見てもらい、自分はインフラいじりつつ重そうなところを見ていく方針。
普通だったらメンバーが使える言語の最大公約数を取るけどノリと勢いで Go になった。

練習

10月中盤と本番直前の祝日にそれぞれ1日ずつ使って行った。
環境はそれぞれ AWS に用意した。

1回目

ISUCON12 予選を利用。
目的としては本番の流れをざっくりと理解するためで、あとは index 貼る練習とかをちょこちょこやった。
あとはデプロイ周りの script とかを整理していた(が、本番のときとの環境祭に耐えられなかったので実際は使わなかった。カナシイ)

2回目

private-isu を利用。
6時間くらい何も見ずに触った後に ISUCON 本 を読んで振り返り。
画像の切り出しなどはここで触れてよかった。あと、1ヶ月ぶりの練習で割と色々忘れてたので直前にやって良かったねってなった。

当日

メンバー1の家に集合。寒かったのでコタツでぬくぬく。
開始までの間に空の repository 作ったり、コマンドメモを見返したりしていた。

10:00

最初の役割分担

  • メンバー1 マニュアル確認。アプリケーションの動作確認。スロークエリの設定
  • メンバー2 マニュアル確認。アプリケーションの動作確認。アイコン切り出し
  • 僕 実装の確認。git の設定。alp 導入。デプロイスクリプト設定

設定後に走らせた初回ベンチは 3,653 で10分くらいで初期設定できた。

11:00

ログ眺めながら方針を立て、以下のタスクに取り組んだ。

  • MySQL のスロークエリのトップがプリペアドステートメントだったのでおもむろに止める
  • SQL 眺めながら index を貼っていく
  • アイコンの切り出し

着実にボトルネックは移り、DB の負荷も減ってきたが対してスコアは伸びない 4,398

12:00

アイコンの切り出しには成功していたのでアイコンハッシュ周りに取り組み始める。

index がうまく貼れていなかった場所を修正したりして 6,211。 さらに index が貼れないか試行錯誤する。

その後少し伸びて 7,639

このとき、ちゃんと計測せずに write の負荷が高くなるのを懸念して慎重にやりすぎて時間を使いすぎた気がするので反省。

13:00 ~ 17:00

なぜか家の Network が落ちたのでお昼休憩を取ることにする。一旦休み。
買い出しから帰ってきたら復旧していたので再開するもベンチが止まる。一旦休み。
2台目のサーバーで開発できるように設定。

明らかに悪そうな INNER JOIN を取ったりするけどそんな伸びない。
statistics 周りの N+1 の解消に取り組み始める。他にも NG ワード判定をアプリ側で行うように修正し、やっと 10,261

17:00 ~ 18:00

ここに来て MySQL の restart に失敗するようになる。
怖くなったので手を付けていなかった3台目のサーバーのセットアップを行い、AWS から VM の再起動を行う。

ログを止めたりしつつ、ベストスコア 11,892 で終了。

個人の振り返り

K

  • チームの Discord 鯖 に GPT-4 の bot を用意しておいたが良かった
    雑に設定方法を聞いたり、SQL からの ER 図生成などに役立った
    ChatGPT が生成した reference を sqldiagram に投げた図
  • 物理で集まったのでコミュニケーションコストが低かった
    直接画面見ながらペアプロできるのは良い
  • 練習でやったことが活かせていた
    画像の切り出し周りは練習でやってなかったらあの短時間でできなかったと思う
  • 細かいけど git commit --allow-empty -m "Score: xxxx" ってしておくのは良かった
    壊したときに戻す場所が見えやすいし、振り返りに便利
    空コミットでスコアを記録すると便利
  • git sync が便利 notfounds.hatenablog.com

P

  • bot を雑に作りすぎてコードを全部投げられなかったので改善の余地がありそう
    そもそもコードの修正などは外部のツールやエディタ上で完結させたほうが良いかも
  • index を慎重に貼りすぎた
    SQLbot になげて index 貼ってもらうくらいやっても良かったかも
  • デプロイ周りがあまり自動化できていなかった&属人化していた
  • インフラ周りに弱すぎる
    練習で複数台構成の練習をサボっていたので本番でやる勇気がなかった
    今回スコアを稼ぐ上で重要な DNS 周り何も分からなくて手を付けられなかった
    MySQL が再起動しなくなったときなどに復旧できなかった。複数台構成にするなら致命的な気がする
  • 瞬発力がない
    言語への理解&慣れ。ISUCON 頻出問題への対応速度
  • キャッシュを効率的に使えなかった
    どこをキャッシュして良いのか?そこをキャッシュすると効くのかが見極められていなかった

T

  • 最初の環境構築はなるべく自動化したいので Ansible などを勉強する
    ログの投稿も手動だったので webhook とか使うようにしたい
  • 各メンバーがデプロイできるようにする
  • 練習で複数台構成を試す
  • 数をこなしてたくさんの施策に取り組めるようにする
  • レギュレーションやアプリケーションの書き込み箇所をちゃんと把握する

TypeScript で TextDecoder と TypedArray の取り扱いではまった

TypeScript と書いているが、JavaScript にも当てはまる内容になっているはず。

tl;dr

  • utf-8 のバイト列(TypedArray) → string への変換で TextDecoder を使った
  • Uint8Array をそのまま TextDecoder.decode() に投げたら生成された文字列がいまいちおかしい
  • Uint8Array.prototype.subarary() を使うとコピーせずに部分配列を作れる

おさらい

いきなり TypedArray とか言われてもピンとこないと思うので関連するオブジェクト周りについておさらいしておく。

ArrayBuffer

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
固定長の生のバイナリデータを扱うためのオブジェクト。他の言語でいうバイト配列。
しかし直接操作することはできないため、後述する DataView や TypedArray を使う必要がある。

DataView

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/DataView
ArrayBuffer を操作するためのインターフェースオブジェクト(ビュー)。ArrayBuffer は生のバイナリデータであるため操作する際は環境毎のエンディアンを考慮する必要があるが DataView を使うとエンディアンに依存せず読み書きできる。

TypedArray

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
ArrayBuffer を配列形式で扱えるようにするためのインターフェースオブジェクト(ビュー)の総称。
あくまで総称なので TypedArray という名前のオブジェクト自体は存在しない。
TypedArray には Uint8ArrayInt32Array などが存在する。繰り返しになるが、これはあくまで View であり、実体は ArrayBuffer である。

TextDecoder

https://developer.mozilla.org/ja/docs/Web/API/TextDecoder Web API の一つで、UTF-8EUC-JP などのデコーダーを提供する。
TypedArray を受け取り、デコードした結果を文字列として返す。
対となる API として TextEncoder がある。

はまったこと

以下のようなことをしていた。

// 適当に buffer を確保
const buf = new Uint8Array(32);

// 実際はここで Stream からデータを読み込んでいたが、ここでは TextEncoder の書き込みとしておく
// e.g. await s.read(buf);
new TextEncoder().encodeInto("Hello, World!", buf);

// byte列 → string 変換
const decoded = new TextDecoder().decode(buf);

console.log(decoded);                                     // "Hello, World!"
console.log(decoded.replace("Hello, World!", ""));        // ""
console.log(decoded.replace("Hello, World!", "").length); // 19 <-- !?

byte 列の操作に慣れている人からしたら当たり前かもしれないが TextDecoder は特定の byte 列からゼロ値までをいい感じに decode してくれるのではない。
与えられた byte 列をすべて decode しようとする。

decoded を表示した際に "Hello, World!" と表示されたが、実際には見えていないだけで Null 文字が28個続いているのである。

どのように対応したか

const buf = new Uint8Array(32);

// byte 列操作系のメソッドは大体読み込み・書き込みした byte 数を返してくれる
// e.g. const n = await s.read(buf);
const { written: n } = new TextEncoder().encodeInto("Hello, World!", buf);

const decoded = new TextDecoder().decode(buf.subarray(0, n));

console.log(decoded);                                     // "Hello, World!"
console.log(decoded.replace("Hello, World!", ""));        // ""
console.log(decoded.replace("Hello, World!", "").length); // 0 <-- 👍

TypedArray には TypedArray.prototype.subarray(begin[, end]) があり、メモリのコピーなしに同じ型の新しい TypedArray を返してくれる。
メモリのコピーが発生しないということは、物理メモリ的は元の TypedArray が参照している ArrayBuffer の実体と同じであるということなので、ここで生成された TypedArray への変更は元の TypedArray にも影響をあたえる。逆もまた然りである。

const base = new Uint8Array([4, 2]);
const copied = base.subarray(0);

copied[0] = 0;
console.log(base.toString());   // 0,2
console.log(copied.toString()); // 0.2

配列のコピー・複製を行う場合は TypedArray.from(source[, mapFn[, thisArg]]) を使うと良い。

const base = new Uint8Array([4, 2]);
const copied = Uint8Array.from(base);

copied[0] = 0;
console.log(base.toString());   // 4,2
console.log(copied.toString()); // 0.2

まとめ

TextDecoder に TypedArray を渡すときは subarray を用いて適切な範囲指定の TypedArray 作ってそれを渡してあげたほうが事故がなくて良いと思う。

余談

ちなみになぜこの問題にあたったのかと言うと TypeScript で TCP Server を立てて独自プロトコルの parse をしていたためである。
はじめは通信周りのバグか何かだろうと思い tcpdump を使って binary packet を眺めていたのだがてんで見当違いであった。

2021年買ったもの

今年も早いもので残すところ数時間となってきました。
昨年は「今年買ってよかったもの」を紹介しましたが振り返ってみると今年はそんなに買ったものがないなと思い、買ったものをつらつらと書き残しておこうかなと思います。

notfounds.hatenablog.com

ウルトラワイドディスプレイ

オフィスではディスプレイを縦置きにしており、Slack や長めの GitHub Issue などを眺めるのが非常に便利で自宅にも欲しかったので買いました。
買ってよかったかというと微妙で、よく調べずに縦置きディスプレイとして使えそうだなーと思い買ってしまいました。
実際はアスペクト比的に縦置きは向いていないですが買い換えるのも面倒なのでそのまま使っています。
もし縦置きディスプレイが欲しいなら WQHD か 4K の 16:9 のディスプレイをおすすめします。

デスクライト

部屋の照明が一つしかなく作業机が暗くて目によろしくないので買いました。
色味や明るさを調整することができ、MTG 時は自分の顔にライトを向けることで顔色をよく見せることもできて便利です。
BenQ が出しているものが有名ですが半額以下の価格でほぼ同じような機能が使えるので個人的にはこちらで十分です。

ライミングシューズ

10ヶ月ほど履いていた2足目のクライミングシューズに穴が空いてきたので新しいものを探していました。
結局色々探した結果2足目と同じ靴を買いました。
少しダウントゥになっていて小さいホールドなどにも乗りやすいです。
機能面だけでなくデザイン的にもめちゃくちゃ気に入っています。 shop.adidas.jp

スマホ用三脚

ボルダリングの様子を撮影するために買いました。やはり客観的に見るということは大事で自分の登っている様子を後ろから撮ってみると想像と全然違っていて面白いなと思いつつ、思い通りにならないもどかしさがあります。
三脚自体は伸ばすと結構高さがありますが(室内で使う上では)割と安定します。
足と伸縮部分が別々になっていて分解しないとカバンに入らないため、毎回組み立てるのが面倒です。

無印のブラシ

こちらもクライミング関係のグッズとなっています。
もともと洋服用のブラシですが、豚毛を使っておりクライミングシューズだけではなくボテなどの大きなホールドを磨くのにも重宝しております。
www.muji.com

激落ちクロス

ライミングシューズを拭くために買いました。
少し濡らして拭くときれいにチョークを拭きとることができます。

Pixel 5a(5G)

これまで使っていたスマホは Pixel 3a でバッテリーの持ちが不安になってきたのと 5G 回線が欲しくなったので買い替えました。
Pixel 3a の前までは iPhone を使っていたのですが、Pixel *a シリーズは安価でありながら普段使いでは十分すぎるスペックがあり満足しています。
端末間のファイル送信で AirDrop が使えないのが結構辛いので AppleGoogle は頑張って規格を共通化して欲しいなと思います。
Google Pixel 5a (5G) - Google ストア

GoPro Hero9

ボルダリングとダイビングの両方で使えるカメラが欲しいなと思い自分への誕生日プレゼントとして GoPro を買いました。
結局今年はあまりダイビングに行くことができなかったのとボルダリングではスマホのカメラで十分ということで全然使い倒せていませんが来年こそは活用していきたいです💪
標準で10m防水がついているため多少雑に扱っても安心して使えるのと、圧倒的な手振れ補正が推しポイントです。 gopro.com

Sony α7IV

カメラ自体は大学生の頃から興味があったのですがなかなか踏ん切りがつかずに買わずじまいでした。
10月に同僚と伊豆大島に行き Sony のカメラを勧められたことがきっかけで今回購入しました。

海外では先行して10月末に発売された Sony の新型ミラーレス一眼で、日本では12/17発売だったのですが運よく当日に入手することができました。
正直初めてのカメラということもあり α7iii でもいいのでは?と迷いましたが、自分の性格上新しいものを買っておかないと絶対後悔するなと思ったのでこれにしました。
www.sony.jp

Tamron 17-28mm F/2.8 Di III RXD

以前 Rentio で Sony の α7ii と純正の 50mm F/1.8(SEL50F18F) レンズを借りたことがあり、最初のレンズとして広角 or 標準域、単焦点 or ズームレンズなど非常に悩みました。
ギリギリまで Tamron の標準域ズームレンズ 28-70mm F/2.8 Di III VXD G2 と迷いましたが、今カメラを買って一番撮りたいものは風景や星景写真でそれにあったレンズということでこれを選びました。
www.tamron.jp

三脚

上で書いたとおり僕はカメラで星景写真を撮りたいと考えています。
星景写真はカメラを固定し、シャッタスピードを長くして撮るため三脚は必須のアイテムとなっています。
正直重さは気にしないのでアルミでもカーボンでも良かったのですが丁度いいアルミの三脚が見つからなかったのでカーボンにしました。
実はまだ届いたばかりで実際に撮影で使えていないのですが、少し触ってみた感じ品質は良さそうだなという感想です。

実は10月頃からほぼ毎週花屋で切花を数輪買うようにしていて部屋に飾っています。
きっかけは友人の家に行ったとき机の上に(造花でしたが)花が飾ってあり素敵だなと思ったからです。
殺風景な部屋に花があると少しばかり心に余裕ができた気がしますし、蕾から咲く姿を見ると頑張ろうと元気をもらえます。

この時期は週に1回程度の水替えでも長持ちしてくれるのでとりあえずで試しやすい季節だと思います。
花屋に行くとワンコインくらいで買えるミニ花束もあるので是非飾ってみてはいかがでしょうか。

f:id:notfounds8080:20211221004421j:plain
白のカーネーションと赤リンゴ

git sync

これまで git checkout mastergit fetchgit pull を one line で書いたものを sync という alias で ~/.gitconfig に登録して使っていた

[alias]
  sync = !git checkout master && git pull origin master && git fetch -p origin && git branch -d $(git branch --merged | grep -v master | grep -v '*')

しかし最近はデフォルトブランチが mainリポジトリが増えてきており、この one liner が動かないことも多い
そこで mastermain のどちらにも対応した git sync script を作った

gist.github.com

やってくれること

  • master/main ブランチもしくは current ブランチの更新
  • マージ済みのブランチの削除

使い方

  1. 適当に path の通った場所に置き、実行権限をつけておく
  2. ~/.gitconfigsync という alias を用意し、1で保存した script を登録する
  3. git sync もしくは git sync -c を叩く

免責

ソースコードの使用に起因するいかなる損害に対しても責任を負わないものとします。

deno_std で始めるゼロからの OSS contribution

f:id:notfounds8080:20211211041437p:plain
Photo by Philipp Berndt on Unsplash

これは Deno の AdventCalendar 2021 の11日目の記事です。

qiita.com

この記事では自分が過去に deno_std に投げた PR をもとに、OSS への contribution の第一歩として deno_std はおすすめじゃないか?という話をします。
deno_std は Deno の標準ライブラリで Deno のコアチームによってメンテナンスされており、ほとんどのコードが JavaScript/TypeScript で書かれています*1

とはいえ、僕自身 OSS にバリバリ contribute しているわけではなく deno_std への PR はまだ2つ投げただけの初心者です🔰。OSS に興味があるけど「普段バグ踏んだりしてないし...」「よく聞くけど何していいのか分からない...」*2のようにまだ手を出せていない人がこの記事を読んで、自分にもできそう!やってみよう!となってくれるといいなと思っています。

少しだけ自己紹介をすると、都内で自社のWebサービスを開発しているエンジニアです。業務では主に Rails や TypeScript を書いています。
就職してから業務以外での開発が減り、なにか手を動かしたいなーと思って OSS 活動に手を出してみました。

OSS への貢献の種類

ひとえに OSS への貢献と言っても様々なものがあると思っています。

  • ドキュメントの typo やリンク切れなどの修正
  • バグ報告や自分も再現したよ!という報告
  • こういった機能あったらいいななどの機能追加の提案
  • バグの修正
  • 機能追加

etc...

もちろんそれぞれ貢献の難易度は異なりますが、どれも大事な貢献です。
また、貢献としてよく挙げられるものは上のような例だと思いますがまずいちばん重要なことはそのツールやライブラリを使うことです。*3

deno_std を選んだ理由

僕が OSS 活動する対象として deno_std を選んだ理由は以下です。

もともと Deno 自体に興味があったが、Deno 本体は Rust で書かれている。
とはいえ Rust は全く分からないし今の所 Rust に対するモチベーションは高くない。
deno_std という標準ライブラリは TypeScript で書かれてるっぽい?
とりあえず全ての issue を Watch してみよう!

大体これが今年の8月くらいでした。 その後最初の PR を投げるまでは以下のような流れです。

どうやら collections という module に新しく API を作っているっぽいぞ? ref: https://github.com/denoland/deno_std/issues/1065
配列操作がメインなので http や byte 周りの molude よりは気軽に手が出せるかも?

というわけで投げたのがこちらの PR です。

github.com

PR を投げるまでの流れとしては、元の issue 上でまず「この機能実装したいんだけど何か気をつけたほうがいいことある?」のようなコメントをしました。
それに対して他の contributer の方が「(意訳)こういうデフォルトパラメータがあると便利かもしれないから検討してみて。あと途中で処理を短絡できるときは短絡してね。」というコメントをくれたので、早速実装に入りました。

命名や引数などで少々議論があり初めての PR がマージされるかビクビクしながら見ていましたが無事取り込まれました 🎉

実際は常に collections のように取っ掛かりやすい*4機能実装があるわけではないため、自分が Pull Request を投げることができたのはタイミング的にも運がよかったなと思っています。 現在は Deno の開発方針として Node との互換性を高めるフェーズ(?)なので Node 関連の実装が多いです。
Node 互換性の対応が落ち着いたらまた機能開発などが活発になっていくんじゃないかなぁと期待しています。

「どの OSS を選べば良いかわからない」といった人は以下の項目に当てはまる物が良いと考えています。

  • 自分が普段使っている/好きな言語で書かれている
  • その OSS 自体に興味がある/好き
  • issue のラベルに good first issue のようなラベルがあり、新規参加者が貢献しやすい環境がある
  • (英語に自信がない場合) 日本人の開発者がいる*5

そういう点で普段 TypeScript を書いている人で特にこれがやりたいというものがない場合、 deno_std は割とおすすめできるんじゃないかなと思っています。

最初に何をするか

さて、この OSS に貢献するぞ!と決めたはいいものの「普段使っていてもバグなんて見つけてないしな...」という方も多いと思います。
そういった方におすすめなのは上で述べたように

  1. バグ報告に対して自分も再現したよ!というコメントをする プログラミングをする上で避けて通れないのはバグで、いくらテストコードを書いていたとしてもバグをなくすことは現実的ではありません。
    もちろんそれは OSS のプロダクトでも同じで、毎日たくさんのバグが報告され修正されています。

しかし、中には報告されたものの他の開発者の手元で再現しなかったり、特殊なケースでのみ発生するため優先度を落として対応しているものもあります。 そういったものに対して「自分の環境でも再現したよ!」とコメントすることでバグの原因がわかったり、優先度の見直しが行われ早急に対応が進むことがあり、バグが再現する環境を報告することには十分が意義があると考えています。

  1. good first issue に取り組む good first issue とは GitHub のデフォルトラベルとして用意されているもので、このページによると以下のような説明となっています。

good first issue Indicates a good issue for first-time contributors

初めて contribute するひと向けに、取り組みやすい issue などにこのラベルがつけられています。
大体そういった issue は description がしっかり書かれていたり、対応方針が明確になっていることが多い印象です。
また、よく分からなかった場合でも「この issue に取り組みたいんだけど、どうやって進めればいい?」のようなコメントをすると親切に答えてくれると思います。

注意すること

いくら OSS だからといって無闇矢鱈に PR を投げていいものではありません。
mentainer も暇ではなく限りある時間の中で PR をレビューするので、しっかりマージされる PR にするためには最低限のルールを守り reviewer の負担を下げる必要があります。

例えば以下のようなものです。

  • PR の description を書く
    「なぜこの PR が必要なのか」「この PR でなにを解決したのか」など書いておくと親切です。また、issue を紐付けておきましょう。
  • テストを書く
    変更内容だけ書いても本当に動くのか確認する必要があります。reviewer が手元でコードを動かして動作確認するのは負担になるため、なるべくテストコードを書き CI 上で動作確認できることが望ましいです。 どうしてもテストコードが書けない場合はその旨を description に書きましょう。
  • コードのスタイルを整える
    普段開発している code base とコードのスタイルが異なることは多々あります。最近は CI でコードスタイルをチェックしていたり、手元で format できる仕組みが整えられていることが多いです。
    「郷に入れば郷に従え」と言うとおり指摘されたら素直に直しましょう。どうしてもそのスタイルに賛同できない場合は別の issue を立ててみてはいかがでしょうか?
  • PR のサイズはなるべく小さくする
    OSS に限った話ではなく普段の開発を行う上でも重要ですが、気付いたら diff が大変なことになってしまうこともあると思います。
    これも reviwer の負担を下げるために大事なことなので、diff が大きいなと思ったら PR を分けましょう。

いくつか基本的な注意事項を挙げましたがプロジェクトによって気をつけなければならないことは異なります。
contribution に関しては各プロジェクトでガイドラインを用意していることが多いです。

Deno の例を上げると公式ドキュメントやプロジェクトの README.md に contribution のガイドが載っているので、PR を立てる前は一通り目を通しておきましょう。

deno.land

github.com

まとめ

自分が deno_std に PR を投げた体験をもとに、deno_std は初めて OSS 活動する人におすすめという話をしています。 また、OSS への貢献の種類と注意事項についても簡単に書いてみました*6

この記事を読んで OSS 活動に興味を持ったり、活動のきっかけになると幸いです。

(おまけ) 自分が英文書くために使っているツール

自分はお世辞にも英語が得意だとは言えないです。
OSS 活動をする上で英語でのコミュニケーションは避けて通ることはできないと考えていて、OSS 活動への参加を躊躇う理由の一つでした。 しかし、最近の機械翻訳が非常に優秀なこともあり OSS 活動への心理的ハードルは大分取り除かれました。
そんなわけで普段どのようなツールを使って英文を書いているかメモしておきます。

  • DeepL DeepL Translate: The world's most accurate translator みんな大好きなやつですね。英文書くときは大体これで再翻訳してみて意味が通じるかの確認に使うことが多いです。自分が論文読むときに欲しかった...
  • Grammarly Grammarly: Free Online Writing Assistant 自分は本当に英作文に自信がないので課金しています。ニュアンス?ライティングのスタイル指定ができたり、改善理由の説明を表示してくれるので学ぶことが多いです。
  • GitHub コミットメッセージの例文や issue の例文探したりと日々眺めています。

*1:wasm 周りで Rust が使われているようです

*2:これは去年までの自分です

*3:特に使ってみて少しでもいいなと思ったら GitHub の Star をつけるなども非常に重要です

*4:配列操作系はネットワークや非同期処理などに比べて必要な知識は少なく、普段自分で実装したり他のライブラリの実装を参考にしやすいなと感じます

*5:これは結構大きいです。まず自分の場合は心理的負担は下がるかなと思っています。また、内部の話などが聞けたりするとモチベーションなどにも繋がります

*6:僕自身完璧にできているとは思っていなくて、普段の開発で指摘されることも多いです...