Timee Product Team Blog

タイミー開発者ブログ

【イベントレポート】iOSDC Japan 2024

  こんにちは、iOSエンジニアの前田(@naoya_maeda) 、Androidエンジニアの伊藤(@tick_taku77)です。

2024年8月22-24日に早稲田大学理工学部 西早稲田キャンパスで開催されたiOSDC Japan 2024に、タイミーもゴールドスポンサーとして協賛させていただきました。 イベントは以下のように、3日間連続で行われました。

8月22日(木):day0(前夜祭)

8月23日(金):day1(本編1日目)

8月24日(土):day2(本編2日目)

私達もイベントに参加したので、メンバーそれぞれが気になったセッションや感想をご紹介します。

naoya編

前夜祭で、「動かして学ぶDockKit入門」というタイトルで発表しました。 www.docswell.com

去年行われたWWDC23で発表されたDockKitフレームワークでできることを、体系的に紹介するトークになります。DockKit対応デバイス自体はよくメディアに取り上げられていて認知度が高いですが、DockKitフレームワークはまだ歴史が浅いこともあり、DockKitフレームワークを使用して何ができるかということはあまり知られていません。

本トークでは、DockKitフレームワークでできることを開発者目線でデモを通して紹介しました。 技術記事ではさらに詳しく解説していますので、ご興味をお持ちいただけた方は是非ご覧いただけますと幸いです。 zenn.dev zenn.dev zenn.dev

ここからは僕が気になったセッションをご紹介します。

タイトル : GIS入門 - 地理情報をiOSで活用する

登壇者 : 堤 修一 さん

www.docswell.com

地図の仕組みを一から理解し、iOSアプリでさまざまな応用ができるようになることを目的としたトークです。iOSアプリエンジニアの方であれば、MapKitのAPIを使用してiPhoneの画面に地図を表示したことは、一度はあるのではないでしょうか。 一方で、地図が表示される仕組みについて深く考える機会はほとんどないと思います。本トークでは、地図の仕組みから始まり、実際にコードを見ながら地図の仕組みの解説を進めていきます。最初はベーシックな地図を表示する方法、最後はマップ上に人間とモンスターを配置した、某ゲームのようなデモを披露してくださいました。

タイトル : iPhone × NFC で実現するスマートキーの開発方法

登壇者 : 岡 優志 さん

www.docswell.com NFCのハードウェア特性や規格といった基礎知識的な話に始まり、NFCでスマートキーを作成する方法をデモを通じて紹介するトークです。NFC周りは僕も触っていたことがあるのですが、曖昧な理解だった部分が多いことを実感したトークでした。 このトークを聞いた後、僕もNFCデバイスを使用して何か作ってみたいなと思いました。 基礎を丁寧にわかりやすく説明してくださる、聞き手のことをしっかり考えてお話しされるokaさんらしいトークだなと感じました! iOSDC 2023でご登壇された「作って学ぶBluetoothの基本攻略 〜スマートキーアプリを作ってみよう〜」も非常に面白いので是非ご覧ください!

www.youtube.com

tick_taku編

タイトル : App Clipの魔法: iOSデザイン開発の新時代 by log5

登壇者 : log5 さん

speakerdeck.com

App Clipの概要やユースケースについて熱量高く解説されていて、iOSDC 2024のトップバッターを飾るにふさわしい未来にワクワクできる素晴らしいセッションでした!

App Clipは名前だけ知っていましたが、Androidで言うInstant Appのようなものでしょうか。 想定している活用方法を聞いてとても感動しました。

自分自身(特に普段使わないのにクーポンなどのために)アプリをインストールすることに結構抵抗があるタイプですし、昔バイトでレジを打ちながらアプリを勧めてインストールのヘルプまで行うのはカロリーも高く、忙しい時間帯ですとレジ待ちの列が長くなりかなり大変でした。 そのステップが短縮されるならユーザーにとっても店舗の方にとってもかなり負担が減ると思われるのでとても効果が高そうだと感じました。

また開発においてもモバイルアプリの共有には非常に課題を感じていて、webと違いURLを共有するだけでは完結せず準備に手間がかかります。App Clipを利用し、スプリントレビューなどにおいてその場でエンジニア以外にロールプレイをしてもらうことでより当事者意識の高いフィードバックが得られるデモンストレーションができそうな予感がします。

ゆくゆくはモバイルアプリはインストールするものではなくなる未来が待っているかもしれませんね。

とはいえ、話されていたユースケースを実現するにはまだまだ課題がありそうなので今後の動向に注目したいと思います。

タイトル : Wallet API, Verifier APIで実現するIDカード on iPhoneの世界

登壇者 : 下森 周平 さん

speakerdeck.com

ここ最近マイナンバーカードをiPhone(Apple Wallet)に搭載できるようになると話題になっていますね。自分も持ち歩くものがまた減るので非常に楽しみにしています!

www.digital.go.jp

モバイルeID (モバイル端末に搭載される身分証明書)に関する国際標準規格 (ISO 23220)があり、マイナンバーカード搭載の話もこの一環だそうです。このセッションでは モバイルeID についての理解と、証明書情報を取り扱うAPIの特徴やユースケースを紹介されていました。

