小さいPull Requestを作る工夫
社でPull Request(以下PR)を小さくする工夫の話が盛り上がっていて機運だったので書いてみる
自分は普段PRを作るときも見るときもPRサイズを小さくするようにしている
見るときというのは、レビューにアサインされたときの話で、PRのサイズが大きかったら分け方の方針の提案込みで分割するよう依頼している
そんな自分が考える、PRがでかくなってしまうケースとそれに対して小さくするアプローチ、というのを書き出してみようと思った次第。
なぜPRはデカくなるのか
小さいPRを作る工夫、とは言ってみたが、むしろなぜPRはでかくなるか、そこからの対策、というブレークダウンで考えてみることにする
- PRを小さくしようとしていない
- 一つのことを実現するのにデカくなってしまう性質の機能である
- サーバーとクライアント一緒くたに作った
- 一度の変更で複数のことをやっている
- テストを丁寧に書いた
- 依存関係的に1箇所変更すると修正箇所が波及してしまう
- 自動生成コードを含んでいる
これらに対してどういうアプローチで対処するかを考えていく。
そもそも小さくしようとしていない
意識的に小さくすることを避けている
それならそれで良いと思う
小さくすることを考えたことがない
まずは小さくすることを考えてみてもらうということでひとつ
小さくすることに心理的抵抗がある
小さくしてみようと頭では思っても実際にやるのは面倒・大変といった気持ちを持っているケース
自分が過去に辿ったのは↓みたいな思考だった。(他の人と話す中で同じような考えを持ってそうと思ったこともあったような気がする
- やってみたらでかくなった
- 作りきったしそこから分けるのは手間が多い
- そもそも小さくするのは難しい
- 設計をちゃんと考えるのはコストが高い(やってみないと分からないことが多いケースでよくある)
- かといって考えきらずに決め打ちで始めると手戻りがち
- それなら最初から一気通貫でやって動くものを作るのが手っ取り早い
このようなケースでは以下のような考え方・アプローチで対策している
→大きくなってしまっても構わないので、まず荒削りで作りきってしまい、そこから分割を考える
これは設計とかも固まってないなら泥臭く雑でもいいから正常系1個だけ最低限通すとか、とにかく製品の品質には足りないけどごく最低限動くレベルで作り、
それを元にして分割を考えるというやり方
不確実性が高い状況だとこういうアプローチのほうが結果的には速いことも多い(はず)
PRのサイズとは異なる文脈だけど、設計を明らかにするとか、考慮事項の洗い出し、規模感の把握といった点でも有効な方法なので、割とよくやる
このアプローチを取ると、既存のロジックにどう手を加えればいいかが明らかになり、事前に必要なリファクタリングや作るべきモジュールがはっきりするので、
その単位でコードを清書してPRを出していくとある程度整理された状態でPRを出していける
このアプローチは大掛かりな機能だと難しいけど、そういうケースはそもそももっと上流工程のテクニックが必要になると思う
一つのことを実現するのにデカくなってしまう性質の機能である
既存のモジュールがデカい・結合度を下げることもアーキテクチャ上困難・その上で影響範囲が広いケース
例としてTypeScriptの型システムに機能を追加する、みたいなの
多分わりとどうしようもないと思う
でも普通のwebアプリケーション開発の範囲でこういう機能・モジュールになることってそんな内容な気はするのであんまり考えたことがない
テスト充実させておくとかしかないんじゃないかなあ・・・
サーバーとクライアント一緒くたに作った
機能を一気通貫で作ったケース。(サーバーとクライアント、みたいなのはあくまでも一例)
よくある。わけよう。
こういうのはcommitごちゃまぜになってたとしても技術的な境界で分割すればそんなに大変じゃないはず
ここでいう技術的な境界は、例えばクライアントサイドとサーバーサイド、サーバー内なら、DBアクセス層・ドメイン層・プレゼンテーション層などのアーキテクチャのレイヤー、のようなビジネスドメインではなく利用している技術による境界を称したもの
サーバーとクライアントサイドを実装した、という場合なんかは素朴に依存関係の末端から順に分割するようなやり方で十分通用すると思う
みたいなの
アジャイルプラクティスの文脈で、よくこういう作り方をすると全部結合するまで動かない、というデメリットを挙げられるやり方だけど、
そもそも全部作っちゃってから分割するときにこの方法を取るならそのデメリットもあまりないのでまあ良いかなと
サーバーで非互換な変更を加えてクライアントも同じPRでそれに対応、みたいなケースもありえるかもしれないけどそれが許されるなら手間をかけてまで分割しないほうがいいのかもしれない
一度の変更で複数のことをやっている
この機能作るならライブラリアプデして〜リファクタして〜あとついでにバグってるやつ直して〜とかしちゃうやつ
よくある。わけよう。
こういうケースは大体適当なところでcommit作らずに書き進めてごちゃらせがちなんで、1commitの粒度をある程度小さくすることを習慣にするのがよい
(と言いつつ自分は静的型付け言語で書いてると変更に対して強気にいられるからってクソデカcommitになっちゃいがちなんだけど・・・
ともあれ、commitさえ変更がそれぞれ独立した形で分かれていればPRを分割するのは簡単なので、そのような習慣を作っていくと良いと思う
この話題の発端の同僚氏が書いてたのがちょうどそんな内容の記事 だったので張っておく
conventional commitにするというのは自分もやっていて、大体1commitでやることはconventional commitに分類できる粒度になるようにしている
(以下余談)
記事にはcommitはあんまりしないとあったけど、自分はマメにする派
自分は割と、「こうやればうまくいくんじゃなかろうか?」→(やってみる)→ダメだったので消す、というのを頻繁にやるので、commitはセーブポイントみたいに使いたい感じ
大体commitするときは「なんらかのキリのいい状態」(コンパイルが通るとかテストが通るとか)にしておきつつ、適当に変更してダメなら消すをカジュアルにやっている
テストを丁寧に書いた
テスト、プロダクションコードの変更に対して何倍ものコード量になるんで、ある程度仕方ない
これもまあしょうがないと思う
一個上の 一度に複数の変更をしない
というのを心がけることでテストでのPRサイズ膨らむのを抑えられるので、そういう感じで頑張る
依存関係的に1箇所変更すると修正箇所が波及する
静的型付け言語・モノリシック・大きいコードベースなどでリファクタリングとかするとありがち
(静的型付けはそういうの言語機能で一発で出来ちゃうからどっちかというと やりがち
、のほうがニュアンスが近いかも)
そしてそういう状態そのものをどうにかする変更もデカくなるんでまあまあ踏ん張りがいる
こういうケースでは、変更内容がリネームやクラス・DBのテーブルへのフィールドの追加などのような単純な変更のみにするよう心がける
変更も前述した「一度の変更で複数のことをやっている」とのあわせ技で大きくなるケースもありがちなんで、単純な変更になるようにしつつ単独でPRを上げるのが効果的だと思う
(というか他にやりようあるのかな・・・
現実問題、単純な変更だけではいかないこともあると思う
その場合は可能な限り複雑さが局所的になるように設計して変更を加える、といった設計面でのアプローチで解決することになると思う
必要ならその準備のリファクタから入ったりしていくこともあるだろうが、まあ前提が影響範囲がデカいものなのでとにかくキツい・・・
そんな場合は歯を食いしばり両の腕のマッチョでシバいていくしかなさそう、辛い
自動生成コードを含んでいる
まあ割としょうがないんで、自動生成による変更だけのPRに分けましょう
commit分けておけばこの手の分割は簡単なのでやはりcommitは粒度を適当にしておこうということで
というわけでここまででPRがデカくなる理由からのPRを小さくするアプローチを書いてみた
紹介したアプローチを選ぶ指針とか、そもそも最初から小さくしようと思った場合のアプローチとかも書きたかったけどまとまりがなくなると思ったのでやめていおいた
機会があれば書くかもしれない(こういうの一生書かなそう)
chattunのSocket Mode対応をした
まだマージしてない けど、普通に普段から動かして使えてるので苦労話というか愚痴を書く
とにかくSlackAPIがしんどかった
一番しんどかったのは「EventAPIでユーザー固有のイベントを受け取れるようにする」という部分
これ今回の実装の前にEventAPIの調査段階(つまりchattunの初期実装の頃、2018年10月頃。当時の記事)から何度も調べて何度もわからずに来て今回ようやく見つけられて勝利に至った
userScope設定すればいいなんてドキュメントいくら探しても書いてなくてなんなの・・・ってなってしまった・・・
そもそもなんでSocket Mode対応をしたのかって、現職のslackワークスペースでも使おうとしてアプリ作ったりしたらどう頑張ってもRTM APIを使ったアプリのOAuthフローが通せなくてこれもう動かないのでは・・・と途方に暮れて仕方なくやったという出発点からしてSlack APIにしんどさを感じてしまう RTM APIはもはやいつdeprecatedになっても不思議じゃない気配があるけど、ドキュメント的には有効なAPIとして記載されてるので使えないはずはないし、前職では動いてたんだから動かせないはずがないのだけど・・・
まあRTM APIは間違いなくいずれ廃止するであろう機能なので余力があるときにやれて良かったとは思いつつ・・・
完全に愚痴になってしまった
転職して1ヶ月が経った記事
6/1 付でstand.fmへ入社してそこから1ヶ月経ったので感想とか書きます (あとLAPRASでキャンペーンやってて転職したことを紹介するとアマギフもらえるのもあり)
主にやってること
プロダクトはクライアントサイドReactNative・サーバーサイドNode.jsでflowを使っていてフルflow+js、DBにmongodb+atlas、インフラがGCPでサーバーはk8sという感じの構成
自分は経歴的にはフロントエンドエンジニアっぽいけどstand.fmではバックエンド中心にやっている
最初の1ヶ月ではライブ配信画面でリアルタイム処理をしているWebSocketサーバーの改修とか開発環境の改善をあれこれやったりしていた
雑多な思ったこと
全部js+flowなのがすげえいい
- 長いことjsやってきたので自分の主戦場でやれてる感じでいい
- かなりスムーズに開発に入れてよかった
- 言語が統一されててモノレポなので横展開しやすいというのも単純にいい
- やっぱ静的型付けは良い
不安はあったけどちゃんとやれてそうでよかった話
- なんだか面接での評判が良かったので期待に応えられるかとか、前職で5年もいたので新しい環境にスッと入れるかとか、そういうのが不安でした
- でも楽しくやれてるので良かった
- twitterで書いたけど早くも同僚からフィードバックをもらう機会があって、その内容も戦力になれてそうという実感を得られるもので良かった
へーしゃ月1で360度評価アンケート取っててフィードバックもらえるようになってて、書くのも楽だし貰うのも早くてありがたいな
— しめにゃん (@_sisisin) 2021年7月13日
リポジトリの流速がめちゃくちゃ速くて目が回ってる
- 会社の規模は小さいけど1つの開発チームとしてはQAやPM含め25,6人な体制で自分的には過去最大規模。速い。
- すごい
- 前職最後にやってたプロジェクトがずっと4〜5人のチームで大体のプルリク見る余裕があったけど現職ではslackに流れてくるgithubのコメント通知を見ることすらままならない
- すごい
- なるべく目を通そうとか自分が関連してるモジュールのコードが変更されてそうだったらキャッチアップしとこうとか思ってたけど全然無理そう(というか無理)
- すごい
- なんとか流し見だけでもして自分が力になれそうだったり関係が深そうなところは見に行ったりするぐらいで精一杯なかんじ
- すごい
- 結構みんな午前中に活動していておそおきの自分は勤務開始してまず圧倒的未読が読みきれないところから一日が始まる
- (みんな早起きできて)すごい
前職との比較で得るものがあった
- 前職でログ集約とかしたいと思ったけど結局やらなかった、ということがあったところから現職でログ集約だったりをしている状況に身を置いてみて、その良さの実感と、「そのソリューションはプロダクトにとって必要なのか?」という問いが弱いままやろうとするのは違うよなということの再確認ができたりした
- 前職では結局必要性が低かったんだということを実感
- 現職では問題の特定とかにこれなしではあまりにも時間かかりすぎることが身を持って感じた
- webはデプロイ楽で良い
- それなりに自明だけど、実感が伴うようになった
- ネイティブで問題が起きたときにログを仕込むところからスタートだと正攻法ではとにかく解決までが長くなる
- これどうやってリードタイム縮めるの・・・
- 立場が変わったので見えるものがあった
- プロダクトをリードする立場じゃなくなった→(これこの記事書いてて思ったけどこの目線で日々を過ごしてなかった気がしてきた)
- 新規参入者として活動し始めた→受け入れる立場の逆をやったことになる。なんとなく自分が受け入れるときに苦労、というか時間を掛けてやっていたことを自分ではあまり世話してもらわずに済んだような気がしてて、ここに自分は何が違ったのか?を自問する余地を得た(あんまり考えられてないまま日が過ぎてしまってるけど)。
- 意識してたのは、なるべく背景を引き出そうとする・issueだったりコードの意味だったりは追える範囲ではなるべく追うようにした・あてがわれたissueに関連するコード・モジュールについては極力広く追いかけるようにしたりした・あんまり聞こうとすると無限に聞きたくなってしまうのである程度「多分こうじゃろ」という気分で動くようにした・個別具体のやり方はなるべく自力で調べるようにして学習に費やした、らへん
- 悪い癖はやっぱり現職でも出ちゃうなあと感じている
- 重い解決策ばっかり出しがちで議論の行き止まりに誘導しがち
- 細かい気になることをほっておけないがち
- 結果的に1ヶ月間で開発環境改善プルリクたくさん出すことになったがメインでアサインされたissueの進捗には影響出てるのでアレ
- CI職人もした
- 前職に比べて色々と進行が速いと感じるんだけど何が違うんだろう?は全然見えてこない
- 多分マンパワーよりは仕組みだと思うんだけど
- モノレポでやってるがちゃんと開発スピード維持できててすごいと感じる
その他
せっかく音声配信のアプリ作ってるので自社製品でこんなこともやってみた
https://t.co/1ILwMEjxivに転職したので、転職ブログの代わりにライブ配信します!
— spice (@rabspice) 2021年6月8日
同じ時期に転職した @natural_clar @_sisisin と一緒にやります!https://t.co/21v65QOV1Z
よかったら聞いてみてもらえればと
転職活動に関して
※この記事は LAPRAS株式会社の「あなたの入社エントリにスポンサーさせてください」キャンペーンに参加しています (https://note.lapras.com/campaigns/sponsor/)
というわけで↑のライブ配信でも触れてるけど、LAPRASのキャンペーンのことがあるので書きます
- LAPRASでstand.fmの募集を見て、会社紹介スライド見て興味が出た
- LAPRASから申し込み
- そのままトントン拍子
- 全部オンラインだったので都合10日ぐらいで決まった
LAPRAS使ったけど最初に申し込みしてからはstand.fm側に提示されたと思われるツールで日程調整とかをしていて特に手間を感じなかったのでその点は良かったのかな
職探しという点ではあまり活用できてないけど、頂く求人の連絡は良さそうなものが多い印象なのと、クローリングして自分のアウトプットを集約してくれたりスキル可視化とかを勝手にやってくれるのが便利なので次回があれば(いろいろな意味で)、また使いたいという気持ち
というわけで転職して1ヶ月経った振り返りでした
頑張っていきたい
最近やったMacの環境改善
色々やったので備忘録
snap の導入
- option+[number]で設定。次の項目のkeylayout変更も必要。割り当てたアプリは↓。とりあえず頻繁に使うやつだけ端っこに設定したので5~7は適当に穴埋めでsystem preferenceとかが入ってる
option + keyでspecial character入力されないようにkeyboard layout変更
- この記事 でリンクされている.keylayoutファイルをダウンロード
- ファイル名とファイルの中の
keyboard
Elementのname属性をUS_without_sp_chars
に変更(元の名称がアレすぎる) - system preference > keyboardの入力ソースでこれを追加
karabiner-elementsで左右optionにかな・英数をアサイン
今までgoogle imeのショートカットを使ってたけど地味にエディタとかとぶつかるのと、↑のkeyboard layout変更で英数入力でgoogle imeを使わなくなったので。
- ここ から
For Japanese (日本語環境向けの設定) (rev 6)
の設定を探してきてimport - optionキーでかな英数割当のやつを有効化
- ほんとはcmdキーのほうが良い気がするんだけど、前にcmdキー使って誤爆多すぎて使うのやめた記憶があるので一旦optionキーを使うことにした
karabiner-elementsでhhkbモードを設定
なんか見つけたのでもののついでに。 リアフォの十字キーは遠いのが結構ストレスだったのでこれで多少便利になると思うけど慣れるまで時間かかりそう
Happy Hacking Keyboard Compatible Mode (rev 2)
をImportしてHHKB Arrow Mode
を有効化- 左caps、右ctrlをfnにアサイン
Rubyの静的型解析を触った感想を書いたりする
この記事は Opt Technologies Advent Calendar 2020 10 日目記事です
12/25にリリースしたRuby 3.0を使った話をしているので時空が歪んでるように見えますが気のせいです
9 日目の記事は @shachitaku さんの M5StickC x JoyStick HATで寝ながらパソコンを操作できるBluetoothマウスを作ってみた です
11 日目の記事は @shoyaokayama さんの LINEThingsとM5CoreInkを使ってO2Oマーケティングを体験してみる です
Ruby 3.0の目玉機能として静的型解析のサポートが挙げられるでしょう
最近仕事ではRailsを書いてるけど、もともと静的型付け派な自分としては大変気になる機能なので試してみて感想を書き残しておこうと思った次第
良かった点は静的に型情報を解析できるようになる、に尽きるので、基本的に足りない点を指摘することに終始する点、ご了承いただければと
結論
まだ早い(そりゃそう)
自分が求める体験との乖離が結構大きかったのと、エコシステムが粗く、これからどんどん変化していくと思うので追従が当面の間かなり大変だろうという所感
(実際に使ってフィードバックしたりして貢献していかないと進化していかないだろうから使いたい気持ちもある)
触った感想とか
- steep checkが遅い
- 実装を変更するたびに型定義を更新しないといけない
- エディタサポートが弱い
- 現状はRBS作ってもuntypedな部分を厳格にしようとすると生成したコードを書き換える必要がありそう?
- 理想的には実装を書き換える→TypeProfによるコード生成→自動で補完!だと思うけど、そうなると自動生成されたRBSファイルを直接変更じゃなくて上書きのような形にしないと継続的にTypeProfの世話になることが出来なくて難しそう。どういう方針なんだろう
- 型定義ファイルの取得方法がgemなどのツールでサポートされていない(git submoduleとかで取ってきてそのパスを指定する必要がある)
- これはSteepの問題ではあるけど、SteepってRubyにバンドルされてるわけじゃないから、そこまでgemなどの公式サイドのツール側でサポートしてくれるようになるんだろうか?という疑問はある
- irbのようなREPL環境でも型情報が使えるようになってほしい
その他の感想
そもそも疑問なんだけど、型定義ファイルの生成と型定義ファイルそのものを扱うライブラリが本体にバンドルされても静的解析ツール(Steep)が本体に入らないのってどういう理由なんだろうか
RBSファイルの存在は最終的にSteepありきになるはずなのでSteepに特化した機能を入れたくなりそうなもんだけど非公式のツール相手にそこまでやれるもんなんか、みたいな
Rubyの開発方針とか詳しくないので、本体には入ってないけど実質公式みたいなもんだからいい、みたいなのもありそうではあるけど
終わりに
まだ実装を書いておらず、ほぼツールチェイン周りへの感想なのだけど、それでこれだけ出てくるので中々に前途多難を感じる
CIとか組み込んで実際の開発体制に組み込めるか?という点や型の解析周りでの感想は追々やっていけたらなと
参考資料・関連リンク
RBS Railsを使ってRailsアプリケーションにSteepを導入する - pockestrap
GitHub - soutaro/steep-vscode: VSCode extension for Steep
Rubyで型チェック!動かして理解するRBS入門 〜サンプルコードでわかる!Ruby 3.0の主な新機能と変更点 Part 1〜 - Qiita
rbs/syntax.md at master · ruby/rbs · GitHub
Ruby 3の静的解析機能のRBS、TypeProf、Steep、Sorbetの関係についてのノート - クックパッド開発者ブログ
type-explorerがポシャった話
ポシャったので記録に残しておこうと思った回
なぜポシャったか
このコンセプトを解決するためのAPIがTypeScriptのCompilerAPIに存在しないであろうと結論づけたため
何が問題になったか
TypeParameterをもつ型を展開出来ないことがわかり、拡張機能として提供する 型を展開できる
という機能が非常に限定的になってしまうことが問題となった
どういうことかというと、以下のような型 Generic<T>
とそれを利用する Foo
があったときに、
type Generic<T> = { prop: T } type Foo = Generic<{foo: string}>;
Fooの型が { prop: { foo: string; }}
という形であることをCompilerAPIの操作で得ることが出来ない
これではConditionalTypeやMappedTypeを利用した型関数を使った結果がどんな型になっているか?を閲覧したいというモチベーションが解決できないという結論になった、という流れ
CompilerAPIの詳細
こちらの記事( type-explorerというTypeScriptの型を展開・閲覧出来るVSCode拡張を作っている - sisisinのブログ )で解説したとおり、この拡張機能では ts.Node
型を巡回、つまりTypeScriptのAST Nodeを見て回って型の構造を引っ張ってくるという処理をしている
さてこの仕組みでいくと、Genericsを持つ型はAST Node上では当然型パラメータを展開出来ないという課題とぶつかることになる
先の例でいえば、 Foo
から以下のような構造を取りたいのだが、
type Foo +-- type Generic<{foo: string;}> +-- prop: {foo: string;} +-- foo: string
AST Node的には Foo
及び Generic<T>
という2つの型の定義を独立にしか取ることが出来ないのだ
type Generic<T> +-- prop: T --- type Foo +-- type Generic<{foo: string;}>
これは当然で、 AST Nodeはあくまで「テキストとして存在しているTypeScriptのコードを抽象構文木として扱う」役割でしかなく、そこに記述されている型情報を解決するのは別のComponentの役割だからだ
ではその型情報を解決するComponent(checkerというやつだ)のAPIを使えばいいだろうと軽く考えていたのだが、これが甘かった
確かに「型パラメータを解決してその結果を得る」事はできるのだが、その得られた結果がAST Nodeのように巡回可能な構造をしていなかった
checker.typeToString
のような指定した型の解決した結果を文字列で得るというようなAPIしかなかったのだ
どうやらTypeScriptの型検査の仕組みは基本的に遅延評価になっていて、エディタサポートを提供するLanguageServiceでは指定されたNodeをピンポイントで解決してその結果を渡す、という構造になっているらしい(らしいというのはissueのコメントとかに書いてあったため)
これはパフォーマンス対策としてそうなっているとのことで、たしかにエディタサポートするのに都度プロジェクト全体を型検査していては重いのは想像しやすい
改めてissueを探してみると同じような課題を解決する方法を提供してくれ、という内容のものも見つかった
github.com github.com github.com
この辺のissueのやりとりで先のcheckerの仕組みに関する仕様について知ったあたりで、現状ある道具で素直に解決できる方法はなさそうだと思って諦めるに至ったのであった
完
他の方法はなかったのか
いくつか考えたことだけ列挙しておく
- 拡張機能内でオンメモリのLanguageServiceHostを立てて置いて、VSCodeのTreeViewを操作するたびにそのHost内でファイルを操作してchecker.typeToStringの結果を得る
- →同期が無理・Host内でのファイル操作を自明に解決する手段を作り上げるのが苦労しそう・ナイーブすぎてまともに動かすの苦しそう
- checker.typeToStringの結果をparseしてAST Node化する
- この記事を書いてて思いついたけど、typeToStringの結果がTypeScriptコードとして必ずしも解釈出来ないのでうーん。あとAST化してもNode単独ではgetTextを呼び出せない(SourceFile内にpos付きのNode、つまりTypeScriptプロジェクト内のファイル上にNodeが存在していないとgetTextを呼び出せず、Nodeを拡張機能上で表示できない)
おわりに
PoCしたらダメだったという感じなのでまあしょうがないかなぐらいの気持ちではあるけど、個人的にとても欲しかったのでその点は残念だった
(あとまあ世の中にないソリューションっぽかったからこれは出来たらかっちょいいなと思ってたフシもあった)(ソリューションがないのにはないなりの理由があると思い至れなかったのはアレ)