我々アプリケーションエンジニアが気になっているアプリからの取得もAPI (Verify with Wallet API)経由でサポートされているとのことなので、ユーザー登録に本人確認が必要なサービスはセキュリティ的にも利便性的にもぜひ対応した方がいいとのことでした。タイミーでも登録時に本人確認を行っているので導入できたら面白そうだなと思っています。

ただし、現時点ではAppleへの利用申請が必要なことに加えて金融など特定のカテゴリーに分類されるサービスでしか利用が許可されていないそうなので注意が必要です。

これからのデジタル社会に向けてキャッチアップが必要そうな内容が解説されていて非常に勉強になりました。

ちなみにスピーカーの方は日本在住でカナダの仕事をしているそうです。僕はカナダへのあこがれがあるので職の探し方などもとても興味があります。

参考

https://www.soumu.go.jp/main_content/000779585.pdf

https://www.jssec.org/column/20231127.html

最後に

この三日間を通して技術的な知見を深めたり、久しい友人に会って話をすることができ、すごく有意義な時間を過ごすことができました。この場を用意してくださったiOSDCスタッフの方々、参加者のみなさん本当にありがとうございました!

上記で紹介したセッション以外にも非常に興味深いセッションが多くありました。 記事にある内容や、その他の内容についても、もしタイミーのエンジニアと話したいという方がいらっしゃればぜひお気軽にお話ししましょう!

product-recruit.timee.co.jp

#DroidKaigi に向けて数字で振り返るタイミーのAndroid開発

こんにちは、タイミーDevRelの河又です。

タイミーはDroidKaigi 2024にゴールドスポンサーとして協賛しています。
当日はブースも出展しておりますので是非、お立ち寄りください。

今回はDroidKaigiを前に一度、タイミーのAndroid開発を数字で振り返ろうという企画です。 Androidエンジニアの中川をインタビューアーとしてAndroid領域のリードエンジニアである村田にタイミーのAndroid開発についてインタビューする形式でお届けします!

タイミーのAndroidアプリのクラッシュフリーレートについて

2022年


2024年現在


※グラフ上の7日間、30日間の数値は当該期間全体の数値ではなく、デイリーの移動平均の数値です

続きを読む

タイミーでOSTを開催しました

こんにちは! Agile Practice Teamでプロセス改善やアジャイルコーチとしてチームの支援を担当しています、吉野です。 2024年4月にタイミーに入社後、初めてオフラインにて社内のOST(オープンスペーステクノロジー)イベントを体験してきましたので、レポートします。

今回お話しする内容

  • どんなイベントを行ったのか?
  • タイミーでのOSTはどんな雰囲気で開催されたのか?

を、お話ししていきます!

どんなイベントを行ったのか?

今回開催されたイベントの概要

という形式でのOSTが開催されました。

参加者について

タイミーのプロダクト開発組織に関わっている方の中から参加希望者を募っての開催でした。先陣を切って動かれていたrazさん、 りっきーさんの一声で、20人近くの人が一気に集まりました!(しかも3日ぐらいで!すごい!!)

結構OSTとか慣れているのかな?と思いながら当日参加したところ、なんと今までOSTを経験したことのある参加者は1/3ほどでした。

OSTの様子

私はまだ社内のオフサイトイベントに参加していなかったので、応募時点ではみんなのモチベーションがどれぐらい高いのかわからずの参加でした。

未体験のイベントにオフサイトで参加することには、一定のハードルがあると思っていましたが、一気に参加者が集まり「みんなのイベント参加へのオープンさ」を感じることができました! イベント参加や勉強へ前向きな環境は、その場にいてめちゃくちゃテンションが上がります!

タイミーでのOSTはどんな雰囲気で開催されたのか?

どんなOSTだったのか?

枠としては、15分区切りの4枠にて開催されました。

お題がめっちゃ出てきた!

OSTの原則として、

💡 OSTの原則の一部

  • いつ始まろうと、始まったときが適切なときである
  • いつ終わろうと、終わったときが終わりのときである

があるので、1枠の長さはそこまで気にしなくても良さそうと思いつつ、15分という時間は自分が経験してきた中で一番短い時間だったので、どうなるのかな?忙しくならないかな?と少し心配でした。

ですが、そのような心配は杞憂に終わり、15分の中でみんなポイントを絞って議論したり、時間が足りなかったら自分たちでテーブルや場を用意して議論を継続したりしていました。

全体の時間としても1.5h(マーケットプレイスを含めず)という短時間での開催でした。 そんな中、各々が自主的に話したいことを話して時間を最大限に活用したOSTらしいOSTだったと思います。 (運営されていたお二人も、ひたすら運営に集中、というわけではなく話し合いにも参加して自身でも楽しむスタイルで立ち回られていました!)

どんな雰囲気だったのか?

タイミーでは、プロダクト開発に関わる多くの方がフルリモートでお仕事をしています。 そのため、今回オフサイトで集まった直後は「ワイワイ!ガヤガヤ!」というわけではなく、少し緊張している空気を感じました。

しかし、オフサイトイベントへ自主的に参加されていることもあり、いざOSTが始まると自己紹介や自身が向き合っているお仕事の紹介など、積極的にコミニュケーションを取りにいっていました。

最終的には、お互いの悩みに共感する声や、笑い声が多く飛び交い、一体感を感じられるイベントになっていたと思います。 「初めまして」とか、「社内イベントへの参加が初めてなんです」という声も多かった中、2hもしないうちに熱量の高い場になっており、人見知りな私も最後はすごく楽しませて頂きました!笑

今後のイベントにも期待をしていきたいプロダクト開発組織

私は社外のオフサイトで開催されるコミュニティイベントへの参加が好きで、よく参加しています。

社内でも、何かイベントが開催できないかな?もしくは開かれたら参加できないかな?と考えていたところ、今回のOSTイベントへ参加しました。 初めての参加でしたが、熱い議論をしたり、同じ組織の人との繋がりができたりと、とても有意義な時間を過ごすことができました。

普段からリモートでお仕事しているからこそ、オフラインで集まれた時はコミュニケーションを重視する時間に対しての熱量は高くなるのかなーと思いました!

今後も、社内のオフサイトイベントが立ち上がれば積極的に参加していきたいですし、自分でも何か開催してみたいと思います!


あわせて、主催であるりっきーさん、razさんのご感想です!

OSTにかける思い

スクラムマスターをやっているりっきーです。

弊社はフルリモートの環境のため、会議の集中力を阻害する要因(Slackの通知だったり、会議とは関係ないことを勧めたり)が多いと感じています。そこで、どのような設計であれば参加者が集中できるかを模索した結果、OSTに辿り着きました。

OSTが優れている点は「自分自身で興味あるテーマを公募する / 自分自身で興味あるテーマにサインアップする」に集約されています。誰かに呼ばれて会議に参加するのではなく、”自分自身”でアクションを起こさなければ会議がうまくいかないので、参加者としても集中して会議に参加できる環境になると考えています。

今回は最もやりやすい環境であることと、全社のイベントで出社する機会があったのでオフラインで開催しましたが、オンライン環境ではより効果が発揮できると思うので、今後は回数を増やしていければと思います。

初参加でもあり、初運営のOST、うまくいってよかった

どうもスクラムマスターやったりエンジニアやったりしてるrazです。今はエンジニアをしております。思いつきでやってみたいと言ったところ、2人や参加者の協力のもとOSTを開催できました。僕の記憶してる範囲では社内で実施するのは初めてだったと思います。

OST開催の動機

今回、全社のイベントがありオフサイトで集まる機会がありました。その会までの時間の使い方として提案させてもらいました。普段フルリモートだったり、違うチームだったりであまり会話する機会のない人も参加してくださったのはとても嬉しかったです。

なぜOSTなのか?の理由は3つあります。

  1. 実は自分が未経験で興味があった
  2. 毎週開催してるアジャイル相談会の様子から、普段話せてないことが色々ありそうだった
  3. 雑に大人数集めて開催してもなんとかなりそうだから

1と3はあまり説明することもないので省略しますが、2のアジャイル相談会について補足します。

アジャイル相談会とは、毎週水曜の夕方ごろにりっきーさんが主催してくれてるその名の通りアジャイルとかスクラムに関して気軽に相談していい会です。ただ、実際にはアジャイルとかスクラムの枠にとらわれず、組織論やマネジメントといった様々な内容で会話しています。その中でも、組織全体に関わることは、話す機会が少ないんだろうと感じていました。そこで、熱量のある人たちが集まって会話できるOSTをやれたらいいのではと思って開催しました。

開催してみた感想

一応運営ではあるので、全体の様子をみながらではありますが、極力会話に参加してテーマを盛り上げていきました。経験者が1/3程度いましたが、半数以上は未経験で何もわからない状態での参加でした。しかし、みんなの自主性の高さがあってか、初回セッションから盛り上がっていて安心しました。自分もその様子に安心できたので、2セッション目以降はより会話に集中できたと思います。途中会話に夢中になりすぎてタイムキープを忘れていたほどです笑。

みなさん初めての開催にもかかわらず適応能力が高いので、事前に出したテーマじゃないことに転換していて、会話を楽しんでいると感じました。個人的にはもう少し「どうしよ?」感に包まれるんかな?と心配してたのですが、杞憂でした笑

次回開催なるか?

今回、準備期間も会自体も短い中での開催でしたが、思ったよりも盛り上がってましたし、個人的には成功したんじゃないかと思ってます。いい成功体験になったと思うので、次回開催に向けて思っていること3つあげて、僕の感想を終わりにしようと思います。

  • フルリモートへの適応
  • 多種多様な役職や職種の人の参加
  • 組織を変革していくアクションを生み出せる会へ

フルリモートへの適応: タイミーのプロダクト開発組織は、フルリモートなのでリモートでも開催してみてもいいかなとは思いました。ただ、実際に運営してみてオフサイト環境だからこそ成り立っているものがありそうとも感じました。四半期に1度くらいのペースで、オンラインとオフラインを交互で開催できても面白そうです。

多種多様な役職や職種の人の参加: 急な呼びかけなのもあって参加できたメンバーは限られていました。組織に存在している様々な役職や職種の人が充足できてない状態でしたので、次回はより多種多様な人に参加してもらえるような会を開催できたらなと思います。

組織を変革していくアクションを生み出せる会へ: 会話メインで会は終わりました。しかし、もう少し時間を長くとれれば、会話も深まりますし、具体的なアクションを生み出したりして、組織やチームに変化をもたらす会にもしていけると思うので、そういう会を目指せればと思います!


以上レポートでした!

ハピネスドアもハッピー感想めっちゃ多かったです!

効果検証の事前設計と結果の管理について

こんにちは、タイミーのデータアナリティクス部でデータアナリストをしている夏目です。普段は主にタイミーのプロダクトに関する分析業務に従事しています。

本日はタイミーにおいて、効果検証設計を施策前に正しく行える仕組みづくりと効果検証設計・結果を一元的に管理できるデータベースについてご紹介します。

解決したかった課題

タイミーでは、プロダクト、マーケティング、営業組織などで様々な施策が行われています。しかしながら、それらの施策の結果を判断する効果検証には課題も多く存在しています。今回は以下の2つの課題にフォーカスしてブログを書きます。

  1. 効果検証設計が事前になされていない施策があった
  2. 効果検証設計や検証結果がバラバラに保管され、会社として知見が溜まっていなかった

まず1つ目の「効果検証設計が事前になされていない施策があった」に関してです。タイミーではアナリストの数も限られており、事前に全ての施策に目を通すことは難しいです。施策によっては事前に効果検証設計がされておらず、必要なログが取れていなかったり、検証に必要なサンプルサイズが担保されていなかったりと、正確な効果検証ができないケースが存在しました。

次に2つ目の「効果検証設計や検証結果がバラバラに保管され、全体として知見が溜まっていなかった」に関してです。タイミーでは様々なチームが施策を行っています。基本的に効果検証の結果はチームごとに管理されており、別のチームの人がその結果を探すことが難しいケースもありました。

取ったアプローチ

以上の2つの課題を解決するために、行ったことは主に以下の3つです。それぞれをこの項では説明していきます。

  1. 各チームが効果検証の設計と結果を記入できるNotion上のデータベースを作成
  2. 効果検証設計と結果を記入するテンプレートを作成
  3. 他アナリストや他チームへの説明の実施
1. 各チームが効果検証設計・結果を記入できるNotion上のデータベースを作成

1つ目のデータベースの作成に関しては、正確にはあるチームがすでに使用しているデータベースを少し改変し別チームにも展開しました。

イメージとしては、ダミーですが以下の画像で、行の一つ一つが効果検証設計と結果をまとめるドキュメントとなっています。チーム横断で1つのデータベースにまとめることにより、別チームの検証結果や検証方法を簡単に参照できるようになっています。

ダミーデータベース

2. 効果検証設計・結果を記入するテンプレートを作成

次は2つ目のテンプレートに関してです。データベースから効果検証ドキュメントを作る際に利用するテンプレートを作成しました。

テンプレートには、大きく効果検証設計と検証結果を書くパートの2つを用意しています。以下の画像は効果検証設計パートのテンプレートの一部です。

比較の手法には、A/Bテスト、DID、目標値との比較といった手法が入ることを想定しています。最後のScenarioは、設定したMetricsの動きによって施策担当チームの次のアクションがどう変わるのかを記入します。

このScenarioを事前に書くことによって、どのようなMetricを見るべきかが明らかになり、またそれらのMetricを計測するための手段が逆算されるはずです。  

テンプレートの一部

3. 他アナリストや他チームへの説明の実施

最後に「他アナリストや他チームへの説明の実施」に関してです。作ったデータベースやテンプレートを展開するため、他のアナリストや、マーケティング担当の部署などに資料を作って説明を行いました。概ね好評で受け入れられるまでのハードルは少なかったです。

結果

データベースを作って3ヶ月ほど経ちました。現状約10個ほどの施策チームがこのデータベース・テンプレートを利用して効果検証の設計を行っています。またアナリストからも、効果検証の設計をPdMとやりやすくなったといった声をもらっています。

残課題

残課題は2つほど明確なものがあると思っています。

1つ目は、効果検証設計のテンプレートの不十分さです。現状は受け入れやすさを重視し、意図的に効果検証のテンプレートをシンプルにしています。しかしながら、A/Bテストなどでは他にも設定をしないといけない項目はまだまだあるはずです。

2つ目は、検証結果を横断したメタ分析ができる体制になっていないことです。検証結果をチーム横断でまとめているので、過去どういった施策が当たりやすかったのかといったメタ的な分析もやりやすくなるはずです。しかしながら現状こういった分析に耐えうる設計はデータベースに表現されていません。

最後に

今回は、タイミーにおける効果検証設計に関して記載しました。弊社では分析自体だけではなく、今回のような分析をより活用するための仕組みづくりも沢山行っております。

We’re Hiring!

タイミーでは、一緒に働くメンバーを募集しています。

https://hrmos.co/pages/timee/jobs

カジュアル面談も行っていますので、少しでも興味がありましたら、気軽にご連絡ください。

Reference

A/Bテスト実践ガイド 真のデータドリブンへ至る信用できる実験とは

後編:YARD から rbs-inline に移行しました

タイミーでバックエンドのテックリードをしている新谷(@euglena1215)です。

この記事は先日公開した「前編:YARD から rbs-inline に移行しました」の後編となっています。前編では rbs-inline の紹介、移行の目的などをまとめています。前編を読んでいない方はぜひ読んでみてください。

tech.timee.co.jp

後編では実際の移行の流れや詰まったポイント、今後の展望について紹介します。

移行の流れ

YARD が日常的に書かれている状況から YARD がほとんど rbs-inline になり、YARD 関連のツールが削除されるまでの流れを紹介します。

1. 型をやっていくことを表明する

まずはバックエンド開発者に対してやっていく気持ちを表明しました。

YARD から rbs-inline への移行は自分1人で進めるよりは誰かに手伝ってもらったほうが自分ごとに感じられる方が増えると思い、表明と同時に手伝ってもらえる方を募集しました。

ここで、 @Juju_62q @dak2 の2名に立候補いただきました。ありがたい。

こんな感じで表明しました

2. rbs-inline のセットアップを行う

移行を段階的に進めるためにも、まずは rbs-inlne コメントを書いたらきちんと反映されるような状況を作ります。

タイミーでの型生成は pocke さんが作った便利 Rake Task rbs:setup をアレンジして使っています。そのため、 rbs:setup を実行することで rbs-inline による型定義も生成されるように変更しました。

また、タイミーでは sord gem という YARD コメントから RBS を生成するツールも使っています。rbs-inline はアノテーションがないメソッドにも RBS を生成するため、ただ生成しただけでは RBS が重複してしまいエラーになってしまいます。且つ、rbs-inline コメントが少なく YARD コメントが多い現段階においては YARD コメントによる RBS は捨てずに互換性を維持する必要がありました。

これらは sord gem の --exclude-untyped オプションを設定しつつ、 rbs subtract コマンドによって YARD → rbs-inline の優先順位で重複を削除することで解決しました。

--exclude-untyped オプションは名前の通り、YARD コメントがなく untyped にせざるを得ない RBS を生成結果から除外できます。ですが、 --exclude-untyped オプションもユースケースに完全に一致するものではなく、YARD コメントがない定数や initialize メソッドが untyped として生成されるので rbs-inline での記述が反映されない形になっていました。

定数が untyped になる問題は最終的に sord gem を削除するまでは解決しませんでしたが、YARD コメントがない initialize メソッドも生成されてしまう問題は以下のように生成されたファイルの中身を書き換えることで生成結果から強引に除外する対応を行いました。

  task sord: :environment do
    sord_path = 'sig/sord/generated.rbs'
    sh 'sord', '--exclude-untyped', '--rbs', sord_path
    Rails.root.join(sord_path).then do |f|
      content = f.read

      # sord は --exclude-untyped をつけていても initialize メソッドの型を出力するが、
      # rbs-inline を優先したいので削除する。
      content.gsub!(/def initialize:.*?(\n\s+.*?)*-> void/, '')

      f.write(content)
    end
  end

また、rbs-inline をセットアップした時点の rbs-inline 0.4.0 では ActiveAdmin 用のいくつかの実装にて rbs-inline コマンドがクラッシュする事象を観測していたので、rbs-inline の変換対象から除外していました。この辺りを soutaro さんにフィードバックしたところ、0.5.0 に入ったこの変更で修正してもらえました 🎉

社内でバグ報告できて便利

結果として、タイミーの RBS 生成ステップは以下のように変化しました。

Before

  1. rbs prototype で untyped ながらも全体の型を生成
  2. rbs_rails:all で Rails によって生成されたメソッドの型を生成
  3. sord で YARD から型を生成
  4. rbs subtract で 1. で生成された型に対して重複した型定義を除外

After

  1. rbs-inline で全体の型を生成
  2. rbs_rails:all で Rails によって生成されたメソッドの型を生成
  3. sord で YARD から型を生成
  4. rbs subtract で 1. で生成された型に対して重複した型定義を除外

rbs-inline が全体のRBSファイルを生成をしてくれるので、 rbs prototype によって型定義を生成するステップを削除しました。そのため、実質的にrbs prototype の上位互換として扱っています。rbs-inline に興味があってrbs prototype コマンドを使っているプロジェクトがあれば、とりあえず rbs prototype コマンドを rbs-inline コマンドに置き換えてみても良いのではないでしょうか。

 

また、このタイミングで開発ルールにも変更を加えています。

「新規で型アノテーションするときは YARD よりも rbs-inline を使うことを推奨する」ルールを追加しました。この時点ではコードベース上に rbs-inline によるアノテーションはほとんどなく、サンプルコードが少ないので義務ではなく推奨という形に留めています。(学んでみてほしくはありつつも、書き方分からないので rbs-inline ではなく YARD を書くことは許容する形)

3. YARD から rbs-inline への移行を進める

2.でrbs-inline 書き始められる状況を作れたので、ようやく YARD から rbs-inline への移行を進めていきます。

1.で手伝ってくれると立候補してもらった2名と一緒に方針を以下のように決めました。

「機械的に変換できる部分は機械的に変換していくが、自動変換を頑張りすぎない。手動で手直しした方が早い部分は手動で書き換える。コスパの良い方法を適宜選んで置き換えを進めていく」

また、変換スクリプトはメソッドに対するコメントを完全に変換可能なもののみ変換するという方針を取りました。

例えば以下のような YARD コメントが書かれたメソッドがあったとして

# @param x [String]
# @return [String]
def foo(x)
  x
end

YARD コメントの@return タグのみ変換できるスクリプトがあったとすると以下のように変換されます。

# @param x [String]
# @rbs return: String
def foo(x)
  x
end

この状態だと、YARD コメントから生成された RBS は (String) -> untyped になり、rbs-inline コメントから生成された RBS は (untyped) -> String になります。

今の rbs-inline コメントによる RBS の生成結果と YARD コメントによる RBS の生成結果だと YARD コメントが優先されるため、結果として (String) -> untyped が最終的な型になります。

元々 YARD コメントだけで記述していた際は (String) -> String と正しい型が定義できていたのに、YARD と rbs-inline が混在することで型情報が落ちてしまうことは意図したものではないため、完全に変換できるもののみ変換対象としました。

前述した方針より、YARD タグの全てをサポートしているわけではないためライブラリとしての公開は控えたいと思いますが、興味ある方向けにソースコードは公開します。興味があればご覧ください。

yard-rbs-inline-sample/tasks/yard_to_rbs_inline.rake at main · euglena1215/yard-rbs-inline-sample · GitHub

また、実行例として YARD アノテーションが多く記述されている yard gem に対して変換スクリプトを実行した結果も載せておきます。

github.com

コードを書かずにエディタの一括置換機能を使って移行したものもいくつか存在します。

  • yard-sig の記法 @!sig@rbs に置換
  • @example に対応する rbs-inline はないので NOTE: に置換
  • @see に対応する rbs-inline はないので refs に置換
  • @raise に対応する rbs-inline はないので Raises に置換
  • @deprecated に対応する rbs-inline はないので Deprecated に置換

数が少なく手動で手直しした例も挙げておきます。

  • YARD のタプル(e.g. Array(String, Integer))を使っている箇所を rbs-inline に修正
  • YARD の @option タグを rbs-inline で Hash のリテラルに修正
# @param [Hash] h
# @option h arg1 [String]
# @option h arg2 [Integer]
# @return [Integer]
def foo(h) = h.size

# @rbs h: { arg1: String, arg2: Integer }
# @rbs return: Integer
def foo(h) = h.size

これらの作業でコードベースからほとんどの YARD が rbs-inline のコメントに移行が完了しました 🎉

移行期間としてはサブタスクとして取り組んで1ヶ月半ほどでした。このタスクに集中すれば1~2週間で終わったのではないかと思います。

4. 後片付け

YARD コメントがほとんどなくなったので YARD 関連のツールを削除を始めとする後片付けを進めていきました。基本的にはスムーズに進んだのですが、進めていく中でハードルとなった点をいくつか紹介します。

sord gem の削除

YARD コメントから RBS を生成する sord gem を削除しようとしたところ、Data, Struct の型定義が見つからなくてエラーが発生しました。

sord が Data.defineStruct.new に対応する型定義を生成していたのに対し、rbs-inline は生成しておらず、sord 削除のタイミングでその問題が健在化しました。

対応としては、以下のように @rbs! を使って直接 RBS をコードベース上に手書きしました。

# @rbs!
#   class Foo
#     attr_reader bar: String
#     attr_reader baz: Integer
#   end

# @rbs skip
Foo = Data.define(:bar, :baz)

さすがにこれはちょっと辛いですという話を soutaro さんにしたところ、rbs-inline 0.6.0 で Data, Struct がサポートされました 🎉

github.com

Data, Struct が面倒なことをsoutaroさんに共有している図

Data, Struct がサポートされたことによって @rbs! を使う必要がなくなり、以下のようにシュッと書けるようになりました。めっちゃ便利…!

Foo = Data.define(
  :bar, #: String
  :baz #: Integer
)

rbs subtract をやめる

sord gem が削除されたことでアプリケーションコードから RBS を自動生成する方法が rbs-rails と rbs-inline のみになりました。rbs-rails は Rails 側が自動的に生成するメソッドに対して RBS 生成を行うのが目的で、rbs-inline は開発者が実装したメソッドに対して RBS 生成するのが目的です。

それぞれ RBS の生成対象が異なることから重複を吸収する必要はないだろうと考え、 rbs subtract をやめました。

rbs subtract をやめてみたところ、以下のコードでエラーが出るようになりました。

class User < ApplicationRecord
  has_one :profile
  
  after_create :create_profile
  
  private
  
  # create_profile メソッドの型定義が重複しているエラーが発生
  def create_profile
    ...
  end
end

ActiveRecord はアソシエーションで関連付けたモデルに対して create_xxx メソッドを動的に定義します。動的に定義されたメソッドとアプリケーションコードで定義したメソッドによる型定義が重複したことによるエラーでした。今回のケースでは意図的にメソッドの上書きをしていたわけではなかったため、本来は別名をつけることが望ましいパターンでした。

rbs subtract をやめたことで、意図せずメソッドを上書きしていた場合はエラーによって別名で定義できるようになりますし、意図的にメソッドを上書きしていた場合は @rbs override を記載することでその意図をコード上に残せるようになります。

必要なくなったから rbs subtract をやめようくらいの気持ちで消していましたが、これは思いがけない発見でした。

さらに細かい話になりますが、上記の重複エラーの参照先が rbs-inline ではなく rbs-rails 側になっていることに気付きました。なんでだろうと思って調べてみると、rbs-inline は sig/rbs_inline/ に格納していて rbs-rails は sig/rbs_rails/ に格納していたのですが、RBS はアルファベット順でファイルを読み込んでいくため sig/rbs_inline/ → sig/rbs_rails/ という順番に RBS を読み込んでいたことに起因するものでした。

なので、rbs-inline は sig/rbs_inline ディレクトリではなく、sig/z_rbs_inline ディレクトリに格納するように変更し、必ず rbs-rails の後に読み込まれるようにしました。

これらの取り組みによって、最終的に RBS 生成のステップが以下のようにシンプルになりました。

Before

  1. rbs-inline で全体の型を生成
  2. rbs_rails:all で Rails によって生成されたメソッドの型を生成
  3. sord で YARD から型を生成
  4. rbs subtract で 1. で生成された型に対して重複した型定義を除外

After

  1. rbs-inline で全体の型を生成
  2. rbs_rails:all で Rails によって生成されたメソッドの型を生成

今後の展望

やりたいと思っているものの、やりきれていないいくつかの点について共有します。

型検査を通す

今回の取り組みで rbs-inline を書いていく土壌は整いましたし、実際に書かれるようになってきましたが、型検査(steep check)を通すところまでは進められていません。これから始まる長い旅のスタート地点に立ったかなという気持ちです。

また、Rails プロジェクトに対して全てのディレクトリに対して型検査を通すようにすべきなのか、それとも特定のディレクトリだけで実施するのが妥当なのかの整理はできていません。これから検証含め進めていく必要があります。

リアルタイムな実装へのフィードバック

前編「RBS 活用推進の背景」で説明したように、実装のフィードバックサイクルを早めるためには rbs-inline のコメントを更新したらリアルタイムに RBS に反映され、その RBS を元にした型検査がエディタ上ですぐに走るのが理想だと考えています。

上記の型検査を通すだけでは理想の状況は実現できません。コーディング環境のセットアップを含む包括的な開発環境の提供を推進していく必要があります。

まとめ

RBS 導入の背景から YARD から rbs-inline への移行理由、移行方法、これからの展望について紹介しました。rbs-inline は experimental ではあるものの本番運用している会社がある事実があなたの会社 rbs-inline 導入への後押しになれば幸いです。

この辺りについてもっと話したい方はカジュアル面談でお話ししましょう!

product-recruit.timee.co.jp

前編:YARD から rbs-inline に移行しました

タイミーでバックエンドのテックリードをしている新谷(@euglena1215)です。

タイミーのバックエンドはモノリスの Rails を中心に構成されています。そのモノリスな Rails に書かれていた YARD を rbs-inline に一通り移行した事例を紹介します。
前編では、rbs-inline の紹介と rbs-inline への移行理由について触れ、後編では実際の移行の流れや詰まったポイント、今後の展望について触れる予定です。

rbs-inline とは

まずは rbs-inline について簡単に紹介します。

rbs-inline とは Ruby コードにコメントの形式で RBS を記述することで、対応する RBS ファイルを自動生成してくれるツールです。

github.com

README にあるサンプルコードを引用するだけになってしまいますが、以下の Ruby コードに対して rbs-inline コマンドを実行すると

# rbs_inline: enabled

class Person
  attr_reader :name #: String

  attr_reader :addresses #: Array[String]

  # @rbs name: String
  # @rbs addresses: Array[String]
  # @rbs return: void
  def initialize(name:, addresses:)
    @name = name
    @addresses = addresses
  end

  def to_s #: String
    "Person(name = #{name}, addresses = #{addresses.join(", ")})"
  end

  # @rbs &block: (String) -> void
  def each_address(&block) #:: void
    addresses.each(&block)
  end
end

以下の RBS ファイルが生成されるようになっています。

class Person
  attr_reader name: String

  attr_reader addresses: Array[String]

  def initialize: (name: String, addresses: Array[String]) -> void

  def to_s: () -> String

  def each_address: () { (String) -> void } -> void
end

サポートしている構文はこちらにまとまっています。
Syntax guide · soutaro/rbs-inline Wiki · GitHub

rbs-inline が作られた動機に関しては RubyKaigi 2024 での発表スライドを見てもらうのが一番かなと思います。

speakerdeck.com

RBS 活用推進の背景

まずは rbs-inline の前段階である RBS 活用推進の背景について紹介します。

メルカリがメルカリハロをリリースし、リクルートもスポットワーク業界への参入を表明するなどタイミーを取り巻く環境は激化の一途をたどっています。まさに戦国時代です。競合サービスと切磋琢磨し勝ち抜いていくために我々は1段階ギアを上げた開発をしていく必要があります。

そして、開発速度を高める方法はいくつかありますが、その中でも実装のフィードバックサイクルの高速化は良いアイデアの1つだと考えています。

タイミーを含む一般的な Rails アプリケーションの開発では、実装の検証はテストコードを用いた自動テストもしくは開発・検証環境での手動テストがほとんどなのではないかと思います。手動テストに一定の時間がかかるのは当然として、自動テストもそこそこのサイズの Rails アプリケーションでは数秒かかることは少なくありません。

それが仮にエディタ上でリアルタイムに静的な型検査という形でフィードバックが返ってくるとなるとどうでしょうか。もちろん手動・自動テストほど詳細なロジックミスは検知できませんが、多くのミスはちょっとした NoMethodError など型検査で気付けるものが大半です。

これまで数秒かけて気付いていたことにリアルタイムで気付けるようになれば、開発速度の改善には間違いなく寄与するはずです。

また、前提としてタイミーは元々 YARD コメントを書く文化があり、YARD コメントから RBS を生成する sord gem を使って RBS を補完用途で導入していました。詳しくは以下の資料をご覧ください。

tech.timee.co.jp

移行理由

RBS を活用し、型検査をしていくぞ!というのは前述の通りです。
一方、sord gem を使うことで YARD から RBS の生成はできていました。それでも rbs-inline に移行した理由は以下の通りです。

1. YARD(sord) よりも rbs-inline の方が表現力が高い

YARD(sord) では interface や type などの表現ができません。rbs-inline では@rbs! を使えば記述できます。

# RBS
class X
    type name = String | Symbol
    def foo: () -> name
end

# Ruby with YARD
class X
    # (String | Symbol) は表現できるが type を使った alias は表現できない
    # @return [String, Symbol]
    def foo = ['foo', :foo].sample
end

# Ruby with rbs-inline
class X
    # @rbs!
    #   type name = String | Symbol

    # @rbs () -> name
    def foo = ['foo', :foo].sample
end

2. YARD は書いていたが yardoc は使っていなかった

これは社内の事情ですが、YARD をコードリーディングを手助けするドキュメンテーションツールとしてしか使っておらず yardoc を用いてドキュメントページの生成はしていませんでした。(正確には一時期生成していましたが、誰も見ていなかったので生成をストップしました。)

同様の YARD を使っているプロジェクトであっても、yardoc を活用しているプロジェクトは yardoc 相当の挙動を rbs-inline で実現するツールを自作するか、yardoc によるドキュメント生成を諦めるかの判断を迫られることになります。

3. rbs-inline が今後言語標準の機能になっていく

rbs-inline gem は今後廃止されて rbs gem に統合される予定です。
rbs gem は Ruby 標準の機能なので rbs-inline も Ruby 標準の機能になるはずです。なので、今のうちに乗り換えておいて損はないだろう、という魂胆です。

また、開発者が社内にいる soutaro さんというのも大きなポイントでした。

productpr.timee.co.jp

rbs-inline はまだ experimental なので仕様が不安定という側面はあるものの、逆に考えるとフィードバックをすれば受け入れてもらう可能性が高いということでもあります。標準機能になるのなら、社内にいる soutaro さんに今のうちにフィードバックしておくことで我々のユースケースで困りにくい形で仕様が確定するといいなと思っています。*1

 


 

前編では rbs-inline の紹介、移行の目的などを紹介しました。このまま肝心の「実際どうだったのか」もお伝えしたいところですが、長くなったので一旦ここで一区切り。後編では実際の移行の流れや詰まったポイント、今後の展望をまとめます。お楽しみに!

今回は RBS 活用によって開発速度を向上させる作戦を取りましたが、開発速度向上には色んな方法があると思っています。各社がどんなアプローチで取り組まれているのかはとても興味があります。カジュアル面談でお待ちしています!

product-recruit.timee.co.jp

追記:後編を公開しました。

tech.timee.co.jp

*1:事実として我々のフィードバックによってバグ修正や新たな構文サポートが行われました。詳しくは後編で紹介します

スクラムで品質を上げ続けるために完成の定義(Definition of Done)を作りました

読んで欲しいと思っている人

  • POやステークホルダーと品質について共通言語や目標が欲しい開発者
  • 開発者と品質について共通言語や目標が欲しいPO
  • スクラムで品質について困っている人

読むとわかること

  • 完成の定義(Definition of Done)とはどんなものか
  • スクラムと非機能的な品質の関係性
  • タイミーのWorkingRelationsSquadでどんな完成の定義を作り、活用していきたいと思っているか
続きを読む