TECH PLAY

Findy/ファインディ

Findy/ファインディ の技術ブログ

165

こんにちは、ファインディ株式会社で機械学習エンジニアをしていますsasanoshouta( @Edyyyyon )です。この記事は、ファインディでインシデントが発生した際に行なっている ポストモーテム の運用とその様子について、先日発生したインシデントを元に紹介をする記事となっています。 今回発生したインシデントについて まず、今回発生したインシデントについて軽く紹介をさせていただきます。一言で表現すると、 サービスの機能の1つを一時的に停止 させてしまいました。 ポストモーテムの様子 弊社ではインシデントが発生した際に ポストモーテム を実施して再発防止に努めております。 ポストモーテムとは? そもそもポストモーテムとはなんだ?と言う方もおられるかもしれませんので、簡単にご紹介いたします。 ポストモーテムは、インシデントとそのインパクト、その緩和や解消のために行われたアクション、根本原因(群)、インシデントの再発を避けるためのフォローアップのアクションを記録するために書かれるものです。 *1 一般的にポストモーテムを実施するには一定の基準があり、基準を超えた障害が起きたときに実施するものです。 しかし弊社ではまだポストモーテムに対する知見や歴史が浅く、文化として根付かせるために、現在は練習も兼ねて小さい障害でもポストモーテムを実施するようにしています。 ポストモーテムの流れ 今回のインシデントを例に、ポストモーテムの流れを見る形で実施の様子をご紹介します。弊社の場合のポストモーテムは、以下の順序で実施されます。 当事者が事前に障害までのアクションを振り返る ミーティングで集まりアクションについてのフィードバックを実施する ネクストアクション決定とウォッチ 1. 当事者が事前に障害までのアクションを振り返る この手順について特筆する事はありませんが、弊社の場合はGoogle docsにてインシデントごとに管理をしているので、以下画像のように主催者からdocsの案内が来たタイミングで覚えている事が新鮮なうちに時系列での出来事の詳細を記載しています。 ポストモーテム実施前の連絡 2. ミーティングで集まりアクションについてのフィードバックを実施する 以下画像のような形で、事前に当事者が書き記した時系列でのイベントを振り返りながら、各イベントに関連するアクションの中で良かった点と改善点について参加者で話し合います。 ポストモーテム実施時の雰囲気例 少しだけインシデントの事について触れますが、「機能が利用不可になっている事が発覚してから復旧までをスムーズに行えた」と言う振り返りがありました。当日はゴールデンウィークの狭間時期と言う事もあり、本来のバックエンド担当者が不在の間に起きた出来事でもありました。しかし、日頃からサービス上で提供している機能のIaC化をコツコツ進めていた事で担当者不在の中でもスムーズな復旧対応が行えました。 3. ネクストアクション決定とウォッチ 2 で行なったイベント・アクションに対する振り返りをもとに、ネクストアクションを決定します。 以下は今回のインシデントにおけるネクストアクションにもなります。今回起きた事は、機能開発者が開発に必要なサービスとその権限をバックエンド運用者と 十分な意思疎通をしきれなかった事で起きたインシデント だと捉えています。ヒューマンエラーを起こしてしまった開発者目線のネクストアクションを今回は以下のように定めました。 開発に着手する前に管理者側と十分に意思疎通を図れるフローを制定し、運用する フローの中で定める項目 開発したい要件について運用者に事前共有する 開発の為に必要最低限の適切な権限を要求する 上記フローをポストモーテム実施後3日以内に作成し、次回週次の全体確認会で案内。 また、運用者目線でも開発時に「 今自分がどちらの環境を開いているのか目視で分かりづらいのでは 」との意見もあり、「 どの環境にいるのか分かりやすくする為の視覚情報を導入 」する事で同様のヒューマンエラーを防ぐ仕組みを導入する事になりました。 ポストモーテム後のネクストアクションを作成はしたものの、形骸化するものも中には少なからず存在すると思います。 なので、弊社ではネクストアクションを議論する際に意識するポイントとして、 具体性を持たせて明確なアクションにすること それを定期的にウォッチして進んだのか進んでないのかを確認し続けて放置しないこと の2点を常に意識してポストモーテムを実施するようにしています。 余談ですが、弊社では全エンジニアが直近の取り組みを持ち回りで定期的に共有する全体会のようなものを設けています。 その全体会の場でポストモーテムの内容と得られた知見を包み隠さず共有し、エンジニア組織全体の共有知見とすることを文化としています。 ネクストアクションにはインシデントを受けてどんな対策を打つか?を考える事に焦点が集中しがちだと思いますが、「当事者でないエンジニアも含む組織全体に対して共有する」という事も再発防止策の一つとして機能している事を実感しています。 ポストモーテム実施時に気をつけている事 上記弊社でのポストモーテムの様子を実際のインシデントを例に紹介しました。最後に、実施の際に気をつけている事について共有します。 犯人探し、批判をしない ポストモーテムで最も重要視している項目です。 ポストモーテムの目的は学びにあり、犯人探し、批判を行うとそれを恐れ当事者が真実を語らなくなり、その背後にある本当の原因に気づくことができなくなる為です。 批判ではなく、 根本原因の追求と分析に徹する 事を心がけるようにしています。 障害の根本原因を分析し、理解する 原因の分析が不十分だと、誤った再発防止策になり、結果として同じ障害が再発することに繋がりかねません。 客観的な視点で、 原因がシステムやプロセスにあることを意識して分析をする ようにしています。 人ではなく、仕組みで解決する 再発防止策が頑張る、気をつけるだけだと、再発防止を人に依存することになりますが、人はミスをする生き物です。 システムやプロセスを修正して、 人がミスしても被害を最小限にくい止める ように努めるようにしています。 問題点だけでなく、良かった点も指摘、称賛する 犯人探し、批判はご法度ですが、良かった点があれば何回でも称賛する事も大切にしています。 さいごに 改めまして、ご不便をおかけしましたユーザーの皆様に深くお詫びをするとともに、再発防止に努めてまいります。 今回のポストモーテムを通じて、開発を進める上で足りていなかった観点・過剰だった点を浮き彫りにする事ができたことは非常に個人として学びになりました。 この学びを活かして、より良いサービス創りに貢献できるよう個人・組織として精進してまいります。 *1 : ※ SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム より抜粋
アバター
こんにちは。 Findy で Tech Lead をやらせてもらってる戸田です。 早速ですが、これは弊社のとあるチームの1ヶ月のサイクルタイムです。 最初のコミットからマージされるまで平均3.6時間程度と、開発に着手したらその日のうちにリリースされるのがデフォルトとなっています。 今回はこの開発スピードを継続し、更に速くするために弊社で実践しているテクニックを紹介していきます。 それでは見ていきましょう! タスク分解 Pull requestの粒度 テスト CI/CD 高速化 自動化 通知 まとめ タスク分解 開発タスクをアサインされた時、まず最初に タスク分解 をします。 タスク分解をすることによるメリットとしては、 工数見積もりの精度が上がる 対応方針の認識を他メンバーと合わせやすくなる 対応漏れに気づきやすくなり、手戻りの発生が少なくなる Pull requestの粒度を適切に保つことができる 他メンバーへの引き継ぎをしやすくなる などが挙げられます。 分解したタスク毎にPull requestを作成することで、Pull requestを小さく作り続けることが可能になります。 マークダウン記法のチェックボックスを使ってIssueにタスクリストを洗い出します。 終わったタスクから順にチェックボックスにチェックを入れ、タスク管理をします。 良いタスクリストを作成するためのコツは、 最初から完璧なタスクリストを作ろうとしない ことです。 まず最初に大枠のタスクを考え、そこから分解していくように意識してタスクリストを作成すると、結果的に詳細なタスクリストが完成しています。 完成したタスクリストを元にPull requestを作成していくと、結果的に適切な粒度でPull requestを作成できるようになるはずです。 例えば、何かしらのデータの一覧を返すREST APIを追加する場合、次のようなタスクに分解できます。 - [ ] APIの仮実装を行う - [ ] APIのエンドポイントを決める - [ ] APIのresponseの形を決める - [ ] モックデータを返す - [ ] データベースからデータを取得して返す - [ ] 検索条件に対応する - [ ] id - [ ] nameの部分一致 - [ ] ソートに対応する 小さくコツコツと修正を上乗せしていき、最終的に完成形に近づけていくような分解の仕方が良いでしょう。 大きな機能の実装担当になった際に、一度に全ての機能を実装するのではなく、 小さい機能追加を何回も繰り返して結果的に大きな機能を完成させる ようなイメージを持つと良いです。 まずタスク分解をして作成したタスクリストを他のメンバーにレビューしてもらいます。そこで認識を合わせることで、開発の進行がスムーズになります。 分解したタスクに沿って開発を進めていくと、そのタスクも更に分解したほうが良いことに気づくこともあります。 その際はどんどん分解していきます。結果的に分解されたタスクそのものが知見となり、今後の開発の参考になるからです。 Pull requestの粒度 Pull requestを小さくすることで開発生産性が改善することは既に知られている事実ですが、小さすぎても問題が出ます。 じゃあどうするのか?という話になりがちですが、これはPull requestの サイズ を気にしすぎていて 粒度 を考えていないために起こる問題です。 Pull requestを 小さくするのではなく、粒度を考える ことが、小さいPull requestを作り続けるための秘訣です。 では適切な粒度とはどういったものなのでしょうか?それは、 一つのことだけに注力している Pull requestです。 具体的に幾つかの例を挙げましょう。 例えば、「関数名を変更したので、それを利用してる1万行を一括置換したPull request」があるとします。 これは適切な粒度だと言えます。変更行数は1万行でサイズは大きいかもしれませんが、「特定の関数名を一括置換する」という1つのことしかしていないため、粒度としては適切です。 Pull requestの概要欄に一斉置換した旨を書いておき、CI通れば即mergeでOKです。 ※後述するように自動化テストが充実しており、守れるテストになっている前提です では「画面の開発中に別の画面のリファクタを入れ、変更行数は20行程度だったPull request」はどうでしょうか? これは適切な粒度とは言えません。変更行数は20行でサイズは小さいかもしれませんが、「画面の開発」と「別の画面のリファクタ」という2つの事を同時にしているため、粒度としては不適切です。 仮に画面の開発の部分で不具合が発生しrevertが必要になった場合、画面の開発部分だけではなく別の画面のリファクタの部分も同時にrevertされてしまいます。 適切な粒度を考える上で、 Pull requestの存在意義が多岐に渡っていないかどうか? ということを考えると良いです。 粒度が大きすぎると出てくる問題は色々とありますが、 レビューに時間が掛かり、レビュワー目線で考えるとレビューに対して精神的に負荷がかかる どこをどうレビューしたら良いのか分かりづらいため、レビューの質が下がり、結果的に不具合の発生率が高くなる Pull request上でのコミュニケーションが必要以上に増えてしまう 不具合発生時の影響範囲が広くなり、原因の特定に時間がかかる etc などが挙げられます。 Pull requestが大きすぎること自体の原因は組織によって異なりますが、 Pull requestの粒度が大きすぎることは、システム開発において何のメリットも生み出さない のです。 適切な粒度を維持し続けることにより、Pull requestのレビューに対する負担が減るためレビュー自体の質が上がることに加え、レビューの優先度が上がることに繋がり、結果的に開発スピードと品質の両方を担保できます。 テスト 実装コードに加えて、それの動作を保証するテストコードを同じPull request内で用意することをマストとしています。 実装コードよりもテストケースやテストの内容に対するレビューの方が多いこともあります。 弊社ではテストのカバレッジに対しての大きな拘りは特にありませんが、カバレッジよりも重要視していることがあります。 それは、そのテストが「守る」テストになっているかどうかです。通るべき時に通り、コケるべき時にコケるテストになっているかどうかが重要なのです。 例えば正常系の次のようなテストコードがあるとします。 const mockOnSuccessCallback = jest.fn (); const mockOnErrorCallback = jest.fn (); const { result } = renderHook (() => useHoge ( { onSuccessCallback: mockOnSuccessCallback , onErrorCallback: mockOnErrorCallback } )); act (() => { result.current.handleSubmit (); } ); expect ( mockOnSuccessCallback ) .toHaveBeenCalledWith ( 'test' ); hooksの関数を実行し、成功時に引数で渡したコールバック関数が実行されることを確認しています。 一見何の変哲もないテストコードですが、このテストコードには次の視点が抜けており、守るテストとしては不十分なのです。 成功時の関数が複数回実行されたとしても通ってしまう エラー時のコールバック関数が実行されたとしても通ってしまう こういったケースでは、弊社では次のようなテストコードが良いとされています。 const mockOnSuccessCallback = jest.fn (); const mockOnErrorCallback = jest.fn (); const { result } = renderHook (() => useHoge ( { onSuccessCallback: mockOnSuccessCallback , onErrorCallback: mockOnErrorCallback } )); act (() => { result.current.handleSubmit (); } ); expect ( mockOnSuccessCallback ) .toHaveBeenCalledTimes ( 1 ); expect ( mockOnSuccessCallback ) .toHaveBeenCalledWith ( 'test' ); expect ( mockOnErrorCallback ) .not.toHaveBeenCalled (); このテストコードにより、「成功時に1回だけコールバック関数がパラメータ付きで実行され、エラーのコールバック関数が実行されない」ことを守ることができます。 この2つのテストコードのカバレッジは変わりませんが、テストとして守ることができる内容が異なります。 弊社ではカバレッジよりもテストが守る内容の方に重きを置いており、結果としてカバレッジが上がっているだけなのです。 弊社のリポジトリではテストカバレッジが90%を超えているものも珍しくないですが、それはカバレッジを意識してテストを書いたのではなく、守るテストを書き続けることによって勝手に上がったものなのです。 このような一定の品質以上のテストコードが存在することにより、ライブラリのバージョンアップや既存処理のリファクタなどは「CI通れば即mergeでOK」という文化が根付いています。 全ての修正に対して動作確認を行うことは無く、テストコードのお陰でスピードと品質の両方を担保する状況を維持し続けることを実現しているのです。 CI/CD 弊社のCIのほとんどはGitHub Actionsで統一しています。 弊社ではPull requestが作成されるたびにテストやLinterなどを実行し、レビューの効率化やコードの品質を一定に保つようにしています。 高速化 CIの速度には非常に拘っており、様々な高速化の工夫をしています。CIの実行速度が遅いと、Pull requestをmergeするまでの時間が長くなってしまい、結果的にクリティカルパスになってしまうからです。 1つ目はキャッシュの活用です。GitHub Actionsが提供している各種パッケージをキャッシュする仕組みを利用することで、CIのセットアップに必要な時間を削減することが出来ます。 弊社ではいくつかのプロジェクトに Nx を導入しており、Nxが持つ 変更検知機能 と リモートキャッシュ機能 によってCIを大幅に高速化しました。 2つ目はテストコードの実行を並列化することです。 GitHub Actionsのmatrixと呼ばれる機能 を利用し、CIのワークフローそのものを並列実行できるようにしています。 テストファイルをいくつかのグループに分類し、それぞれのワークフローにグループごとのテストファイルを割り当てる独自のスクリプトを用意しています。 1つのワークフロー内で全てのテストファイルを実行してしまうと、テストファイルの数に比例してCIの実行時間が長くなっていきます。 しかしグループ分けをしてワークフローを並列実行することで、CIのトータルでの実行時間はほとんど変わりませんが、CIが完了するまでの時間を一定に保つことが出来るようになります。 詳細は 別記事 の方でも紹介しているので、興味がある方はそちらもご覧ください。 3つ目はGitHub Actionsのワークフローが実行される runnerのスペックを上げる ことです。 runnerのCPUのコア数やメモリを増やしたマシンを利用できるように設定し、テストコードの実行コマンドを見直して、同じワークフロー内でもテストコードを並列実行できるようにしています。 つまりワークフローそのものと、ワークフロー内の両方の並列化を実現しています。これにより、弊社では最大で40個のテストファイルを並列実行しているリポジトリもあります。 runnerのスペックを上げることにより利用料金の大幅な増額が予想されますが、GitHub Actionsの課金はrunnerの総実行時間に対して行われており、ワークフロー自体の実行が高速化される分、結果的に総実行時間が短くなり、課金額が必要以上に膨らんでしまうことを防いでいます。 自動化 CI高速化以外にも業務の効率化のため、ほぼ全てのプロジェクトにリリース作業を自動化する仕組みを取り入れています。GitHub Actionsのワークフローを手動実行するだけで、リリース用のPull requestが自動生成され、それをmergeするだけで自動的にstaging/production環境へのデプロイが実行されるようになっています。 更にPull requestのLabelsやAssigneesを自動で設定するようにもしています。 このように手間とコストを掛けてでもCI/CDの高速化と自動化の両方を実現させています。 通知 何かしらの異変、変化、依頼があった際に、そのタイミングでSlackに通知が飛ぶようになっています。 まずエラーや障害の検知です。本番環境でエラーや障害、各種負荷の増加が発生した際に、SentryやDatadogを通じてSlackに通知を飛ばすようになっています。この仕組みによって不具合や障害に最初に気づくまでの時間が短縮され、修正コードを本番環境にデプロイされるまでの時間が短縮されます。 次に開発時の通知です。GitHubのWebhookを通じて、Issueが作られた時やコメントが追加された際にSlackにメンション付きでリアルタイムで通知するようにしました。 特に効果が大きかったのはPull requestのレビュー依頼をメンション付きで通知することです。この仕組みによりファーストレビューまでの時間が短縮され、結果的にPull requestがマージされるまでの時間が短縮されました。 本来であればGitHubの公式APPを利用して通知を送信すればよいのですが、弊社では独自に開発したスクリプトを利用しています。 公式APPは通知内容の情報も入ってくるので一見すると便利ですが、弊社の開発スピードだと通知が多すぎて、逆に通知の内容が流れすぎてしまうことがありました。 そのため、もっとシンプルな内容を通知してくれる独自スクリプトを用いて通知を送信しています。 不具合や障害、レビュー依頼もコメントも、気づかないとそこから何も動きがありません。気づかないことがクリティカルパスになるのです。 まとめ いかがでしたでしょうか? 弊社では開発生産性や開発スピードに重きを置いており、そのためには手間とコストを惜しまず日々改善を続けています。 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers また、6月28日(金)・29日(土)に『LeanとDevOpsの科学』の著者であるNicole Forsgrenの来日、テスラ共同創業者元CTOの登壇など、国内外の開発生産性に関する最新の知見が集まるConferenceを開催します。 開発生産性に関する他の企業の取り組みや海外の事例に興味がある方は、ぜひお申し込みください! dev-productivity-con.findy-code.io
アバター
こんにちは。ファインディでVPoEをしている 神谷 です。 沖縄、楽しかったですね、 観光、しましたか? 私は龍が如く3の世界観を感じようと最終日に国際通りを1時間ほど散策しました。(そしてすぐに飛行機の時間・・・ ) 龍が如く8の名所巡りもしたいので、再来年のRubyKaigiは是非ハワイ開催にならないかな・・・ この記事では、RubyKaigi 2024に参加した皆さんがそれぞれ感じた、 「 Rubyを書くことが楽しい 」 「 Rubyコミュニティに参加することが楽しい 」 これらの想いが将来のキャリアに絶対につながる、という話を、普段採用に関わる私の目線で書こうと思います。 もちろんRubyKaigi不参加の方でも、将来のキャリアに悩んでいる方に読んでいただきたい内容です。 筆者はどんな人? 私のRuby歴はRailsがバージョン1.0の時代からなので、18年ほどになります。 RubyKaigiは2014年に初参加したのを覚えています。 ファインディでは2018年からスポンサーをする機会があり、2022年から3年連続ブース出展をしています。ファインディブースで話して頂けた方々、ありがとうございます。 ファインディのブースの取り組みは、DevRelの まっきー がnoteに公開しているので、そちらを参考にしていただけると嬉しいです。 note.com 普段の業務はVPoEとして組織作りがミッションであり、採用はその中心となります。 2023年では私1人の面談・面接の回数が1年間で450を越え、入社予定も含めると40名規模のエンジニア組織になりました。お時間頂いた皆様、ありがとうございます。 その中で、Rubyistとの面談も3割ほどはあるのですが、RubyKaigi参加したことありますか?という質問に対して、参加していますと回答されるのは体感10%ほどです。 ご家庭事情や業務都合で参加できない方も多いと思いますが、今回のRubyKaigiに初参加の方も多数いらっしゃいました。初参加の方に参加理由を聞いてみたところ、「スポンサー企業に転職したのでついに参加できました」という方も何名かいらっしゃいました。 1000人以上のキャリアと向き合った中で感じること RubyKaigiのようなカンファレンスに参加されない方が多い中で感じる課題感となりますが、経験年数が長い方でもRailsのソースコードを読む経験が一度も無いという方や、gemのソースコードを追った経験が無い方も多くいました。Rubyは業務で使う言語なので、プライベートでは書かないようにしています、と宣言される方もいました。 私が面接の中でよく聞く質問として、「 メタプログラミングの中で好きなテクニックはありますか? 」があります。 この質問に対する回答として 「先輩からメタプログラミングは絶対に使ってはいけないと教わったので、それ以来触っていません」 「Ruby始めた頃に使ってはいけないと教えられました。それ以外、業務で使うことが無いので、特に勉強する機会がありませんでした」 このような回答が多いです。 メタプログラミングは使い方を誤ると後で苦労することは実際にありますが、だからと言って勉強しないと決めてしまうのは非常に残念です。 技術として難しくもあるが楽しいものであり、理解が深まるたびにプログラミングを好きになる可能性もあると思っています。 Railsや様々なgemにはこの技術が有効利用されており、知らないとソースコードを読むのも非常に苦労するでしょう。 逆に、シンプルにプログラミングや技術を楽しんでいる方とお話するのはこちらも嬉しい気持ちになります。 余談:「 okuribito 」gemの開発者であり弊社社員の @shakemurasan さんと面接した時は、 method_missing の使い方のディスカッションが中心となりました。めちゃめちゃ楽しかったです。 そんな彼の記事はこちら↓ tech.findy.co.jp 弊社で活躍しているエンジニア、私が過去にご一緒した素晴らしいエンジニアの方々、全員に共通して言えることは、仕事とか関係なく技術そのものを楽しみながら探求している方が多いなと感じています。 楽しめることによって技術力が向上し、その技術力を使って課題解決し、顧客に価値提供していくという良いスパイラルが生まれているのではないでしょうか。 RubyKaigi 2024で感じたこと 今回のRubyKaigi 2024で感じたことは、裏テーマとして「 Ruby楽しもうぜ 」があったのかな?と思うくらい、Rubyや技術を全力で楽しんでいる方々の登壇が多かった印象です。 特に初日の @tompng さんの「 Writing Weird Code 」のキーノート。 最後に感動すら待っている、非常に素晴らしいキーノートでした。 何をやっているか全く分からなかったが(褒め言葉)、Rubyが楽しい言語だということが分かった、そう感じた方も多くいたと思います。 普段業務で使うプログラミング言語の1つ、多くの方にはそれだけのことかもしれませんが、言語そのものを楽しめることによって、エンジニアとしての成長速度が早くなると思っています。楽しめている方と楽しめていない方、長期スパンで見れば見るほどこの差は大きくなるのではないでしょうか。 また、コミュニティに参加することによって自分自身のスキルの立ち位置も把握しやすくなり、スキルの立ち位置を把握することこそが、スキルアップの近道だと思っています。 Ruby/Rails書籍の著者の方やgemの作者、コミッターの方々、本当に様々な方と(drink upなどを通して)お話する機会が多くあり、それがモチベーションにもつながっていきます。 「 Rubyを書くことが楽しい 」 「 Rubyコミュニティに参加することが楽しい 」 これらのシンプルなことが今後のキャリア形成において非常に重要なのでは、とあらためて感じたのでこのような記事にしてみました。 将来のキャリアに悩んでいる方、初心に立ち返り、自分が技術を楽しめているか振り返ってみるのも良いかもしれません。 最後に 5/28(火)に、「After RubyKaigi 2024〜メドピア、ZOZO、Findy〜」として、メドピア株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2024の振り返りを行います。 オンライン・オフラインどちらもあり、LTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!! findy.connpass.com ファインディでは、これからもRubyを積極的に活用して、Rubyとともに成長していければと考えております。 そして、一緒に働くメンバーを絶賛募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
ハイサイ、ファインディでTeam+を開発しているEND( @aiandrox )です。 RubyKaigi 2024最高でしたね!!私は2度目の参加でしたが、去年よりもみんなが笑っているところで笑えるようになり、各種イベントなどでいろんな方と話すことができたのでさらに楽しめました。 今回特に印象に残ったのは初日のKeynoteの「 Writing Weird Code 」でした。 「なるほどわからん」という感じで、正直半分以上わからなかったです。ただ、描画されたコードが格好よくてめちゃくちゃ感動しました。何が起きているのかはわからなかったけど、なんか浪漫のようなものを感じました。 せっかくRubyKaigiでQuineというものを知ったのだから、自分にどこまでできるのかはわからないけどやってみたい!ということで、Quineに挑戦してみました。 Quine(クワイン)とは Quineとは、自分自身のソースコードを出力するプログラムのことです。 すごい見た目でなければいけないのかと思いきや、Quine自体にデザインはなく、 cat と実行時の出力結果が同一になるものをQuineと呼びます。 eval $s = %w' o="eval$s=%w"<<39<<$s<<39<<".join";puts(o) ' .join しかし、私がやりたいのは見た目が格好いいやつなので、今回はデザインQuineを作るのを目標にしました。 いざ実践 まずは何からすればいいのか?ですが、最初に読むには「 あなたの知らない超絶技巧プログラミングの世界 」が一番わかりやすかったです。とりあえずこれを読んで手元で実行しながら、ざっくりとQuineの概要と考え方を頭に入れます。半分くらいは理解できていませんが気にせず進めました。 その後、他の実装記事なども読みつつ実際にコードを書いてみました。 RubyでうどんげQuine(とAA型Quineの作り方講座) で、AAのQuineを作る方法が紹介されていたので、それを参考にしました。詳細な作り方はこちらの記事に書いてあります。 まず、使いたい画像をAAに変換します。今回はFindyのロゴを使用しました。 その後、形を整えつつAAを 01 に置き換え、Quineを作成するための build.rb を作成しました。 細かいロジックは参考元 *1 の記事にあるので省略しますが、簡潔に書くと以下の通りです。 圧縮したAAデータ bin を元に、空白またはコード1文字分を o に追加していく 最初に eval$s=%w' 、最後に '.join があるので、その中のコードは空白を削除されたうえでRubyコードとして実行可能 # build.rb aa = <<~ END 00000000000111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000001111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000111111111111111111100000000000001111111111111111100011111100000000000000000000000000000000000011111000000000000000000000 00011111110000000001111111000000000001111111111111111100011111100000000000000000000000000000000000011111000000000000000000000 00111111000000000000011111100000000001111110000000000000000000000001110000000000000000000000000000111110000111000000000111000 00111110011110000000000111110000000001111110000000000000011111100001111111111111110000000111111111111111000111111000001111110 01111111111100000000000111110000000001111110000000000000011111100001111111111111111100001111111111111111000011111000011111100 01111111111000000000000011110000000001111111111111110000011111100001111110000011111100011111110000111111000011111100111111000 01111111000000000000000111110000000001111111111111110000011111100001111100000011111100011111000000011111000001111111111110000 00111110000000000000000111110000000001111111111111110000011111100001111100000001111100011111000000011111000000111111111100000 00111111000000000000011111100000000001111110000000000000011111100001111100000001111100011111100000111111000000011111111000000 00011111111000000011111111000000000001111110000000000000011111100001111100000001111100001111111111111111000000001111110000000 00000111111111111111111111110000000001111110000000000000011111100001111100000001111100000111111111111111000000001111100000000 00000000111111111111100011111000000000111110000000000000001111100000111100000001111000000000111111001111000000011111100000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111000000000 END start_text = ' eval$s=%w ' end_text = ' .join ' aa_data = aa.split( "\n" ) x_length = aa_data.first.length y_length = aa_data.length last_point = aa_data.last.split( ' 1 ' ).last.length # 最終行に1がないパターンは考慮しない bits = aa.gsub( "\n" , '' ).reverse.to_i( 2 ) bin = [ Marshal .dump(bits)].pack( ' m ' ).gsub( "\n" , '' ) code = <<~ CODE b=" #{ bin } " n=Marshal.load(b.unpack("m")[0]) e=" #{ start_text } "<<39<<($s*3) o="" j=-1 0.upto( #{ y_length } * #{ x_length } -1){|i| o<<((n[i]==1)?e[j+=1]:32) o<<((i% #{ x_length } ==( #{ x_length - 1 } ))?10:"") } o[- #{ last_point + end_text.length + 2 } ,6]=""<<39<<" #{ end_text } " puts(o) CODE code = code.split( "\n" ).join( ' ; ' ) code << ' # ' file = File .new( ' quine_base.rb ' , ' w ' ) file.puts "#{ start_text } ' #{ code } ' #{ end_text }" これによって作成されたコードを実行すると、以下の出力を得られます。出力結果を quine.rb に記述して、 ruby quine.rb を実行すると、同じ結果になります。 これにてQuineの完成です🎉 eval $s = %w' b="BAhsK3oA+ AMAAAAAAAAAAAAAAAAA 8P8HAAAAAAAAAAAAA AAAgP/ /A4D/ //gBAAA A4AMAAP wB/AHw/x8/AAAAAHw AAMAPA H4Afg AAgAMA AMCHAz j4PAAf wA8 A/PD/ B/z /8Y Of/wP gA/g BgB/+ /8P/P3 z48T8A eAD/f/DDD3784Ye fH/4AgA/g/w9++M CPD/jg /4EPAP AB/P/BDx/w8 QEf+B /wA4Af gB8A+O EDPn7wA/4B/AP+AfA DAD98wIf/f4AfAP7 //wB+ AOCHD/ jg/w/wAQD+ Pz6A DwD44AEP4OcBPwA AAAAAA AAAAAA AAAAA8 AM=";n= Marsha l.load (b.unp ack("m" )[0]) ;e="eval$s=%w"< <39<<( $s*3) ;o=""; j=-1; 0.upt o(15*125-1){ |i|;o <<((n [i]==1)?e[j+=1] :32);o <<((i %125= =(124 ))?10 :"");};o[- 16,6]= ""<<39 <<".jo in";pu ts(o) #b="B AhsK3o A+AMAA AAAAAAAA AAAAAAA8 P8HAAAAA AAAAAA AAAAAg P//A4 D///g BAAAA4AMAAPwB/AH w/x8/A AAAAHwAAMAPAH4AfgAAgAMA AMCHAz j4PAAf wA8A/ PD/B/ z/8YOf/wPgA/gBg B/+/8 P/P3z48T8AeAD /f/DD D3784 YefH/ 4AgA /g/w 9++MCP D/jg /4EPAP ' .join ここまではわりとスムーズにできました。 eval$s=%w' から '.join' まではRubyコードなので、 # の後はコメントアウトとして好きな文字を入れることができます。例えば、 code 変数内の e を変更すると、 puts(0) 以降を # で埋めることができます。 e= "#{ start_text }" << 39 <<( $s + " # " * 500 ) eval $s = %w' b="BAhsK3oA+ AMAAAAAAAAAAAAAAAAA 8P8HAAAAAAAAAAAAA AAAgP/ /A4D/ //gBAAA A4AMAAP wB/AHw/x8/AAAAAHw AAMAPA H4Afg AAgAMA AMCHAz j4PAAf wA8 A/PD/ B/z /8Y Of/wP gA/g BgB/+ /8P/P3 z48T8A eAD/f/DDD3784Ye fH/4AgA/g/w9++M CPD/jg /4EPAP AB/P/BDx/w8 QEf+B /wA4Af gB8A+O EDPn7wA/4B/AP+AfA DAD98wIf/f4AfAP7 //wB+ AOCHD/ jg/w/wAQD+ Pz6A DwD44AEP4OcBPwA AAAAAA AAAAAA AAAAA8 AM=";n= Marsha l.load (b.unp ack("m" )[0]) ;e="eval$s=%w"< <39<<( $s+"# "*500) ;o="" ;j=-1 ;0.upto(15*1 25-1) {|i|; o<<((n[i]==1)?e [j+=1] :32); o<<(( i%125 ==(12 4))?10:"") ;};o[- 16,6]= ""<<39 <<".jo in";p uts(o )##### ###### ######## ######## ######## ###### ###### ##### ##### ################ ###### ####################### ###### ###### ##### ##### ############### ##### ############# ##### ##### ##### #### #### ###### #### ###### ' .join 色を付けてみる とりあえず、ほぼコピペをすることでQuineが作成できたので、次は色を付けてみたいと思いました。 eval の中は自由にコードが書けるので、わりと簡単にできるのでは?と思いましたが、そんなことはなく、かなり苦労しました。 こちらが今回作成した色付きQuineです。 実際のロゴと重ねるとこんな感じ。 コードはこちら↓ github.com 以下に、実際に作ってみて自分が感じた点について記載します。 データを圧縮するのが難しい このQuineでは、AAを成形するためのデザインコードと色付けロジックに関するコードを持っています。また、それらを描画するコードもあるため、すべてをQuine内に収める必要があります。つまり、 AAの字数 - (AAを描画するロジックの字数 + 色付けロジックの字数 + 描画コードの字数) > 0 にしなければなりません。 Findyロゴをできるだけ大きくすることでコード文字数を確保し、色付けロジックをstringにして実行コード内でeval展開することで対応しました。 irb(main):018> colors_bin = [ Marshal .dump(colors)].pack( ' m ' ).gsub( "\n" , '' ) => " BAhbFG86ClJhbmdlCDoJZXhjbEY6CmJlZ2luaRI6CGVuZGkCZAFvOwAIOwZGOwdpAvQBOwhpAggCbzsACDsGRjsHaQKUAjsIaQKoAm87AAg7BkY7B2kCPgM7CGkCUgNvOwAIOwZGOwdpAuMDOwhpAvwDbzsACDsGRjsHaQKKBDsIaQKcBG87AAg7BkY7... " irb(main):019> colors_text = colors.to_s.gsub( ' ' , '' ).gsub( "\n" , '' ) => " [13..356,500..520,660..680,830..850,995..1020,1162..1180,1328..1345,1495..1510,1660..1675,1825..1840,1990..2005,2155..2170,2320..2336,2490..2506,2655..2669] " irb(main): 020 > colors_bin.length => 408 irb(main): 021 > colors_text.length => 156 # 252字の削減! 「Ruby Committers and the World」で `frozen string literal の話題のときに mametterさん が話していた「1byteを切り詰めている」とはこういうことかと思いました。 また、AAロジックをQuine内で持つことで、AA成形コードを削減することができるようですが、これについてはどうすればいいのかわからず断念しました。 文字コードの意識が難しい 出力する文字列を、実行コード・出力両方として扱う関係上、そのまま使うことができない文字もあります。そういったものは、ASCIIコードを使うことでエラーを回避します。 例えば、 "\e[34m" などのエスケープシーケンスを使うことで出力文字に色を付けることができます。 しかし、 \e をそのまま記述すると文字列として解釈されます。そのため、生成されたQuineを実行するとそのまま文字列が出力されてしまいます。 (1敗) しかし、 27.chr で "\e" と同値を出力することができます。 *2 ちなみに、 #chr で文字列に変換しているのは + メソッドを使うためなので、 << で文字連結する場合は文字コードの整数のままで問題ありません。 エスケープシーケンスを追加することにより、細かい位置の調整が難しくなる $s に代入する %w で空白を許容した文字列を改めて実行可能なコードにするために、AAのコードの終わりの箇所に '.join を挿入しています。しかし、エスケープシーケンスのそれぞれの文字列が1文字として扱われるため、最後の調整が難しかったです。 今回は以下のようにしましたが、AAが変わると若干崩れるので、最後は手作業で地道に調整することになりそうです。 # build.rb end_text = ' .join ' end_text_start_point = last_point + end_text.length output_text_length = "\e [0m# \e [m " .length # エスケープシーケンスを使って白字を1字出力する必要な字数(`#`は仮の文字列) real_output_text_length = end_text.length * output_text_length # エスケープシーケンスを考慮した上で確保すべき文字数 # (一部省略) code = << CODE l= #{" ' " .ord } .chr o[- #{ end_text_start_point + real_output_text_length } , #{ (end_text.length + 1 ) * output_text_length } ]=l+" #{ end_text } " CODE 感想 eval 内ではRubyのコードを素直に書くことができるので、超絶技巧を使わなくてもQuineでAAを書くことはできました。 ある程度完成しないとコード自体が動かないのでデバッグしづらいし、理解できていない部分もたくさんあります(irbとはずっと付きっきりでした) 。でも、コードを書いていい感じの見た目ができるのがとても楽しかったです!! おまけ この記事をレビューしてもらったところ、早速カスタマイズLGTMをいただきました🙌 最後に 5/28(火)に、「After RubyKaigi 2024〜メドピア、ZOZO、Findy〜」として、メドピア株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2024の振り返りを行います。 オンライン・オフラインどちらもあり、LTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!! findy.connpass.com また、ファインディでは、これからも新しいものを取り入れつつ、Rubyを積極的に活用して、Rubyとともに成長していければと考えております。 一緒に働くメンバーも絶賛募集中なので、興味がある方はぜひこちらから ↓ herp.careers *1 : RubyでうどんげQuine(とAA型Quineの作り方講座) *2 : クックパッド春の超絶技巧パンまつり 超絶技巧プログラミング編 資料
アバター
こんにちは!ファインディでTeam+開発チームのエンジニアメンバーの 西村 です。 この記事では、私が聞いたRubyKaigi 2024のセッション「Lightning Talks」より「Enjoy Creative Coding with Ruby」で紹介されたクリエイティブコーディングを試してみたので共有します。 クリエイティブコーディングとは クリエイティブコーディングとは、アプリケーションのような機能的なソフトウェアを作るのではなく、プログラミング言語を使ってビジュアルアートを創作することです。 クリエイティブコーディングをはじめるまでの背景 私は、RubyKaigi 2024の「Lightning Talks」より「Enjoy Creative Coding with Ruby」で、初めてクリエイティブコーディングについて知りました。 Miyuki Koshibaさんのスライド資料を引用させていただいています。 スライド内にある作例を見て、自分も創りたい!!と思う素敵LTでした!! speakerdeck.com DAY2の終わりに、クリエイティブコーディングを試しました。 すると、LTの内容通り、数行のコードでブラウザ上に図形を描画できたので、とてもワクワクしました! もう少し凝ったものを創りたいと思い、ファインディのロゴを創ることにしました。 セットアップ index.htmlに必要なスクリプトを読み込ませるだけで始めることができます。ローカルPCにRubyやほかのライブラリをインストールすることは不要です。 これをもとに、index.htmlを作成します。 github.com scriptタグには、次のライブラリを読み込ませる必要があります。 p5jsライブラリ ブラウザ上で円、四角や三角関数を使った図形等を描画できるようにするライブラリ p5rbライブラリ p5jsとRubyを橋渡ししてくれるライブラリ ruby.wasm ブラウザ上でRubyを実行できるライブラリ <script type="text/ruby"> タグ内に書いたRubyコードが実行され、 <main> タグ内に描画されます。 < html > < head > < script src = "https://cdn.jsdelivr.net/npm/ruby-3_2-wasm-wasi@next/dist/browser.script.iife.js" ></ script > < script src = "https://cdn.jsdelivr.net/npm/p5@1.5.0/lib/p5.js" ></ script > < script type = "text/ruby" src = "p5.rb" ></ script > < script type = "text/ruby" > # ここにRubyで処理を書く </ script > </ head > < body > < main > </ main > </ body > </ html > 創ったもの ファインディのロゴをクリエイティブコーディングで描きました。 創作物は 外部公開 しています。 リポジトリは、こちら↓ github.com どう創ったのか ライブラリが対応している図形を組み合せることで、ファインディのロゴを創れそうだなぁと思いました。 ひとつずつ解説していきます。 まずは、外円部分を描画します。 def draw # 色設定 gray = color( 165 , 165 , 164 ) white = color( 255 , 255 , 255 ) blue = color( 26 , 90 , 164 ) background(white) # 輪郭を消す noStroke() # 外円 fill(gray) arc( 200 , 200 , 300 , 300 , radians( 0 ), radians( 360 ), PIE ) fill(blue) arc( 200 , 200 , 300 , 300 , radians( 130 ), radians( 300 ), PIE ) # くり抜き fill(white) circle( 200 , 200 , 210 ) end つづいて、外円部分の青い部分の端を鋭角にします。 外円とくり抜きの間に下のコードを追加します。 # 外円 ... fill(gray) arc( 284 , 115.5 , 150 , 90 , radians( 190 ), radians( 260 ), PIE ) fill(blue) arc( 96 , 270.3 , 150 , 90 , radians( -60 ), radians( 80 ), PIE ) # くり抜き つづいて、外円から横に伸びる青い部分を描画します。 ※ 青い部分はFindyのFを表しています。 このFの横棒が一番難しかったです。 複数の円を組み合わせて描画する必要があり、それぞれの図形の位置を調整することが大変でした。 # くり抜き ... # 外円から横に伸びる青い部分 fill(blue) rotate( -0.15 ) arc( 127 , 270 , 200 , 200 , radians( 200 ), radians( 290 ), PIE ) fill(white) arc( 127 , 270 , 132 , 132 , radians( 200 ), radians( 360 ), PIE ) arc( 190 , 231 , 125 , 125 , radians( 200 ), radians( 250 ), PIE ) 最後に、外円の右下に四角形を描画すれば、完成です!🎉 # 外円から横に伸びる青い部分 ... # 外円から伸びる四角形 fill(gray) translate(width / 1.25 , height / 1.25 ) rotate(radians( 55 )) rectMode( CENTER ) rect( -5 , 75 , 70 , 45 ) このように部品ごとにコーディングしていくと、簡単に創ることができます! デザインチェック 本物のロゴと並べて、デザイナーにチェックしてもらいましたが、Fの文字の形を中心にたくさん突っ込まれました。 いただいたフィールドバックの一例です! 「Fの2本の横棒の右端の角度がいまひとつ」 「外円から横に伸びる青い部分の描画が甘い」(画像を拡大しながら) 本物のファインディのロゴ クリエイティブコーディング作成ロゴ 自分のクリエイティブコーディングの技術力はまだまだなので、今後向上させていきたいです! 終わりに Xの#creativecoding を見ると、様々なクリエイティブコーディングが投稿されています。 気になる方は是非見てみてください。 また、少しでもクリエイティブコーディングに興味を持っていただけたら、嬉しいです! 5/28には「After RubyKaigi 2024〜メドピア、ZOZO、Findy〜」として、メドピア株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2024の振り返りを行います。 LTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!! findy.connpass.com ファインディでは、これからも新しいものを取り入れつつ、Rubyを積極的に活用して、Rubyとともに成長していければと考えております。 そして、一緒に働くメンバーを絶賛募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
こんにちは、あるいはこんばんは。 @gessy0129 です。 沖縄、行ってきました! 観光、しませんでした! とても楽しかったです! 今回、ファインディブースでは、 Ruby歴×Ruby関連のカンファレンス参加回数は? というアンケートと 日替わりでRuby Code Quiz を実施しました! 全体的なブースの話はDevRelの まっきー がまとめているのでそちらをご覧ください! note.com 本記事では、エンジニアなら気になるであろうFindy Ruby Code Quizの解説をしていきたいと思います!では、早速見ていきましょう! 一日目 - Rubyバージョンの組み合わせ 問題 下記、実行結果のRubyバージョンの組み合わせを答えてください。 なお、ひとつのバージョンはひとつの選択肢にのみ回答できます。 Ruby versions: 3.3.0 or 3.2.0 or 3.1.0 or 3.0.0 ① irb> Time .new( " 2024-05-15T11:11:11Z " ) => 2024 - 05 - 15 11 : 11 : 11 UTC ② irb> Time .new( " 2024-05-15T11:11:11Z " ) => 2024 - 01 - 01 00 : 00 : 00 + 0000 ③ irb> Time .new( " 2024-05-15T11:11:11Z " ) <internal :timev >: 310:in ` initialize': invalid value for Integer(): "2024-05-15T11:11:11Z" (ArgumentError) ④ irb> Time .new( " 2024-05-15 " ) <internal :timev >: 409:in ` initialize': no time information (ArgumentError) 選択肢 ①: 3.2.0、②: 3.0.0、③: 3.1.0、④: 3.3.0 ①: 3.3.0、②: 3.2.0、③: 3.1.0、④: 3.0.0 ①: 3.1.0、②: 3.0.0、③: 3.2.0、④: 3.3.0 ①: 3.0.0、②: 3.1.0、③: 3.3.0、④: 3.2.0 正解と回答率 正解 ✅ 1. ①: 3.2.0、②: 3.0.0、③: 3.1.0、④: 3.3.0 回答率 14.15% 53.77% 19.81% 12.26% 解説 RubyのバージョンアップのNEWSを追いかけていれば解けるという問題です。 Ruby3.3.0のNEWS https://github.com/ruby/ruby/blob/v3_3_0/NEWS.md Time.new('2023-12-20') # no time information (ArgumentError) これを知っていたら④が3.3.0だとわかります。 Ruby3.2.0のNEWS https://github.com/ruby/ruby/blob/v3_2_0/NEWS.md Time.new now can parse a string like generated by Time#inspect and return a Time instance based on the given argument. Time.newはinspectが出力するstringからパースできるようになった。これで、①は3.2.0以上とわかります。 Ruby3.1.0のNEWS https://github.com/ruby/ruby/blob/v3_1_0/NEWS.md At the same time, time component strings are converted to integers more strictly now. Time.new(2021, 12, 25, "+07:30") #=> invalid value for Integer(): "+07:30" (ArgumentError) Ruby 3.0 or earlier returned probably unexpected result 2021-12-25 07:00:00, not 2021-12-25 07:30:00 nor 2021-12-25 00:00:00 +07:30. そもそも3.1.0までは文字列だけをいれるユースケースはなかったようです。3.0.0以前は文字列だけだとよくわからない挙動になっていたけど、この対応でチェックを厳密にしたことで文字列だけだとエラーになったのではないかと推測します。変更意図などはCommiterの方々に解説求めたい部分です。 ただ、このNEWSの内容を全てを記憶してるのは難しすぎになってしまうので、回答の選択肢を4択にしています。4択なので3.3.0の挙動を知っていたら1番目と3番目の2択に絞れると思います。 これに加えて、3.2.0の挙動を知っていたら①は3.2.0以上とわかるので1番目が答えだとわかります。また、3.2.0の挙動を知らなかったとしても、①〜③は全く同じコマンドを実行しており①が正しく挙動しているので①の方が新しいバージョンだと推測できます。これでも1番目が答えだとわかるようになってます。 二日目 - 代入されているオブジェクトはどれ? 問題 fooに代入されているオブジェクトは次のうちどれでしょうか? (Ruby: 3.3.0で実行した場合) irb(main): 001 > foo = ?? ? irb(main): 002 > foo.include?( 3 / 2 ) => true irb(main): 003 > (foo + [ 3 ]).inject( :+ ) => 9 irb(main): 004 > foo.last => NoMethodError 選択肢 1..3 [1, 2, 3] Set.new([1, 2, 3]) Enumerator.new { |y| [1, 2, 3].each { |i| y << i } } 正解と回答率 正解 ✅ 4. Enumerator.new { |y| [1, 2, 3].each { |i| y << i } } 回答率 25.37% 4.88% 39.02% 30.73% 解説 メソッドに対しての出力結果から元の値を当てる という問題形式を使ってみたかった問題です。 そこから逆算して、ちょっと似ているクラス(Range, Array, Set, Enumerator)を当てるようにしてみました。 irb(main):002 の出力は引っ掛けで、3/2 => 1になるのですべてtrueになる。 が、直感的には3/2 => 1.5という考えを利用して、1..3では?と思わせて問題を難しくしました。 irb(main):003 は、同じ値は重複しないというSetの特徴を利用から、Setを除外できるようになっている。また、Rangeには+メソッドがないのでエラーになる。 irb(main):004 は、Setには順番がなく、Enumeratorは外部イテレータなので最後の要素が特定できないという特徴を利用している。 実行結果を見るのが一番早いですね。 選択肢1 irb(main): 001 > foo = 1 .. 3 => 1 .. 3 irb(main): 002 > foo.include?( 3 / 2 ) => true irb(main): 003 > (foo + [ 3 ]).inject( :+ ) (irb): 3:in ` <main>': undefined method ` + ' for an instance of Range (NoMethodError) irb(main):004> foo.last => 3 Rangeには+メソッドがないのでNoMethodErrorになるのと、lastメソッドがあるので結果が返ってきます。 選択肢2 irb(main): 001 > foo = [ 1 , 2 , 3 ] => [ 1 , 2 , 3 ] irb(main): 002 > foo.include?( 3 / 2 ) => true irb(main): 003 > (foo + [ 3 ]).inject( :+ ) => 9 irb(main): 004 > foo.last => 3 lastメソッドがあるので結果が返ってきます。 選択肢3 irb(main): 001 > foo = Set .new([ 1 , 2 , 3 ]) => #<Set: {1, 2, 3}> irb(main): 002 > foo.include?( 3 / 2 ) => true irb(main): 003 > (foo + [ 3 ]).inject( :+ ) => 6 irb(main): 004 > foo.last (irb): 4:in ` <main>': undefined method ` last ' for an instance of Set (NoMethodError) 同じ値は重複しないというSetの特徴から 003 の結果が6になります。 選択肢4 irb(main): 001 > foo = Enumerator .new { |y| [ 1 , 2 , 3 ].each { |i| y << i } } => #<Enumerator: ...> irb(main): 002 > foo.include?( 3 / 2 ) => true irb(main): 003 > (foo + [ 3 ]).inject( :+ ) => 9 irb(main): 004 > foo.last (irb): 4:in ` <main>': undefined method ` last ' for an instance of Enumerator (NoMethodError) 正解ですね!! 有識者の方に解説モトム Setには順番がない と書きましたが、firstはあります。なので、こうなります。 irb(main): 001 > foo = Set .new([ 1 , 2 , 3 ]) => #<Set: {1, 2, 3}> irb(main): 002 > foo.first => 1 なんでfirstがあるのか、詳しい方のコメントを待っています。Enumerableを継承しているからと言われたらそれまでと言えばそれまでですがw 三日目 - 正しい出力結果は? 問題 次のコードを実行した出力結果で正しいのはどれでしょう? (文字コード UTF-8、Ruby: 3.3.0で実行した場合) hoge = [ ?1 , ?2 ].zip([ ?3 , ?4 ], [ ?5 , ?6 ]).transpose.flatten hoge.reject! hoge = hoge.map{|n| n.ord}.sort puts hoge.join( " , " ) 選択肢 1, 2, 3, 4, 5, 6 6, 5, 4, 3, 2, 1 49, 50, 51, 52, 53, 54 54, 53, 52, 51, 50, 49 正解と回答率 正解 ✅ 3. 49, 50, 51, 52, 53, 54 回答率 17.19% 14.06% 60.94% 7.81%  解説 問題文のコードを読みにくくするため、わざとインデントやスペースを入れていない。というちょっとイジワルをしました。 [Integer, Integer]に対して、なにかしら変換するのはひっかけるポイントが少ないので、String→Integerへ変換するようにして、複雑さを増すようにしています。 # zipメソッドを使う人はあまりいなそうなので、採用 > hoge = [?1,?2].zip([?3,?4],[?5,?6]) => [["1", "3", "5"], ["2", "4", "6"]] # ノーマルなRubyistは2次元配列を扱うことが少ないはずなので、transposeメソッドを採用 irb(main):003> hoge = hoge.transpose => [["1", "2"], ["3", "4"], ["5", "6"]] # Rubyistはflattenを結構使っている気がする。ここは配列を入れ子にしたくないから、採用 irb(main):004> hoge = hoge.flatten => ["1", "2", "3", "4", "5", "6"] # なにかやっていそうだけど、Enumeratorに変えているだけ irb(main):006> hoge = hoge.reject! => #<Enumerator: ...> # ordメソッドが、StringからIntegerへ変換することを知っているかチェック # sortメソッドが降順 or 昇順どちらで並び替えているか試す irb(main):007> hoge = hoge.map{|n|n.ord}.sort => [49, 50, 51, 52, 53, 54] irb(main):008> puts hoge.join(", ") 49, 50, 51, 52, 53, 54 さいごに 4/1 からクイズ作成を開始して、約4日ほどでほぼほぼ今の形まで出来上がりました。ラフで色んな案を出して、煮詰めていく過程がとても楽しかった(らしい)です。 また、 knuさんがブースに遊びに来てくれてPostしてくれていたのがとても嬉しかったです!来ていただいてありがとうございました! Findyさんの問題良問ですね! 自分がSetとEnumeratorとirbセッション4行目の登場人物すべての作者なのに、足し算を勘違いして誤答してしまったw #RubyKaigi pic.twitter.com/qkXyemZZkn — Akinori Musha (@knu) 2024年5月16日 問題は @hamchance0215 、 @aiandrox 、 @sontixyou が作成してくれました!ありがとうございました! 5/28には「After RubyKaigi 2024〜メドピア、ZOZO、Findy〜」として、メドピア株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2024の振り返りを行います。 LTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!! findy.connpass.com ファインディでは、これからもRubyを積極的に活用して、Rubyとともに成長していければと考えております。 そして、一緒に働くメンバーを絶賛募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
こんにちは!ファインディでTeam+開発チームのEMをしている 浜田 です。 以前公開した記事「ファインディはRubyKaigi 2024 にPlatinum Sponsorsとして協賛します!」で紹介した通り、ファインディはRubyKaigi 2024に協賛しており、現地で参加してきました! tech.findy.co.jp 今週(5/20〜25)はRubyKaigi 2024の振り返りも兼ねてRubyKaigiに関連した記事を投稿していきます! この記事では、私が聞いたセッションの中の1つ「 Unlocking Potential of Property Based Testing with Ractor 」で紹介されたGem「 PBT 」を試してみたので共有します。 Unlocking Potential of Property Based Testing with Ractor 「 Unlocking Potential of Property Based Testing with Ractor 」は、Property Based TestingをRubyで実行するためのGemを作成し、さらにRactorを活用することでテストパフォーマンスを向上させる話でした。 speakerdeck.com セッションの中でも触れられていましたが、Property Based Testingを聞いたことがある方は20%ほどとのことだったので、この記事でも簡単に紹介したいと思います。 Property Based Testingでは、従来の入力値を開発者が列挙してテストする手法(セッションではExample Based Testingと呼んでいました)とは異なり、入力値の性質(プロパティー)を指定してテストケースを自動生成して網羅的なテストを行うことができます。 私は以前ファインディ主催で開催した「 t-wadaさん、ymotongpooさんに聞くテスト戦略最前線 」というイベントでt-wadaさんから紹介されて興味を持ちました。 スライドも公開されているので、興味がある方はぜひご覧ください。 speakerdeck.com この記事では、Gem「PBT」を使ってProperty Based TestingをRubyで実装しつつ、従来のテスト手法であるExample Based Testingとの違いを比較します。 試してみた それでは早速試していきます。 今回使用したRubyと主要なGemのバージョンは次の通りです。 Ruby 3.3.1 PBT 0.4.1 RSpec 3.13.0 今回は入力値を3倍にして返却する triple というメソッドを実装します。単純にするため入力値は整数のみ考慮します。 class PropertyBasedTest def self . triple (value) # valueを3倍にして返す end end それではまずいつも通り入力値を開発者が列挙してテストするExample Based Testingでテストを書きます。なお、テストはRSpecを使用します。 triple のテストを書く場合、皆さんはどのような入力値のパターンでテストを書きますか? 今回は、正の整数 / 負の整数 / 0 の3パターンのテストケースを書きました。 Example Based Testingの場合、開発者がテスト条件を満たす任意の値を選んでテストコードを書くため、適当に選んだ 22 / -5 / 0 を指定しました。 RSpec .describe PropertyBasedTest do describe ' .triple ' do context ' when example-based testing ' do subject( :add ) { described_class.triple(number) } context ' when input value is 22 ' do let( :number ) { 22 } it { is_expected.to eq 66 } end context ' when input value is -5 ' do let( :number ) { -5 } it { is_expected.to eq( -15 ) } end context ' when input value is 0 ' do let( :number ) { 0 } it { is_expected.to eq 0 } end end end end 実行結果は次の通り。無事テストが通りました🙌 PropertyBasedTest .triple when example-based testing when input value is 0 is expected to eq 0 when input value is 22 is expected to eq 66 when input value is -5 is expected to eq -15 Finished in 0.1124 seconds (files took 4.22 seconds to load) 3 examples, 0 failures ところが、 triple メソッドには未知のバグが埋め込まれていました。 3の倍数、もしくは3のつく入力値が与えられた場合は異常終了するというバグです。 今回のテストではたまたま3の倍数や3がつく数値をテストケースに含めていなかったため、このバグを見逃してしまいました。 このような見逃しを回避するための手法として、Property Based Testingが有効です。 Property Based Testingは、入力値の性質(プロパティー)を指定してテストケースを自動生成し、網羅的なテストを行うことができます。 では、PBTを使ってテストを書いてみましょう。 テストコードを見ていただければわかる通り入力値の具体的な値は指定しておらず、整数であること( Pbt.integer )のみを指定しています。 RSpec .describe PropertyBasedTest do describe ' .triple ' do context ' when property-based testing ' do it do Pbt .assert( verbose : true ) do # 入力値が整数であることを指定 Pbt .property( Pbt .integer) do |number| result = described_class.triple(number) # 結果が3の倍数であることを確認 expect(result).to eq number * 3 end end end end end end 実行結果は次の通り。正しくエラーが検出されました。 Randomized with seed 19396 PropertyBasedTest .triple when property-based testing example at ./spec/models/property_based_test_spec.rb:42 (FAILED - 1) Failures: 1) PropertyBasedTest.triple when property-based testing Failure/Error: Pbt.assert(verbose: true) do Pbt.property(Pbt.integer) do |number| result = described_class.triple(number) expect(result).to eq number * 3 end end Pbt::PropertyFailure: Property failed after 5 test(s) seed: 113103392077172016306707556593348939592 counterexample: 3 Shrunk 8 time(s) Got RuntimeError: (´Д`) サンッ!! app/models/property_based_test.rb:5:in `triple' spec/models/property_based_test_spec.rb:45:in `block (6 levels) in <top (required)>' (長いので省略) Encountered failures were: - 614700 - 307350 - 153675 - 76838 - 38419 - 4803 - 301 - 38 - 3 Execution summary: . √ -929894 . √ 440110 . √ -801140 . √ -194044 . × 614700 . . × 307350 . . . × 153675 . . . . × 76838 . . . . . × 38419 . . . . . . √ 19210 . . . . . . √ 9605 . . . . . . × 4803 . . . . . . . √ 2402 . . . . . . . √ 1201 . . . . . . . √ 601 . . . . . . . × 301 . . . . . . . . √ 151 . . . . . . . . √ 76 . . . . . . . . × 38 . . . . . . . . . √ 19 . . . . . . . . . √ 10 . . . . . . . . . √ 5 . . . . . . . . . × 3 . . . . . . . . . . √ 2 . . . . . . . . . . √ 1 . . . . . . . . . . √ 0 # ./spec/models/property_based_test_spec.rb:43:in `block (4 levels) in <top (required)>' Finished in 0.08717 seconds (files took 4.23 seconds to load) 1 example, 1 failure PBTではデフォルトで100通りのテストを実行します。 Property failed after 5 test(s) と表示されているので、5回目のテストでエラーが発生したとわかります。 counterexample にはエラーが発生した入力値のうちShrunkして発見した最小の値が表示されています。今回は 3 が入力された場合にエラーが発生したことがわかります。 また、 Shrunk 8 time(s) とのことなので、8回Shrunkして最小の値 3 を見つけたことがわかります。 無事バグが検出できたので、コードを修正して再度テストを実行します。 今回は無事テストが通りました🎉 PropertyBasedTest .triple when property-based testing example at ./spec/models/property_based_test_spec.rb: 42 Finished in 0.09535 seconds (files took 4.28 seconds to load ) 1 example, 0 failures このようにProperty Based Testingを活用することで、開発者に依存した入力値ではなく、網羅的なテストを行うことができるため、開発者が予見できていないバグを見つけやすくなります。 今回のサンプルコードは単純な処理なのでテスト実行時間にほとんど差がありませんが、通常はPBTの方が試行回数が多くなるためテスト実行時間は長くなります。そのため、様々なインプットのパターンを網羅すべきテストケースなど適材適所で活用いきたいと思います! 5/28には「After RubyKaigi 2024〜メドピア、ZOZO、Findy〜」として、メドピア株式会社、株式会社ZOZO、ファインディ株式会社の3社でRubyKaigi 2024の振り返りを行います。 LTやパネルディスカッションなどコンテンツ盛りだくさんなのでぜひご参加ください!! findy.connpass.com ファインディでは、これからもRubyを積極的に活用して、Rubyとともに成長していければと考えております。 そして、一緒に働くメンバーを絶賛募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
こんにちは。 FindyのFreelance開発チームの久木田です。 今回は 自慢の作業環境を大公開 のPart3になります。 前回までの記事はこちら ↓ 👉 自慢の作業環境を大公開シリーズ Part3は関西特集ということで、大阪と京都からフルリモートでJOINしているエンジニアたちの作業環境を公開します! 作業環境を大公開 久木田 まずは大阪から JOIN している久木田の作業環境から紹介していきます。 作業環境の全体像はこんな感じになっています。 ディスプレイ3台PC2台おいても広々スペース 会社支給の MacBook Pro 14インチ をノート用スタンドに載せて使っています。 ディスプレイはノートPCのディスプレイを含めて4枚使っていて、左からブラウジング用・コーディング用・Slack用・Datadog等の監視ツールのダッシュボード閲覧用として使っています。 このデスクですが、工務店を営む父親に頼んで、一緒に手伝いつつ、天板の木材から切り出して、取り付けてもらったものになります。 音声環境周りですが、自分は Anker PowerConf S3 というスピーカーフォンを使っています。 スピーカーフォン:Anker PowerConf S3 マイクも色々試しましたが、途中で耳を塞ぐのが個人的に好きじゃなかったのに気がついてから、このスピーカーフォンを愛用しています。 ミュートボタンが Zoom と Google Meet のミュートと連動してくれているのもお気に入りポイントです。 ちなみにカメラは MacBook のカメラの方が、市販されている廉価なWebカメラより性能が良いと思っているので、そのまま使っています。 キーボードは HHKB Professional2 を使っていて、トラックボールは Kensington スリムブレードトラックボール を使っています。 HHKBのUS配列が好みです ボールを横回転させてするスクロールが慣れると快適 キーボードは大学院生の頃からHHKBが好きで(当時は廉価版のHHKB Lite2 for Macでしたが)、この画像のキーボードは社会人になった7年前くらいからずっと使っているものです。 トラックボールは前職の上司の方からお下がりとしてもらって以来、このKensingtonのスリムブレードを使っていて、今使っているものは2代目になります。 トラックボールも親指タイプのものなど色々試した結果、このデバイスに落ち着きました。 広い画面を移動するのに、トラックボールはおすすめです。 そして使っている椅子もこだわっているものの1つになりまして、ハーマンミラーの エンボディゲーミングチェア を使っています。 背面の青いデザインも気に入っています 大阪に ハーマンミラーストア心斎橋 があり、そこへ行って色々な椅子を試した結果、この椅子を選びました。 背もたれが背骨のカーブにフィットしているので体重重めの自分も快適に座れています。 後ろにデスクトップPCが見えていますが、これは私用のPCで左のディスプレイの入力切替をして使っています。 ディスプレイの奥にあるのが自分でパーツを取り寄せて組み立てたPC 前回や前々回の作業環境紹介では私用と仕事用でデバイス共用している人が多かったですが、自分はmacとwindowsでOSが違っていたり、トラックボールだとゲームがやりづらかったりするので、手元のデバイスも全く別のものを用意して使っています。 最後に机の角に飾っている好きなものごちゃまぜディスプレイを写して自分の作業環境紹介は終わります。 攻殻機動隊・ウマ娘・ガンダム・にじさんじ 金丸 次に京都から JOIN している金丸の作業環境を紹介します。 作業環境の全体はこのようになっています。 ディスプレイとノートPCを最小限のスペースで設置しています 会社支給の MacBook Pro 14インチ をノートPC用アームに載せて使っています。 デスクの幅が120cmのためノートPCを宙に浮かせることでスペースを節約しています。 デスクには COFOの昇降デスク を利用し、定期的に立ち上がって作業をするようにしています。 天板の裏がマグネットになっているため、マグネットフックで雑に収納できるところが気に入っています。 ディスプレイは HP社の曲面ディスプレイ を利用しています。 ディスプレイに設置したライトで常に明るい WQHD、USBハブ機能、USB-C(USB PD) に対応しているため、給電 + USB ケーブル1本で出来るところが気に入っています。 ウルトラワイドなディスプレイですが曲面になっているため違和感なく使えています。 スピーカーにはクラウドファンディングで購入した ovo を利用しています。 マットな金色がデスクに映えます コンパクトな本体でも音割れが少なく、気に入っています。 通話には eMeet Luna を利用しています。 有線/Bluetooth切替可能で取り回し◎ 本体にノイズキャンセリング機能がついているため、タイピング音などが軽減されるところが気に入っています。 キーボードは分割キーボードの 7sPro を利用しています。 レトロっぽいキーキャップが好み 「HHKBの配列で分割キーボードを使いたい」という要望を満たしているキーボードで、キースイッチを変えながら3年くらい使っています。 ホットスワップに対応しているのでキースイッチの変更が出来るところもお気に入りポイントです。 現在は alpacaスイッチ をセットして利用しています。 マウスは MX MASTER 3S を利用しています。 勢いよく動かしてもカーソルがブレない点がお気に入りです トラックパッド、トラックボールと試してみたのですが、結局マウスに戻ってきて以来、このマウスを利用しています。 最後に推しを詰め込んだディスプレイを紹介して、作業環境紹介を終わります。 カニさん・ガブモン・幽遊白書・ズートピア 西村 最後に、京都からフルリモートで Findyの転職サービス を開発している西村の作業環境を紹介します。 妻と同じ部屋でリモートワークをしているため、机を向かい合わせるように設置しています。奥に見えるのが妻側のデスクです。 あまり広くない部屋なので、作業スペースもコンパクトです ディスプレイは DELL U2720QM を使用しています。4K 27インチのディスプレイなので、これ1つでも十分に作業が可能です。 MacBook Pro 16 をスタンドに載せて、デュアルディスプレイとしています。MacBook は主に Slack 専用としています。 こだわりポイントとしては、目線を下げないためにディスプレイをスタンドの上に置いていることです。 猫背気味で首や肩に負担を掛けがちなので、それを軽減できないかなと考えてこのようにしています。 また、ディスプレイと正対するように座ることも意識しています。 リモートワークを始めた初期に、目線を左右どちらかに寄せ続けて首や腰の片側だけが痛くなってしまった。という苦い経験から、このような工夫をしています。 ただ、少し目が疲れやすく感じるので、本来であればもう少しディスプレイとの距離を取りたいと考えています。 しかし部屋の広さの関係で、これ以上奥行きのあるデスクを置くことができないので、この状態で我慢しています。 キーボードは Keychron K2 を使用しています。 Keychron K2 のキーボードと木製のパームレスト 元々は HHKB を使ってみたいなと思っていたのですが、値段が高くてなかなか手が出せていなかったところに前職時代の先輩に勧められて購入しました。 比較的安価だったので試しに使ってみたところ、とても打ちやすく非常に気に入っています。かれこれ3年以上経ちました。 マウスは Apple の Magic Trackpad を使っています。マウスに対するこだわりはなく、友人からプレゼントしてもらったものをずっと使い続けています。 また、手首から先の負担を減らす目的で、 FILCO のパームレスト を使っています。肌に優しそうという理由で木製のものを選びました。 今回はデスクの写真を撮るタイミングだったのでかなりスッキリとしています。Findyでは社内輪読会も行っており、業務時間中にもチームで同じ書籍を読みエンジニアのレベルアップを図っています。日常的には、その輪読会用の書籍が置いてあったりすぐに読み返したりできるようにしています。 購入して1年ほど経ってから成長の兆しが見え始めたお気に入りのサボテン まとめ いかがでしたでしょうか? やはり、キーボードにはそれぞれこだわりがありましたね! また、今回の記事を通して意外にもスピーカーマイクを使っているのが自分だけではないことに驚きました。 自分の環境でも記載しましたが、マイクの物理スイッチはおすすめなので是非試してみてください! 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
こんにちは、ファインディ株式会社でフロントエンドのリードをしております 新福( @puku0x )です。 この記事では、転職サービス Findy の開発チームにおける開発生産性の向上に対する取り組みをご紹介します。 以前の状況 モノリスの解体 開発基盤の刷新 コンポーネント設計の刷新 テストの拡充 CI の高速化 改善の効果 まとめ 以前の状況 2020年頃の Findy は Ruby on Rails と React のモノリス構成で作られていました。 機能の増加に従いコードが複雑化し、しだいに開発スピードが伸び悩むようになりました。 ここで Findy Team+ で算出した当時のリードタイムを見てみましょう。 2020年のFindyのリードタイム 上記のグラフから次のことがわかります。 改修が本番に適用されるまで 約1週間 かかる プルリクエストがレビューされるまで 約5日 放置される プルリクエストのレビュー完了までに 約2日と半日 かかる 1年を通してプルリクエスト数の平均が2.0を超える日が無かったことからも苦しい状況であったことが推測できます。 モノリスの解体 前述の通り Findy は Rails モノリスで作られており、CI にフロントエンドとバックエンドの両方が含まれていることから、画面の文言を1つ更新するだけでも長い CI 待ちが発生します。 この状況を打破するために、Findy で最初に行われた取り組みは「Rails モノリスの解体」でした。 約3ヵ月かけてバックエンド側を Rails の API モード、フロントエンド側を Next.js で再実装するという大掛かりなプロジェクトでしたが、これによりフロントエンドとバックエンドで独立して動けるようになったため、先述した CI 待ちを大幅に短縮できました。 実際の効果は note の記事 にて弊社 CTO が紹介しておりますので、そちらも是非ご一読ください。軽微な改修であれば1日で修正するスピードを実現し、最速でユーザーに価値を届ける基盤を作ったのがモノリス解体の大きな効果と言えるでしょう。 開発基盤の刷新 2021年5月、モノリス解体後のフロントエンドが晴れてリリースされました。大きな不具合もなく無事に終わったと安心したいところですが、まだ残っている課題を解決しなければなりません。 フロントエンド側に残された課題は次の通りです。 バージョンの古いツール・ライブラリが多数 コンポーネントの書き方が古い 型(Flow)はあるがうまく動作していない テストが書かれていない 見通しの悪いフロントエンド設計 最初に、依存ライブラリのアップデートや使われなくなったライブラリを削除していきました。また、Dependabot を導入し定期的に更新する体制を整えました。 次に Nx を導入し、モダンな開発環境へ移行していきました。Nx は前職で利用した経験があり、コマンドひとつで TypeScript + React + Jest + ESLint + Prettier が揃った開発環境を作成できるため移行は短期間で済みました。ここで導入した Nx は、後の CI 高速化への布石となります。 Findy には開発当初から React が用いられておりましたが、この時はまだコンポーネントが Class Component で書かれてありました。今後の React エコシステムの発展に追従するべく Function Component への書き換えも同時に進めました。 Flow から TypeScript への移行は直接書き換えるのが難しかったため、一度全てのソースコードから Flow の型アノテーションを外して純粋な JavaScript にし、その後 TypeScript で書き直しました。移行の早い段階で strict: true で書くようにしたため、Null チェック漏れによる不具合を大幅に削減できたと思います。 当時は API レスポンスに対しても手作業で型付けを行っていましたが、API の仕様が複雑であったりドキュメントと同期するのが難しかったことから、後に REST API から GraphQL に移行し、 GraphQL Code Generator による型生成が採用されるようになります。 コンポーネント設計の刷新 当時の実装では、コンポーネントは Container Component / Presentational Component のパターンで書かれてありました。メンバーの習熟度やテストのしやすさを考慮し、基本設計は踏襲しつつ、下記に示すようなデータの流入元に着目した三層構造へと拡張しました。 コンポーネント名 カスタムフック名 扱うデータ Page Component Params Hook ブラウザURL Container Component Facade Hook API や ストレージ等 Presentational Component Presenter Hook フォーム 各層での責務が明確になったことで実装時の混乱が少なくなったと思います。 余談ですが本設計は筆者が Angular コミュニティから得られた知見を React アプリケーションに転用したものとなります。不思議な縁もあるものですね。 *1 テストの拡充 プロジェクト進行中にいくつかの不具合に遭遇することがありましたが、それらのほとんどはテストがあれば防げたものでした。将来的な変更に耐えられるよう、テストの拡充は早期に着手しました。幸いバックエンド側では既に「テストを書くのは当たり前」という文化が根付いていたため、フロントエンドのテスト導入のハードルは高くありませんでした。 当時はまだメンバー全員がフロントエンドのテストに慣れている訳ではなかったため、テストのお手本となるような実装例を増やしていくことから始めました。まず、ユーティリティ等の入出力の関係が明らかなものからユニットテストを書き、次にコンポーネントのスナップショットテスト、それからインタラクションテストや結合テストといったように範囲を広げていきました。 また、テストを書くモチベーションを向上させる工夫として Wallaby.js のような可視化ツールを導入しました。Wallaby.js については次の記事もご参照ください。 tech.findy.co.jp 今では全員が当たり前のようにフロントエンドのテストを書くようになりました。テストによって守られているという安心感もさることながら、テストを書くことが習慣化したことによって「テストを書きやすいように設計が洗練される」といった副次的な効果が得られたのも嬉しいところでした。 CI の高速化 コードベースが増え、ビルドやテストの実行時間が増えていくと、CIの待ち時間も長くなっていきます。CI 時間の増加はリードタイムの増加に直結するため、改善は必須でした。 開発基盤を刷新するにあたり、CI 時間の増加は予め想定されていたため Nx を導入し、 nx affected コマンドによる変更検知によって必要なジョブのみ実行することで CI の高速化を図りました。ジョブの再実行といった変更検知だけでは高速化が難しいものに対しては Nx Cloud を導入し、リモートキャッシュを活用した高速化を実施しました。 結果は次の通りです。CI 時間は平均6分で、キャッシュヒットしない場合は16〜17分かかることもありますが、早い時は2〜3分で終わるようになりました。 ジョブ単位の詳細を次の図に示します。 Nx Cloudによる高速化の内訳 Nx Cloud 導入により300時間以上の CI 時間を削減できました。ここではフロントエンド側のCI高速化を取り上げましたが、バックエンド側の取り組みも記事にされておりますので、ご興味のある方は是非ご一読ください。 tech.findy.co.jp 改善の効果 それでは、モノリス解体をはじめとした様々な取り組みの成果を見ていきましょう。次の図は2023年における転職サービスの Findy のフロントエンドとバックエンドそれぞれのリードタイムを計測した結果を示しています。 フロントエンド バックエンド リードタイムが100時間を超えていた状況から大幅な改善ができました。特にフロントエンドに関してはマージまでの時間が8時間を切っていることから、高速な開発を実践できていると言えます。 フロントエンドのアクティビティの変化についても見ていきましょう。モノリス解体が実施された2021年と比較すると、近年のアクティビティ量は著しく増加しています。 2021年 2023年 もちろんこれはメンバーの増加が主な要因として考えられますが(採用チームの皆さんいつもありがとうございます)、それだけでなく、一人あたりのアクティビティが増えた(=一人あたりのパフォーマンスが向上した)ことも結果に貢献していると思います。 2021年(1人あたり) 2023年(1人あたり) 2021年当時の倍のアクティビティを示しているので、単純計算で開発生産性も倍になっていると言えますね!頑張った甲斐がありました 💪 まとめ ファインディでは「モノリスの解体」「開発基盤の刷新」「CI の高速化」を実施し、開発生産性を向上させることに成功しました。 数年前まではファインディも開発スピードに伸び悩んでいたと知って驚いた方も多いかもしれません。ここ数年で大きく成長できたのは社内の優秀なメンバーの協力あってのものだと感謝しています。 ここまでご覧いただいた方は、 ファインディはそこまで特別なことをやっている訳ではない とお気付きになられたかと思います。 1つ1つの積み重ねが今の私達の開発を支えています。 日々の改善を大事にしていきましょう。 ファインディは積極的にエンジニアを採用しています。CI/CD を始め、Four Keys、開発生産性、技術トレンド、転職市場など興味のある方は、お気軽にカジュアル面談を受けてみてください。 ファインディの採用情報はこちら ↓ herp.careers *1 : https://dev.to/puku0x/angular-react-2h4j
アバター
こんにちは、あるいはこんばんは。 @gessy0129 です。 ファインディは昨年に続き RubyKaigi 2024 でPlatinum Sponsorsとして協賛します。RubyKaigi 2018からここまで継続して協賛できているのは、様々な御縁のおかげだと感じています。Rubyコミュニティおよびファインディを応援してくださる方には感謝の気持ちでいっぱいです。 Platinum Sponsorsとして実施すること 今年のファインディは、ブース出展とDrinkupを2件実施させて頂く予定です。 Drinkupはconnpass上にオープンさせて頂いています。 findy.connpass.com findy.connpass.com ほぼほぼ埋まってしまいましたが、ぜひご参加頂けると嬉しいです! ブースには、Findy Team+の開発リーダーである @ham0215 をはじめ、 @yuichiro826 、CTOの @ma3tk やVPoEの @kxmxyx が参加します。僕もいる予定ですので、ぜひ遊びに来てください! ブースはこちらです! ブース内容の全貌については、現在、最終の内容をFixさせるための作業を実施中です。ご期待頂ければ幸いです! 一部、公開させて頂きますと、Ruby Code Quizというクイズを用意しております。開催期間中は内容が毎日変わるので、ぜひ毎日挑戦しにきてください! 前年からの振り返り 昨年のRubyKaigiから早一年が経過しました。この間のファインディの変化を、一年前と比較しながらご紹介します。主にRubyに関係することをメインにピックアップさせて頂きました。 プロダクト開発状況変化 Findy Team+ を使って、弊社がRubyで開発している主要バックエンドリポジトリの確認をしました。 2023年4月と2024年4月の対比です。 直感的に分かりやすく増加してますよね! グラフだけだと分かりにくいかもしれないので、具体的な数字で表現させて頂きます。 コミット数: 1.49 倍 PR数: 1.56 倍 コミット人数: 1.34 倍 オープンからマージまでの平均時間: 13% 削減 人数が増えると開発時間が長くなったり指標が悪化することもあるのかな?と思いながら見ていたら主要スタッツが軒並み向上していて正直言って驚きました。 普段はこういうグルーピングで見ることがないのでなかなかに新鮮な図です。 Ruby biz Grand prix 2023 ソーシャルインパクト受賞 rubybiz.jp RubyKaigi 2023の参加をきっかけにRuby bizにも応募させて頂きまして、ソーシャルインパクト賞を受賞することが出来ました。本当にありがとうございます。 今回のRubyKaigiにも参加予定の @ham0215 からのRubyに関する熱いコメントもあり、これから先、Rubyへの貢献をより一層強めていきたいと考える事が出来ました。 この度はソーシャルインパクト賞をいただくことができ、誠に光栄に思います。エンジニア組織の開発パフォーマンス向上を支援するSaaS「Findy Team+」が、日本発の「Ruby biz Grand prix 2023」で受賞をできましたこと、大変喜ばしい限りです。当社では、今回受賞した「Findy Team+」だけではなく、多くのプロダクトでRubyが使われており、Rubyコミュニティや「Ruby on Rails」をはじめとした多くのライブラリなどのおかげでユーザーへ爆速で価値を届けることができております。今後も「Ruby」への恩返しの意味も込めて、Rubyを活用したサービス開発やエンジニア向けのイベントや事例記事の公開などを行い、Rubyコミュニティの発展に寄与してまいります。 ( エンジニア組織支援SaaS「Findy Team+」が「Ruby biz Grand prix 2023」でソーシャルインパクト賞を受賞 | ファインディ株式会社のプレスリリース の受賞コメントから引用) Rubyを使用した新しいプロダクトFindy Toolsがリリース Findy Tools は数ヶ月でプロダクトリリースができ、現在も着実に成長中のサービスです。プロダクトとしては未成熟な部分もありますので、皆様からのフィードバックも数多くお聞きしながらプロダクトに反映していきたいと考えています。ブースなどでぜひお聞かせ頂ければ幸いです! Findy Toolsに関する具体的な内容はテックブログに記載をさせて頂いたのでそちらもぜひご参照ください! tech.findy.co.jp いかがでしたか?ファインディのこの一年の歩みをダイジェストでお届けしました。 ファインディ社は引き続きRubyとともに成長していければと考えております。 そして、一緒に働くメンバーを絶賛募集中です。 興味がある方はこちらから ↓ herp.careers おまけ ファインディは4月29日にオフィス移転をしました!移転後のオフィスの会議室にもRubyがあります!Rubyでファインディ株式会社が大きくなったこともあり、会議室サイズは比較的大きめです!
アバター
こんにちは、2024/3/18 からファインディに入社した本田です。 ファインディでは、Findy Team+ という、エンジニア組織の開発生産性を可視化し、開発チームやエンジニアリングメンバーのパフォーマンスを最大化するためのサービスの開発に携わっています。 今回は、入社して一ヶ月ちょっとが経ったので、入社して新メンバーとしていいなと思ったことをご紹介したいと思います。 オンボーディングがわかりやすい 入社すると初めにオンボーディング用 Issue が作成、アサインされていて、いつまでにどういうことができるようになっているために何をしてくのか、というのが一通りまとまっていて、すごいわかりやすかったです。 オンボーディング用 Issue には、次のようなことが記載されています。 新メンバーがキャッチアップすべき事項の一覧 環境構築手順や必要なツールのセットアップ手順、設定項目など コーディング規約やその元となる思想 携わる各リポジトリの役割や、各アプリケーションのアーキテクチャ 各種定期ミーティングの一覧やその内容 いつ頃までにどういう業務ができるようになっているかの期待の目安 メンバーの経験、スキル、やっていきたいことなどを鑑みてマネージャーと個別にすり合わせもしている こんな感じにオンボーディングのゴールと期間が定義されていて、 この後にこれらのゴールに向けて知るべきことやそのための資料が記載されている感じです。 初日からこの Issue を見つつメンターの方と密にコミュニケーションを取りながら環境構築や携わるプロダクトの理解をスピード感持ってキャッチアップできました。 個別に知るべきことを逐次教えてもらうのではなく、全体として何を知ってどういう状態になるべきかがわかると、必要に応じて能動的に情報をキャッチアップする動きもでき、非常にやりやすく感じました。 good first issue からコードで貢献できる 急ぎでない簡易かつ新しく参画した人向けの Issue を good first issue というラベルを付けて管理していて、新メンバーはまず good first issue から着手して、開発の流れや携わるプロダクトのコードベースを理解、習得していきます。私もこの流れで開発を始めました。 各 good first issue の Description は新メンバーでも理解できるようちゃんと書かれていて、前述のオンボーディング Issue で紹介されているドキュメントも読みつつ理解しながら Issue を対応していきます。 実際私も他のオンボーディングプログラムを受けつつ3週間ほど good first issue を対応していきましたが、15営業日で 34 PR を出してコードで貢献できました。 他のオンボーディングプログラムもあって波はありますが平均して3PR/日くらいできています もちろんオンボーディング期間はしっかりインプットすることが大切ではありますが、それもやりつつ入社してすぐに何かしら貢献できることは自信にも繋がりましたし、いいバランスでインプットとアウトプットを両立できました。 レビューがすごく速い めちゃくちゃ速いです。 PR のレビュアーをアサインしてだいたい 10 分以内、遅くても 1-2時間以内にレビューされます。 もちろんレビュアーの方の状況にもよりますが、体感7-8割くらいの PR は 10 分くらいでレビューされる印象です。 レビュアー一人当たり4件/日、平均しても1h以内にレビューされてます これにはいくつかの要因がありますが、一番大きいのはバッチサイズを小さくしてリードタイムを短くすることで手戻りやコンフリクトを防ぐ、ということが文化として根付いていることが大きいと感じています。 そのために、 PR の粒度を極力小さくする レビュアーは極力優先してレビューする といったことが徹底されています。 なお、ここらへんの話は先日開催された Qiita Conference での弊社CTO佐藤の発表で詳しく触れていますので、ご興味ある方はぜひご一読ください。 speakerdeck.com まとめ 他にもご紹介したいことはたくさんありますが、今回は特に新メンバーにとっての開発者体験について3つピックアップして紹介させていただきました。 入社して1ヶ月経過しての感想としては、ファインディは開発スピードを大切にする組織文化が根付いているなと思いました。 より良いプロダクトを作ってより多くのユーザーに良いプロダクトを提供するためにも、開発スピードはその源泉になるものだと思っています。 それをエンジニアとして各々が理解して体現している組織なので、私にとって新しい挑戦ができる環境だと感じています。 このスピード感を自分も体現し、プロダクトの成長と自分の成長を重ねながら良いプロダクトを作っていきたいです。 ファインディでは一緒に働くメンバーを絶賛募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
こんにちは。 FindyのTeam+を開発している西村( sontixyou )です。 【エンジニアの日常】エンジニア達の自慢の作業環境を大公開 Part1 と題して、公開したブログが好評でした。 それに続いて、弊社エンジニア達の作業環境を見ていきましょう! 作業環境を大公開 西村 私は、現在週3日ほど出社と残りはリモートワークしています。そんな私の作業環境をご紹介します。 デスクの全体像はこのような感じです。 デスクは新卒時代の先輩からおさがりです。幅120cmのものを使用しています。 ディスプレイはDELLの27インチ 4Kモニタを2枚使っています。1枚だけ縦置きにしている理由は、省スペース化と首の振り向きが大変だからです。 横置きのディスプレイでは、エディタとSlack専用になっています。 縦置きのディスプレイでは、ブラウザ専用になっています。ウィンドウを垂直に2枚置いて活用しています。 ディスプレイはエルゴトロンのアームで支えています。ディスプレイの縦置きを実現するために欠かせないアイテムです。また、ディスプレイの台座があると場所を取るためです。 Webカメラは10年前に買ったlogicoolのものを使用しています。マイクが内蔵されているため、外付けマイクは用意していません。そろそろ買い替えしたいので、乗り換え先を探し中です。 USBハブには、 サンワサプライのUSBハブ を使用しています。このハブは、HDMI(4k/60Hz対応)1本とUSB3.0 2本とUSB Type-Cの充電ケーブルを指すことができます。 キーボードは keychronのK6 Pro を使用しています。 キースイッチは Keychron Silent K Pro Switch を使っています。打鍵感がとても良いため、自宅用とオフィス用で2台購入してしまいました。 マウスは、 logicool ERGO M575S を使用しています。 手首を動かさせずに、カーソル移動ができます。そのため、手首の疲れは全然ありません。また、ボールを取り外すことができるため、マウス内部の清掃やボール交換が楽々です。 自分が好きなものをデスクに置くことで、仕事終わりまでモチベーションをキープしつつ仕事ができます。 まずは、フィギュアです。私は 海洋堂 制作のフィギュアが好きなため、ガチャポンで当てた怪獣ネロンガと恐竜を置いています。縦に長いものはミイラ展で当てた猫のミイラを題材にしたフィギュアです。 デスク横には、PS5を置いています。仕事終わった瞬間に、ゲームプレイできるためとても快適です。 仕事を快適かつ集中できる場所になるように意識して、デスク環境を整えています。 浜田 次は浜田( ham )の作業環境を紹介します。 コロナ禍のときにリモートワークを始め、コツコツリモート環境を整えました。 私のポリシーは「日々使うものは良いものを!」なので、予算の許す範囲で妥協せずに自分が良いと感じるものを選ぶようにしています。 ディスプレイは2枚ないと開発効率がガタ落ちするタイプのエンジニアなのでデュアルディスプレイです。 右側は4Kで31.5インチ、左側は27インチです。 4Kディスプレイは、ブラウザやZoomやGoogle Meetなどを表示しています。 27インチディスプレイは、Slackやメール、エディターなど文章を扱うツールを表示しています。 4Kディスプレイはメーカーなどにこだわりはないのですが、USB Type-Cで出力とPC本体への給電ができるものを選びました。ケーブルが少ないのは正義!! また、こちらのディスプレイは足が大きかったのでモニターアームを使っています。 デスクスペースを拡げるためにモニターアームを買ったのですが、モニターの角度を簡単に変えられるため、モニターの背面の配線を変えたり、モニター周りの掃除もしやすくなり地味にストレスだったことも解消されました。 27インチディスプレイは解像度などのスペックは劣りますが、文章を書く場合に4K解像度だと文字が小さくて使いづらいので、必要十分なスペックだと感じています。 リモートワークにおいてオンライン会議は生命線です。こちらの画質や音質が悪い場合は相手にストレスを与えてしまう可能性があります。 そのため、 Webカメラ / マイク は良いものを選ぶように心がけました。 また、自室では前からの光が当たらず、顔が暗く見えていたためライトも買いました。見た目の印象も大事です。 椅子は Ergohuman PRO2 Ottoman を使用しています。足を伸ばせるようにオットマンがついているものにしましたが、全然使わないので不要でしたw なお、足置きと一緒に使うことで椅子の高さ調整がやりやすくなるので、足置きとセットで使うことがお勧めです。 冒頭に書いた「日々使うものは良いものを!」のキーボードから始まりました。 REALFORCE から始まり、HHKB Professional(手元にないので写真なし)、 HHKB Professional Hybrid Type-S 、そして今は HHKB Studio を使っています。 また、分割キーボードを使っていたこともありました。 Corne Cherry V3 はキーが少ない代わりにレイアーを活用するのですが、使いこなせず挫折しました・・・ HHKBライクな 7sPro は、気に入っていたのですがキーの反応が鈍いことが多々ありストレスを感じたのでHHKBに戻りました。 髙橋 週に2日程度リモートしている髙橋( @nokv )です。 オフィス勤務が主体のため自宅はシンプルな環境にしています。 デスクの全体像はこんな感じです。 200cm程度の大きいデスクを妻と半分ずつ使っています。 妻はリモートワークが多いため、MTGが被った際はどちらかが移動しないといけないなど不便なこともありますが、作業の合間に会話がしやすくリフレッシュできるので1つのデスクにして良かったと感じています。 自分の作業環境はMacBook Proと外部ディスプレイを縦並びに配置したシンプルなデュアルディスプレイ構成で作業しています。 以前はもう1枚ディスプレイがあり横に配置していましたが、首や目に負荷がかかると感じたため現在の構成にしました。 外部ディスプレイは DELLの27インチの4Kモニタ を使っていて、PCへの給電もケーブル1つで可能な点がお気に入りです。 エディタやGitHubなど閲覧頻度の高いものは外部ディスプレイに表示し、首への負荷を軽減するため視点がなるべく下がらないようにしています。 キーボードはNuPhyの Halo75 で、スイッチはNight Breezeを使っています。打鍵感と静音性のバランスが良く、長時間使用しても疲れにくいのが気に入っています。 MacBook Pro本来の配置に慣れているため、マウスやトラックパッドを別で用意することはなくいわゆる尊師スタイルにしています。 椅子はHerman Millerの セイルチェア を使っています。椅子の選択は体の負荷に直結するので機能性の高さを重視しました。それに加えて普段はhamさんと同じように足置きを使っています。 性格的にものが多いと落ち着かないので、必要最低限のものだけを配置して作業に集中できるような環境を作っています。 まとめ いかがでしたでしょうか? 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
こんにちは。 Findy Freelance の開発チームでエンジニアをしている 2bo です。 この記事では私が開発生産性を上げるために開発をする前に考えていることについて書きます。 ここで「開発をする前」というのは次のようなタイミングを指します。 PdMなどから新規施策の仕様について相談を受けたとき 起票された開発Issueを最初に確認するとき 自分がIssueを作成するとき なぜこのタイミングで考えるかというと、開発を進める上での方向性を間違える可能性を減らし後から軌道修正をしやすくするためです。 なおこの記事においては、開発生産性を「開発成果物の提供価値を投入リソースで割ったもの」とします。 いくら頑張って開発をしても、そもそもやるべきことの方向性を大きく間違えると提供価値が0に近づくため開発生産性が低下します。 特に開発が高速なチームで方向性を誤ると高速に間違った方向へ進んでしまうことになります。 そのような事態を避け、提供価値を少しでも上げるために考えていることを記します。 なぜ、いつ、だれがつかうか? 開発する必要があるか?しなくて済む方法はないか? シンプルにできないか? リリースを分割できないか? 実例の紹介 プロジェクトの概要 請求フローの理解 設計の見直しによるシンプル化 スコープの決定とリリースの分割 終わりに なぜ、いつ、だれがつかうか? 大前提にはなるのですが、まずは以下を考えます。 なぜ:なぜその機能が必要なのか?ユーザーはなぜこの機能をつかうのか? いつ:いつその機能を使うのか?そのときユーザーはどういう状況にいるのか? だれ:どういう属性のユーザーか?ユーザーが社内にいる場合、どの部署/役割の人か? これらの情報はありがたいことに PdM から普段提供してもらえています。しかしながら自分でも考えるようにしています。 エンジニアの視点からも考えることで、それを実現するための別の形の機能やアプローチが良いのではないかと提案できることがあります。 たとえ提案が採用されなかったとしても現行案の妥当性を確認できるため、方向性を間違える可能性を減らすことができると考えています。 開発する必要があるか?しなくて済む方法はないか? 提供価値を極力変えないまま、投入リソースを減らせるアプローチがないか次のことを考えます。 既に同じ機能が存在するか、または全く同じではないものの実質的に同じ提供価値を持つ機能があるか? 既存の機能を利用、または組み合わせて実現できるか? 外部サービスやライブラリをつかうことで実現できるか? 例えば、画面Aで情報の表示追加を要望された際、その情報をすでに表示している画面Bがあるため、画面Aへの直接追加ではなく、画面Bへのリンクを設けるだけで十分というケースがあったりします。 このように要望機能そのままでなくても必要十分をみたせば問題ないというケースでは、開発リソースを減らせることがあります。 シンプルにできないか? 機能が複雑になるとその後のメンテナンスや改修の工数が増加したり、作り直しが発生したりしてトータルで見ると生産性が落ちるということが起きがちです。 そのため次のことを考えます。 複数の関連性のない異なる目的を担う機能になっていないか? その場凌ぎでワークフローや処理の分岐を増やすことになっていないか、またはそれらを極力減らして開発できないか? 過度に自動化、抽象化することでなにをしているかわからなくなったり、イレギュラーケースに対応しづらくなっていないか? 私は過去にシンプルさよりもリードタイムを優先して機能追加をしたことがあります。その時は本来異なる目的をもつ既存機能に付け足す形で機能追加をしました。 最初のリリースまでのリードタイムは短縮できましたが、処理の分岐とデータのパターンが増えてメンテナンスとデータ集計がしづらくなり、結局は一から作り直すことになりました。 トータルで考えると最初からシンプルに別の機能として設計/開発をしたほうがよかったと考えています。その反省も踏まえての内容になります。 ただし、特にベンチャー企業においてはどうしてもリリースまでのリードタイムを短くしなければならない状況もあると思います。 そのため完全に否定できることでもないですが、単にそのほうが楽だからという理由ではやらない方がよいと考えています。 リリースを分割できないか? ユーザーへの価値提供を考えて立案された施策や機能が、本当に有効かどうかは実際に提供してみないとわかりません。 また、大きな施策の場合は必須なものと必須ではないがあれば嬉しいというものが混在しがちだと考えています。 それらすべての内容をつくってからリリースする場合、期待通りの結果が得られなかったときに投入した開発リソースを無駄にしてしまう可能性があります。 これを避けるため、今回の開発で一番実現したいことはなにか?を考えて、もっとも重要なものから段階的にリリースすることを考えます。 いわゆるMVP(Minimum Viable Product)を開発ごとに考えるようなイメージです。 次のメリットがあると考えています。 ユーザーに素早く価値提供ができる 効果やユーザーのフィードバックを早く得られる 結果が素早く得られるため、軌道修正や撤退の判断が早期にできる リリースあたり開発ボリュームを減らせるため見積もりのブレを減らせる エンジニアのモチベーションの観点からも、早期にリリースすることで開発者のモチベーション向上につながりやすく、すべて作り込んで無駄になった場合のモチベーション低下も回避しやすいと考えています。 実例の紹介 ここからはこれまで紹介した考え方を実際に活かした「請求フローのシステム化」プロジェクトの事例を紹介します。 プロジェクトの概要 Findy Freelanceではユーザーの稼働後サポートとして請求書の作成代行と業務委託報酬の代理徴収を行っています。 そのため、ユーザー個々の稼働状況を確認し、内容に応じて請求書を作成して企業に送信する必要があります。 これらの作業の多くは手作業で行われていました。サービス利用者の拡大に伴い運用の限界が見えたため、請求フローをシステム化するプロジェクトが立ち上がりました。 請求フローの理解 私は開発リーダーとしてプロジェクトを途中から引き継ぎました。その時点で請求業務のドメイン知識がほとんどありませんでした。そのためPdMにヒアリングしながら請求フローの図を作成し、どの役割の人がどのタイミングで何をするのかを把握しました。つまり「なぜ、いつ、だれが?」の観点を確認しました。 情報を整理することでエンジニアユーザー、企業担当者、社内の営業担当者および経理など多くのステークホルダーが関与しているとわかりました。以後の開発で各々の視点を意識でき、先を見据えながら起案時よりもさらに価値のある仕様に繋げることができました。 設計の見直しによるシンプル化 次に、設計の見直しを行いました。プロジェクト引き継ぎ前は、手作業で管理されていた情報をそのままデータベースに移す形で設計が進んでいました。 例として、Findy Freelanceの契約スキーム上「企業とファインディ間の契約条件」と「ユーザーと企業間の契約条件」の2種類の契約情報が必要です。 当初はこれらを1つのテーブルで管理する設計でしたが、本来は別の情報であり、それぞれの契約変更のタイミングも異なるため、別のテーブルで管理することにしました。 これによりテーブルとモデルクラスの役割が明確になり、シンプルになりました。 また、2種類の契約情報がそれぞれいつ変更されたのかも管理できるようになりました。 設計の見直しには時間がかかりましたが、その後の改修や運用コストを考慮すると、見直して良かったと考えています。 スコープの決定とリリースの分割 プロジェクト引き継ぎ時、システム化に必須な機能と便利な機能がIssueに混在していました。 せっかくシステム化するならと多くの要望が積み上がっていましたが、スコープが不明確でした。 そのため、PdMを中心に関係者と協議してスコープを決めました。 早期の運用開始を目指し、必須機能のみを最初にリリースしました。例えば、次の機能は最初のリリースから除外しました。 社内管理画面の利便性向上 ダッシュボードの作成(初回リリースではBigQueryからのデータ参照で代替) ユーザーがログインページから報告した稼働時間の確認(報告時にユーザーへメール送信しているため) これらの機能は実運用後に必要性を再検討することにしました。スコープを決定し、リリースを分けることで運用開始までのリードタイムを短縮しました。 また、実運用をしたことで発覚した必要となる機能や改修があり、最初からすべて作りこまなくてよかったと考えています。 終わりに 以上が開発をする前に私が考えていることとその実例です。誰かの参考になれば幸いです。 ここに書いてあるようなことは普段から考えているという方も多いのではと思います。 読んでくださった方々が開発に際して考えていることなど是非コメントをいただけると嬉しいです。 また、ファインディではエンジニア領域のプロダクトの開発をするため、開発エンジニアが提供価値を考えるにあたり自然とユーザーの視点にたって考えることがしやすい環境であると感じています。 エンジニアへの価値提供に少しでも興味を持っていただけた方は、ぜひカジュアル面談のお申し込みをお待ちしております。 ファインディの採用情報はこちら↓ herp.careers また、ファインディの開発メンバーが登壇するイベントを4/24(水)12:00~13:00で実施します! 開発生産性や開発のパフォーマンス向上にご興味ある方はconnpassページより是非お申し込みください! developer-productivity-engineering.connpass.com
アバター
こんにちは。 Findy で Tech Lead をやらせてもらってる戸田です。 先日、弊社から Findy Tools がリリースされました。 今回は、そのFindy Toolsがどのようにして開発されたのか、開発の背景や工夫点などを紹介していきます。 それでは見ていきましょう! Findy Toolsの概要 紹介 Findy Tools は開発ツールに特化したレビューサイトです。第三者の視点で実際にツールの選定をした企業の生の声を集めることで、ツール選定に関する不安を解消し、導入検討に必要な情報を提供します。 「Findy Tools」を開発ツールの導入検討をしているユーザーが利用すると、実際にツール選定をした大手企業やメガベンチャー企業の技術責任者やエンジニアによるレビューを集めることができ、導入検討がスムーズになります。 また、開発ツールを掲載するベンダーには、実際の利用企業の声を活かしたコミュニティマーケティングによる新規顧客の獲得や、認知向上をご期待いただけます。 システム構成 システム構成は次の通りになっています。 Ruby on Rails React Next.js App Router bulma GraphQL AWS S3 CloudFront Aurora Fargate 上で挙げた構成に特に強いこだわりはなく、既存資産の流用をしやすかったことと、リプレイスや変更しやすいという理由でこの構成になっています。 APIとフロントエンドを疎結合で設計し、どちらか一方を作り直しやすくしたり、GraphQLを用いることでqueryそのものを変更することなく、呼び出し先のエンドポイントを切り替えることでアプリケーションの振る舞いを変えずにAPIを切り替えることができるような構成にしています。 開発 スケジュールとリソース 今回の開発はα版、β版と2回に分けて段階的にリリースしました。 α版は2023年9月から2023年10月で開発し2023年11月上旬にリリース、β版は2023年11月から2024年1月まで開発し、2024年1月下旬くらいにリリースしています。 開発リソースはα版は1人、β版は2024年に入ったあたりから2人で、それに加えてインフラ担当1人で開発をしました。 基本的にβ版リリースまではアプリケーション側の1人で開発していました。 新規サービスが当たるかどうかは実際に世に出してみないとわからないので、「最小限のリソースで最速でリリースする」という方針でした。 開発のポイント 既存資産を活用 最小限のリソースで、最速でリリースするという方針だったので、まず「全てを1から作る」という選択肢は可能な限り避けました。 幸い弊社には既に運用されてるシステムがあり、そこからの流用を決めました。 具体的には次の内容を流用しました。 アプリケーションの実行基盤まわりの共通コード フレームワークの各種設定ファイルや設計思想 デプロイやCIのワークフローのコード 共通コードや各種設定ファイル、設計思想は既存の各サービスでほとんどが統一されており、コピペして少しいじったらすぐに使えるような状況に整備されています。 またデプロイやCIまわりは全てGitHub Actionsに統一されており、こちらもワークフローのファイルをコピペして少しいじるだけですぐに使えるようになっています。 これらの既存資産を流用することで、コードを書き、テストで守り、動作環境にデプロイし、最低限動くものを確認できる環境をすぐにセットアップできました。 ゼロからモノを作るうえで動くものを用意することは非常に重要です。動くものをメンバーと共有して確認することで、認識の齟齬を防ぎ、余計な修正作業を減らすことが出来ます。 データ投入を簡略化 データ投入は開発の中でも時間を取られる作業の1つです。 レビュワーの方から頂いたレビューデータや、各種マスターデータをデータベースに投入する必要があります。 α版の時点ではデータ投入を簡略化するために、全てのデータをseedで投入することにしました。 本来であればデータ投入用の管理画面を作るのがベストなのですが、α版のリリース前に1人で開発してる状況で管理画面を作るようなリソースはありませんでした。 機能の取捨選択 初期リリースの目的は多くのユーザーを獲得することではなく、「サービスが受け入れられるかどうかを可能な限り早い段階で判断する」ことにあります。 そのため、「サービスが受け入れられるかどうかの判断に必要のない機能」を初期リリースのスコープから外しました。 具体例を挙げると、β版リリースまでは画像アップロードの機能を外しました。 レビューデータやアバター画像などをアップロード出来るようにしたかったのですが、画像アップロードを実現しようとすると、色々と考えないといけないことがあります。 画像のアップロード先はどうするのか?削除は許可するのか?画像サイズはどこまで許すのか?などなど色々出てきます。 今回は画像のアップロードを許可せず、運営チームが用意した画像をS3に直接アップロードして、そこへのパスをseedに直接書くことで画像ファイルの管理を単純、簡略化しました。 この機能を簡略化するだけだと良くて1,2日程度の開発工数削減に留まると思います。しかし、このような取捨選択をいくつかすることによって、結果的に半月や1ヶ月程度の工数削減に繋がりました。 この機能を削減して大量に工数削減、といったものではなく、ミニマムな削減の意思決定を大量に行うことによって実現しているのです。 テストコード 色々な取捨選択の意思決定の中で、テストコードだけは切らずに必ず用意していました。 「時間が無いからテストを書かないのではなく、テストを書かないから時間が無い」というのは事実で、結果的にテストが守ってくれているおかげで、余計なことに時間を取られずにやりたいことに集中できるのです。 テストコードのお陰で、α版リリース以降に本番環境で発生した不具合、障害は記憶しているだけですが片手で数えられる程度でした。 管理画面を内製 α版のリリースにより、「このサービスが世に受け入れられる」ことを確認できたため、β版のリリースに向けて着手しました。 α版からβ版に向けて何を実現させるのかを検討している間、開発するものがなくて時間を持て余していたので管理画面を内製で実装することにしました。 各種マスターデータやレビュワーの方から頂いたレビューデータなど、運営で管理したいデータは多数あります。 それらの変更のたびにseedの再実行、データの変更を依頼するようなフローにしてしまうと、エンジニアが機能追加の作業に集中できなくなってしまいます。 β版リリースからはエンジニアを介さずにデータの追加、変更を運営メンバーだけで完結出来るようにしました。 管理画面の画面数は25個程度で、APIとフロントエンド込みで1人で2、3週間程度で開発しました。 管理画面はSSRが必要ないので純粋なReactのSPAで、CSSフレームワークに Bulma を採用しました。 管理画面を用意できたことによって、β版以降の開発時に運営メンバーからのデータ管理周りの依頼が無くなるので、開発作業に集中できるようになりました。 反省点 Findy Toolsの開発では、チャレンジ枠としてNext.jsのApp Routerを採用しました。 既存プロダクトに用いられているPages Routerからの移行を検証する狙いもありましたが、今回は上手くいかなかったと反省しています。 誤解を与えないように言うと、Next.jsのApp Router自体は悪いものではありません。 何が上手くいかなかったかというと、既存資産の流用をした結果、利用していたライブラリがApp Routerに対応出来ておらず、想定していた以上の工数を取られてしまったことです。 弊社ではApp Routerを初めて採用したケースであり、社内にノウハウが蓄積していなかったため、問題解決に時間がかかってしまいました。 今後、他サービスでのApp Routerの採用時に今回のノウハウを活用できるようになったと思いますが、今回のような開発スピードを優先されるケースで採用するべきではなかったかなと思います。 まとめ いかがでしたでしょうか? 新規サービスの開発時にはリソースやスケジュールに大きな制約が課せられます。そのような状況下でやるべきこと、やるべきではないことの取捨選択の意思決定が非常に重要になってきます。 半年近く1人で開発をしてきた Findy Tools ですが、みなさまのおかげもありサービスが順調に成長しており、今では開発に着手しているエンジニアの人数を増やすことができました。 今後もFindy Toolsをより良いサービスにしていくために、引き続き開発を進めていきます。 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
Findy Team+でフロントエンドエンジニアをしている 川村( @peijun333 )です。 Findy では、フロントエンドのコード品質と安定性を確保するために Jest などのテストフレームワークを積極的に活用しています。通常、Jest は CLI から実行してテスト結果をコンソールで確認しますが、コマンドを用意する手間や、テスト経過のデバッグのために都度 console.log などでその内容を確認しなければならずとても不便です。 そこで、今回はテストの自動化とリアルタイムなフィードバックを提供する JavaScript の統合テストツールである Wallaby.js を紹介します。Wallaby.js を導入することで、開発効率の向上が期待できます。 Wallaby.js とは? 前提条件 VS Code でテストの修正 Wallaby.js はリファクタリングに強い スナップショット の更新 ユニットテストにおけるデバッグ機能の紹介 Wallaby.js を使ってみて 最後に Wallaby.js とは? Wallaby.js is an integrated continuous testing tool for JavaScript. It runs your tests immediately as you change your code (you don’t even have to save the file) and displays various results (including the code coverage, error and console messages) right inside your code editor, next to your code. Wallaby.js is great for doing JavaScript TDD (Test-driven development) or BDD (Behavior Driven Development), but it works great for other approaches as well. 公式ドキュメントより Introduction: What is Wallaby.js? Wallaby.js は JavaScript の統合テストツールで、次のような機能があります。 リアルタイムなフィードバック Wallaby.js は、テストの実行に手動のステップが不要なため、コードを変更するとリアルタイムで結果をフィードバックしてくれます。 エディタ内でのテスト結果表示 Wallaby.js は、コードエディター内にテスト結果を表示してくれます。エラーやカバレッジ情報などが、コードの隣に直接表示されるため、エディタから離れることなく、テスト結果を確認できます。 デバッグ機能 Value Explorer を使用することで、任意の場所で値を調査し、呼び出し元のコールスタックを確認できます。また、コンソールログをエディタ上に表示する機能も備えており、デバッグプロセスをスムーズにします。 また、サポートしているコードエディタとテストフレームワークが 豊富 で導入がとても容易です。 Wallaby.js は OSS 開発では無料で利用できますが、商用利用の場合は 有料 です。ファインディではフロントエンドを開発するメンバーにアカウントを発行しています。 前提条件 ここから実例を交えて機能を紹介します。実例に使った動作環境は以下の通りです。 VS Code VS Code 上に Wallaby.js の拡張機能 をインストールする必要があります VS Code 以外のコードエディタ もサポートされていますが今回は VS Code を使った紹介になります React version 18.2.0 Jest version 29.6.1 React Testing Library version 14.0.0 Wallaby.js の導入に関する詳細な手順や設定方法については、 公式ドキュメント を参照してください。 VS Code でテストの修正 Wallaby.js は サポートされているエディタ 上で動作します。今回は VS Code で普段テストを書いている様子を紹介します。 以下は、GraphQL を用いてユーザーデータを正しく取得できているか確認するテストの例です。 + expect(result.current.users).toStrictEqual([{ id: '1', name: 'name' }]); - expect(result.current.users).toStrictEqual([{ id: 1, name: 'name' }]); id が number 値ではなく string の '1' であるべきなのですが、上記動画の例では誤った結果を書いています。 Wallaby.js ではこのようにテスト結果がコードエディタ上(VS Code の例)にリアルタイムに反映されます。 予想される結果と実際の結果が異なる場合、赤色のテキストでエラー文が表示されます。そこにカーソルを合わせることで、具体的に差分内容を VS Code 上で確認できます。 - Expected - 1 + Received + 1 Array [ Object { - "id": 1, + "id": "1", "name": "name", }, ] また、VS Code 上では左側の余白に四角のテストカバレッジインジゲーターが 赤色 から 緑色 に変わった様子が確認できたかと思います。これにより視覚的にテストのカバレッジを確認できます。 詳細: Editor Code Coverage Indicators この機能のメリットは、エディタ上でテストの操作が完結でき、さらにリアルタイムに優れている点です。テストの実行や結果、失敗時のエラー原因の迅速な特定、ナビゲーション、差分ビューなど、すべてをエディタ上で行うことができます。 Wallaby.js はリファクタリングに強い 特にリファクタリングでは、 Wallaby.js の機能の 1 つである「リアルタイムにテスト結果がフィードバックされる機能」が発揮されます。 以下は ユーザーデータから空の name を取り除けることを確認するテストの例です。 テスト結果と関数のインターフェイスはそのままで、ロジックのみをリファクタリングしています。こちらの例のように、リアルタイムにテストが失敗し、リファクタリング後ファイルを保存せずともテストが成功しました。 このようにリアルタイムにテストが落ちてくれるので、関数の影響範囲がわかりやすく、テスト結果からすぐにフィードバックされることで効率よくリファクタリングが可能です。 スナップショット の更新 次にスナップショットテストの更新について紹介します。スナップショットテストとは、UI が予期せず変更されていないかを確かめるテストです。Findy Team+では @testing-library/react を使い コンポーネントのスナップショットを撮影し、必要に応じて差分を更新しています。 はじめに Wallaby.js を使わずに Jest のスナップショット を更新する手順をみてみましょう。 次の例は title と description を表示するコンポーネントのスナップショットテストです。 import { render } from "@testing-library/react" ; import { TestComponent } from "./test.component" ; const props = { title: "title" , description: "description" , } ; describe ( "TestComponent" , () => { it ( "表示確認" , () => { const { asFragment } = render ( < TestComponent { ...props } /> ); expect ( asFragment ()) .toMatchSnapshot (); } ); } ); 実装コード type Props = { title: string ; description: string ; } ; export const TestComponent = ( { title , description } : Props ) => { return ( <> < div > { title } </ div > < div > { description } </ div > </> ); } ; @testing-library/react の render 関数を使いスナップショットを撮影しています。このテストを初めて実行すると次のようなスナップショットファイルが作成されます。 // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`TestComponent 表示確認 1`] = ` <DocumentFragment> <div> title </div> <div> description </div> </DocumentFragment> `; 仕様の変更により、description を表示するタグを <span> タグに変更してみます。 - <div>{description}</div> + <span>{description}</span> そしてテストを再度実行すると UI の差分によりテストが失敗します。 ● TestComponent › 表示確認 expect ( received ) .toMatchSnapshot () Snapshot name: `TestComponent 表示確認 1` - Snapshot - 2 + Received + 2 < DocumentFragment > < div > title </ div > - < div > + < span > description - </ div > + </ span > </ DocumentFragment > 12 | const { asFragment } = render( < TestComponent { ...props } /> ); 13 | > 14 | expect(asFragment()).toMatchSnapshot(); | ^ 15 | }); 16 | }); 17 | › 1 snapshot failed. 今回は description を表示するタグは div ではなく span タグが正しいのでスナップショットを更新する必要があります。 Jest でスナップショットを更新するには --updateSnapshot や -u オプションを使い更新します。 (または ウォッチモードで対話的に更新) 以上の手順を踏むことでスナップショットの更新が完了します。 今回は 1 ファイルのみの修正で変更差分も軽微なものですが、複数のテストが失敗した場合、目的のテストを見つける手間と時間がかかります。また、エディタからターミナルに移動しコマンドを実行する必要があるため、コンテキストを切り替える必要があり、多くの時間が消費されます。 次に Wallaby.js を使ったスナップショットの更新をみてみましょう。 エラーにカーソルを合わせ、差分を確認した後、右上のカメラアイコンをクリックしただけです。 失敗したテストをエディタで確認し、スナップショットを直接更新するだけなので、目的のテストを見つける手間も時間もかかりません。 ユニットテストにおけるデバッグ機能の紹介 Wallaby.js には Value Explorer という機能があります。これを活用することで、デバッグ作業を効率化でき手動でのログ出力作業を大幅に削減できます。 また、テスト実行ログに記録されている値に対して、呼び出し元(実装コード)の Call Stack を調べることもできます。 Value Explorer を使用することで、標準出力をコードに仕込まずともデバッグ作業を可能にしますが、使い慣れている console.log を使ったデバッグも Wallaby.js は対応しています。エディタ上にログの結果を表示できるため、コードとログを同時に確認しながらデバッグ作業を行うことができます。 Wallaby.js は多様なログの確認方法があり、当記事では紹介しきれないので気になる方はチェックしてみてください。 Introduction: Advanced Logging Wallaby.js を使ってみて Wallaby.js を使ってみて特にリアルタイムにテスト結果がフィードバックされる点はとても便利だと感じました。Wallaby.js を導入する前はファイル単位やテストケース単位で、テストを実行させる必要があり時間がかかっていましたが、リアルタイムにテスト結果を反映してくれるので実装に集中して開発を進めることができます。リファクタリングでは、リファクタリングの前後でインターフェイスが保たれているかをエディタ上のテストカバレッジインジゲーターから確認できる点は Wallaby.js の強みだと感じました。 FAIL PASS また、 Smart Start という機能により、コードの変更により影響を受けた箇所のみ自動的にテストが実行されるので、とても高速にストレスフリーで動作してくれます。Findy Team+のように大規模なプロジェクトでも高速に動作してくれるのは非常に強みだと思います。 最後に 今回の記事では、JavaScript の統合テストツールである Wallaby.js について紹介しました。 Wallaby.js の導入により、開発プロセスの効率化やコードの品質・安定性の向上が期待できます。是非、Wallaby.js を活用して、よりスムーズなフロントエンド開発を実現してみてください。 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
こんにちは、あるいはこんばんは。 @gessy0129 です。 このたび、ファインディに入社しましたので、入社エントリーをさせていただきます。 お時間のあるときにご覧いただければと思います。 ファインディに対する想いと気持ち 改めて、この度、ファインディ株式会社に正式に参加することとなりました。 パチパチパチパチ 昨年からいくつかのnote記事を執筆させていただいたところでございますが、それに絡めつつ、今回の気持ちを綴りたいと思います。 参考記事: ANDPADを退任しました|げっしー 2024年目標と決意と挑戦と|げっしー 相変わらず、自身の掲げるミッションは、 半径数メートルから幸せの連鎖を生み出す という事だと思っています。 過去の在籍企業で、VPoEや開発本部長として長い間勤務をしてきました。 その中で、ものづくりに集中しているエンジニアたちを支えていく事というのは相当なやりがいでした。 エンジニアたちが幸せに業務出来る環境を整え、それが連鎖していくようにすること。 これはとても楽しかったです。 ※ もちろんVPoE としての業務はこれだけではないです。 この領域を更に深め、挑戦するエンジニアを応援していきたいという思いが強まりました。 そして、その想いが高まった時、多くの選択肢の中からファインディと出会いました。 ファインディのミッション、ビジョン、バリューに共感し、これは非常に大きな出会いであると感じております。 エンジニアの可能性を拡げ、スタートアップのエコシステムに貢献できることを願っています。 やろうとしてること ファインディは色んな事業を展開してます。 エンジニアの転職を支える転職事業 フリーランスエンジニアを支えるフリーランス事業 国内外のエンジニアと企業をつなげるグローバル事業 活躍するエンジニアを支えるTeam+事業 技術選定や意思決定をサポートするTools事業 などなどがあります。 これらの事業がすべて、挑戦するエンジニアのプラットフォームを作り上げ、エンジニアの可能性を広げることにつながると考えております。 そして、CEOの山田さんやCTOの佐藤さんが、この領域で挑戦しようとしていることに感銘を受けております。 ここで、素晴らしいメンバーと共に、挑戦するエンジニアが必要とするサービスを積極的に提供していきたいと思っております。 しかし、まだまだファインディ自体が、挑戦するエンジニア、挑戦したいエンジニアに積極的に選ばれる状況にあるとは言えないのではないかと考えております。 これは、ファインディにとっても僕にとってもさらなる 伸び代 だと思っております。 「ファインディに相談すればなんとかなる」という状況を作り上げることができればと考えております。 最後に ファインディの対外活動にも積極的に参加していく予定でございます。 その際は、皆様、よろしくお願いいたします! 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
Findyでエンジニアをしている松村( @shakemurasan )です。 以前、弊社の栁沢が「RailsのCIのテスト実行時間を10分から5分に高速化した話」という記事を投稿しました。 tech.findy.co.jp 本記事ではその少し前のお話、そもそもRSpecの実行時間自体にまだまだあった伸びしろ、特にFactory周りの問題をTestProfというgemを活用して解消していった話となります。 当時のRSpecの実行時間状況 TestProfとは TestProfでの分析結果 改善1. 関連したレコードを複数件作成しているFactoryを1レコードに減らす 改善2. テストで必要最小限なレコードのみを作成する RSpecの実行時間の改善結果 考察 当時のRSpecの実行時間状況 これまでにもテスト実行時間の短縮のための取り組み(CI側でのマシンの並列起動および並列実行)はしており、そこまでストレスのある環境ではありませんでした。異様に実行時間を要しているテストが存在することはメンバー間で認識しており、何となく原因のアタリはついていたものの、並列実行導入で快適な環境が得られていたので、テスト軽量化の優先度が落ちている状況でした。 しかし、じわじわとテスト実行時間は伸びていき、並列実行の導入当初は10分程度だったものが、15分を超えるケースが目立つようになってきました。いよいよテスト軽量化に着手しようかという話になり、実際に対応していくことになったのでした。 更に並列化を推進しようという案もその時点であったのですが、そもそも前述の「異様に実行時間を要しているテスト」が原因でマシンごとのテスト実行完了時間に度し難いバラツキが生じており、まずはここを解決しなければならないよねとなりました。 TestProfとは テスト軽量化にあたって、まずは何となくアタリをつけていたところに本当に原因があるのか測定することにしました。解析/測定に際し、TestProfが使えそうということは様々な記事、他企業様のテックブログ事例から知っていたので、これを採用することにしました。 github.com TestProfには様々な機能があるのですが、アタリをつけていたのはテストデータ生成周りだったため、FactoryProfというFactory周りの解析機能を使っています。FactoryProfを用いることで、Factory別の 総呼び出し回数 / 総実行時間 / 1回あたりの実行時間 あたりをレポーティングでき、この機能を用いて改善に着手しました。 ※ この後の章で登場するファイル名、Model、Factoryは仮名に置き換えていますが、数値は実際のものを掲載しています TestProfでの分析結果 まず大前提、時間がかかっているテストケースの状況を見てみます。 spec Before After Reduce Worst1 78.37s ??? ??? Worst2 75.71s ??? ??? Worst3 48.76s ??? ??? Worst4 48.16s ??? ??? Worst5 88.01s ??? ??? Total of worst 339.01s ??? ??? 実にテストケース5本で5分39秒を越えている状況で、これはちょっと辛いな.... というのがわかります。ここから、一番重たいテストケースのみを指定してFactoryProfの解析をかけてみます。 FPROF=1 bin/rspec spec/models/worst_spec.rb:2244 すると次のようなレポートが得られます。 [TEST PROF INFO] Factories usage Total: 1586 Total top-level: 8 Total time: 01:32.432 (out of 01:59.446) Total uniq factories: 12 total top-level total time time per call top-level time name 309 0 80.7570s 0.2613s 0.0000s full_item 103 0 0.8972s 0.0087s 0.0000s shop_user 103 0 90.3245s 0.8769s 0.0000s item_like 103 0 88.4511s 0.8587s 0.0000s shop ... ... ... この結果を見ると、 item_like shop が呼び出し回数と1回あたりの実行時間あたりが1s弱と長いことがわかります。ここから段階的に改善の手を打っていきます。 改善1. 関連したレコードを複数件作成しているFactoryを1レコードに減らす そこで実際のコードを読んでFactory間の依存関係を見てみると次のような状況でした。 item_like -- create --> shop ┬-- create --> full_item ├-- create --> full_item └-- create --> full_item テストの要件は割愛させていただくのですが、テストを実行するうえで full_item は3件も必要なく、最低1件はshopにぶら下がっていれば十分という状況でした。 item_like というのはここ以外でも結構色々なところで使われているFactoryで、ほとんどのテストは最低1件のshopがぶら下がっていれば問題ない状況でした。 そこで、 item_like のFactoryを修正して full_item を1件だけに限定して再解析したところ、次のような結果が得られました。 [TEST PROF INFO] Factories usage Total: 659 Total top-level: 8 Total time: 00:36.813 (out of 01:14.075) Total uniq factories: 11 total top-level total time time per call top-level time name 103 0 32.7865s 0.3183s 0.0000s shop 103 0 27.1212s 0.2633s 0.0000s full_item 103 0 0.8971s 0.0087s 0.0000s shop_user 103 0 34.5748s 0.3357s 0.0000s item_like ... ... ... 単純に full_item の生成回数が1/3に減り、それに比例して実行時間も約1/3に圧縮できました。 改善2. テストで必要最小限なレコードのみを作成する 別の重たいテストも解析していくと、共通して同じ問題が見えてきます。 [TEST PROF INFO] Factories usage Total: 3157 Total top-level: 582 Total time: 02:05.161 (out of 04:30.214) Total uniq factories: 48 total top-level total time time per call top-level time name 322 6 6.2221s 0.0193s 0.1149s user_profile 276 70 74.0465s 0.2683s 5.2776s shop 230 0 63.5779s 0.2764s 0.0000s full_item ... ... ... 次に着目したのは、 full_item の実行時間の長さです。1回あたり 0.2764s ということでそんなに長く感じないかもしれませんが、呼び出し回数が非常に多く、塵も積もればこのテストでは230回呼ばれて 63.5779s は無視できない実行時間になっています。 またまたコードを読んでみると、 full_item は関連するデータを全部盛りで作る.... というFactoryになっていました。 item というモデル自体、システムの中でコアなドメインのモデルとなっており、非常に多くの関連を持ちうるものなのでした。しかし、大部分のテストにおいてはそこまで完全な状態の item は要らないという状況でした。 そこで、 shop 生成時のデフォルトは full_item ではなく item というスリムなFactoryを使うように切り替え、再度解析をしてみることにしました。 [TEST PROF INFO] Factories usage Total: 2697 Total top-level: 582 Total time: 01:06.857 (out of 02:53.458) Total uniq factories: 47 total top-level total time time per call top-level time name 322 6 4.7178s 0.0147s 0.0844s user_profile 276 70 29.5793s 0.1072s 4.0465s shop 230 0 10.8307s 0.0471s 0.0000s item 結果、1回あたり 0.2764s かかっていた full_item から、 0.0471s で済む item に取り変わったことで、このテストの実行時間は半分にまで圧縮できるようになりました。 RSpecの実行時間の改善結果 他にもTestProfを用いて細かな改善は入れていき、最終的にワースト5のテスト実行時間は次のように変化しました。 spec Before After Reduce Reduce Rate Worst1 78.37s 17.55s -60.82s -77.6% Worst2 75.71s 15.73s -59.97s -79.22% Worst3 48.76s 8.28s -40.48s -83.01% Worst4 48.16s 7.84s -40.31s -83.72% Worst5 88.01s 18.1s -69.9s -79.43% Total of worst 339.01s 67.5s -271.51s -80.08% 339.01s から 67.5s に圧縮され、この最悪値だったテスト5本に限定して言うと、実行時間を8割以上削減できました。さらに上記のテスト以外でも今回改善したFactoryは使われているわけですから、テストの総実行時間にも効いてくるわけです。 考察 今回、TestProfを用いて、何となく重たいんだろうと思っていたところが想定以上に無駄なことをしていたことがわかりました。 「とりあえずデフォルトで全部入りのテストデータ作っておくか!」というのは、ともすればやりがちですし、歴史の長いコードベースだと気づきにくいように感じました。 「推測するな、計測せよ」という言葉がありますが、実際に計測して打ち手をピンポイントに打っていくことで、着実に改善している感覚を得られて周囲にも説明しやすかったのも学びです。 また、今回Factoryの挙動を大胆に変更することで実行時間削減を実現していますが、これは常日頃みんながテストコードをかなり手厚く書いていてくれたおかげというのが大きいです。システムが壊れないことを担保するため、不安と戦うために我々はテストを書くわけですが、テスト自体のリファクタリングにもテストコードが有効であるということは学びになりました。 ファインディは積極的にエンジニアを採用しています。CI/CDを始め、Four Keys、開発生産性、技術トレンド、転職市場など興味のある方は、お気軽にカジュアル面談を受けてみてください :) ファインディの採用情報はこちら↓ herp.careers
アバター
こんにちは。 Findy で Tech Lead をやらせてもらってる戸田です。 Findy 社の Tech Blog を開設して 1 ヶ月程度が経とうとしています。 このブログを通して弊社の技術情報だけでなく社内のエンジニアのことをもっと知ってほしいと考えた結果、今回から 自慢の作業環境を大公開 と題して、社内のエンジニアの作業環境と、そこに対する考え方、思いなどを発信していく事になりました。 弊社のオフィスは東京にありますが、北は北海道、南は福岡まで遠隔地からのフルリモートで JOIN しているエンジニアが多数在籍しております。 エンジニアの多くが各自の自宅から作業しており、きっと自分にとっての最高の環境を整えているはずです! それでは、弊社エンジニア達の自慢の作業環境を見ていきましょう! 作業環境を大公開 戸田 というわけで初回は福岡から JOIN している Tech Lead の戸田の作業環境から紹介させてもらいます。 作業スペースの全体像はこんな感じです 基本的には DELLの32 インチの 4K モニタ を使い、クラムシェルモードで作業をしています。 スピーカー はモニタのアーム部分に接続しており、イヤホンを繋げてボタン一個で出力切り替えが可能なので、普段はスピーカーを使い静かにしたい時はイヤホンを使う。ということが簡単に切替可能です。 Web カメラ と マイク は別途購入して外付けで利用しています。リモートでの作業がメインとなるため、映像、音声環境は最低限のマナーと思い投資しました。 デスクは bauhutte というメーカーの 昇降デスク を使っています。 この昇降デスクは電動でボタン一つで高さを自由に変更でき、横幅も奥行きも大きいので色々な作業シーンに合わせて利用することが出来るので非常におすすめです。 そして安心と信頼のバグ退散お守り。CI を通すときや重要なリリースの前に祈りを捧げます。 手元周りはこんな感じ キーボードは HHKB Professional HYBRID Type-S を使っています。 今まで色々なキーボードを試しました。その結果、個人的に作業終了後の手元の疲れを一番小さく感じたのはこのキーボードでした。 特殊なキー配列ですが、慣れたらコードを書いたりコマンドラインを叩いたりしやすいキーボードだと思います。 マウスは Apple の Magic Trackpad を使っています。マウスに対する拘りはなくて、正直 Mac のショートカットアクションが使えれば何でもいいです。早くマルチペアリングに対応して欲しいです。 また、変わり種としては指の筋トレ用具を手元に置いてます。 事の発端は格ゲーをしてた時に思っていたより指が動かなくて、「人類は薬指と小指を思ってるより使ってないな?」と感じ、この二本の指を鍛えるために買いました。 リモート会議中とか、コードを考えてる時とかずっとこれで指を鍛えてます。これで鍛えだしてから薬指と小指がスムーズに動くようになり、キーボードのタイピングが今までよりもスムーズになった気がします。 作業用 BGM は HomePod mini と Apple Music の合わせ技を使っています。 iPad を HomePod mini とペアリングさせて、サブスクには新曲やサントラがどんどん追加されます。 作業スペースの横には趣味のプラモ作成スペースを、後ろには本棚とプラモケースと積みプラ。 自分の好きなものに囲まれているというのは、毎日1人で黙々と作業する環境としては想像以上に重要だと思います。 毎日部屋に籠もって1人で作業しているので、五感に入るもので飽きが来ない環境を作ることが大事だと思います。 熊野 青森からフルリモートの熊野( @shoota )です。 フルリモートは 2017 年夏からで 7 年目になります。 まずはメインのデスクから。 自宅に備え付けのデスク MacBook Pro 16” をノート用スタンドに載せ、デュアルディスプレイで作業をしています。 MacBook ディスプレイは基本的には Slack 専用で、ほぼすべての作業は 外部ディスプレイ + 仮想デスクトップでこなしています。 外部ディスプレイは DELL の 4K ハブモニター に仕事用の MBP と個人用の Mac mini を繋いでいて、画面を切り替えると接続しているキーボードも自動で切り替わるのが気に入っています。 デスクライトを Amazon Alexa に接続し、デスク左側の Amazon Echo から音声操作できるようにしています。デスクから離れたあとにライトの消し忘れに気づくので、遠くからでも声でライトのオンオフができるのが便利です。 Echo は Spotify などの音楽再生をしたり、他の家電の操作にも使っています。 デスク右側は Anker の無接地タイプの充電器 で iPhone のスタンバイ表示をしています。 その隣には iPad mini をおいていて、ちょっとしたメモをしたり、作業をしながらオンラインイベントを見たりするときに使っています。 全体的に黒で統一するのが好きで、トラックパッド だけが白なのでそろそろ黒の方に買い替えちゃおうかなと思っています。 キーボードは Keychron の分離式キーボード Keychron Q11 QMK Custom Mechanical Keyboard 、ポイントデバイスは Magic Trackpad2 です。 分離式キーボード 肩こりがひどくなりやすいので分離式キーボードをずっと使っていますが、Q11 はキースイッチごと交換したりキーマップのカスタマイズが簡単で、多機能なのでかなり気に入っているキーボードです。 いまは赤軸スイッチと標準仕様のキーキャップなので、そろそろキーキャップを変えたいな〜と思っているところです。 またフルリモートとしてはビデオ通話の品質を大事にしたい&自宅では子どもたちの声などの環境音が混ざりやすいので、自分も音響はしっかりと用意しています。 マイクは指向性の Yeti Nano で自分の声だけが入るようにスタンド設置しています。 Yeti 以前はヘッドセットを使っていましたが、いずれも「物理スイッチでミュートできる」ことがこだわりです。 急にくしゃみしたいときにソフトウェアミュートは間に合わないので 逆に出力側は周りの音が聞こえないと不便なので、オープンイヤータイプを愛用していて、いまは shokz の OPENFIT を使っています。 指向性マイク 最後にデスクの後ろに DIY で作ったコミック棚があるので、自慢がてらに紹介したいと思います。 自分が好きなものや集めたものに囲まれて仕事をするのが好きなので、コミック(たぶん 800 冊くらいあります。もう数え切れません。)やゲームグッズ、お気に入りのウイスキーをおいています。 安全率を含めた荷重計算をして設計図をつくり、部材の切り出しから組立て、取り付けまでひとりでやりました。 だいたい 2 万円強くらいですべて揃い、先日の震度 5 以上の地震でも コミックは 1 冊も落ちませんでした。 2023 年下半期の総会、 Fine-day で MVP を受賞 したときのトロフィーも飾っています。 DIY本棚とMVPトロフィー 仕事場でありながら自分らしさを出しつつ、仕事における自分なりの「快適性 / 合理性」を求めたデスクに仕上がっていると思っています。 主計(かずえ) 福岡からリモートでフロントエンドの開発をしている主計( @nesskazu )です。 デスクの全体像はこのような感じです。 デスク全体 4k ディスプレイを 2 枚配置しコーディングしながらデバッグできるようにしています。 ミーティング時にドキュメント等を参照できるのも便利です。 サブモニターはデバッグしやすく、web等の閲覧性が良いため縦配置にしています。 コーディングとデバッグの画面配置 Thunderbolt ケーブルで会社支給 PC と私用 PC で全ての機器の接続が切り替わるようにしています。 ドッキングステーションには CalDigit TS3 Plus Dock を使用しています。 Thunderboltケーブル1本でPC切替 ドッキングステーション キーボードは静電容量無接点方式やメカニカルなどいろいろ試してきましたが、 自分はキーストロークの深いキーボードは苦手(疲れる&タイプミス多い)なことがわかったのでキーストロークが浅いキーボードを使っています。 MX Keys mini がお気に入りでしたが、途中からチャタリングが発生したり Touch ID が欲しかったので現在は Apple Magic Keyboard を愛用しています。 また、切り替えや充電の手間をなくすため有線で接続しています。 Apple Magic Keyboard リモートワークということもありミーティングが相手と自分双方にとって快適なものになるように構築しています。 マイクは周囲の音を拾いにくいダイナミックマイク(SHURE MV7)を採用しています。 カメラはなるべく目線の高さに合うようにディスプレイ間のスピーカーの上に設置しています。 マイクとカメラ ミーティングや BGM の音は OWNDAYS の HUAWEI Eyewear で聞いています。 普段メガネ付けている人であればメガネが変えるだけでスピーカーが着いてくるので便利です。 ミーティングのたびにイヤホンを装着したりする必要がなくなります。最近電池持ちが悪く昼休みに充電する必要があるため Eyewear2 の購入検討中です。 Eyewear を使い初めてから休みの日にしかコンタクトを使わなくなりました。 Eyewear 我が家でもバグ退散のお守りが後ろから見守ってくれています! バグ退散お守り このように仕事をするうえで不便な点を排除し、快適に作業等ができるように意識してデスクを構築しています。 まとめ いかがでしたでしょうか? 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
こんにちは。こんばんは。 開発生産性の可視化・分析をサポートする Findy Team+ のフロントエンド リードをしている @shoota です。 Findy Team+はエンジニア組織の開発生産性を可視化し、開発チームやエンジニアリングメンバーのパフォーマンスを最大化するための支援をしています。 そして(当然のことながら)Findy Team+ を作っている自分たちも、チームや個人でドッグフーディングをして、チームや自分自身の働き方やエンジニアリング組織の健康チェックをしています。 今回はそんな Findy Team+の開発チームのうち、フロントエンドチームがどのような開発環境・開発インフラで働いているかの概要をご紹介したいと思います。 フロントエンド技術スタックとCI高速化 技術スタック まずはじめにフロントエンドの技術スタックを簡単に紹介します。一般的なSPA構築の技術スタックを採用しており、それほど特別なものではないことをご理解いただけると思います。 React React Router Redux TypeScript GraphQL Apollo Client GraphQL Codegen Emotion Storybook Testing Jest @testing-library/{react,jest-dom,user-event} reg-suit Linter / Formatter ESLint Stylelint Prettier ここまではいわゆる一般的なSPAの構成ですが、Findy のフロントエンドはモノレポ管理ツールである Nx を利用することで高速化を図っています。 Nxの機能詳細についての説明はここでは省略しますが、トップページに「Smart Monorepos・Fast CI」とある通り、CIの高速化が簡単に管理できるところが最大の特徴と恩恵だと思っています。 NxでCIを高速化する Nxは内部に複数のプロジェクトを持つことができ、且つ、それらの依存関係を自動で解析します。 この「プロジェクトの依存関係」をもとに、プルリクエストでの「変更されたプロジェクト」と、その「変更部分が依存しているプロジェクト」のみのテストを実行する手段を提供しています。 これによって開発者が開発しているものに関係のない部分は省略されるためCIが高速化します。 それ以外の変更についてもNx Cloudにキャッシュが残っていればその結果を採用してテスト等をパスさせ、CIが高速に完了します。 CIの実行結果を載せてみました。 GitHub Actionsの様子 共通のhooksを修正してる3段目のプルリクエストではほぼ全てのテストを実行(そして新たにキャッシュ)しているため、20分以上のテスト時間がかかっています。 一方でその他をみてみると、新しい画面などの通常開発の場合は変更内容がプロジェクト内で完結しているので、CIの実行時間は数分で終わっています。 また単にCIが高速化される以外にも、Nxは開発生産性の向上に寄与してくれます。 それは 「プルリクエストはできるだけ小さく作ろう」という開発文化を「CI時間」という数値で可視化 してくれることです。 前述の通りプルリクエストの変更の依存関係が小さいほどCIは早く終わりますので、逆にプルリクエストが広範な範囲で大きくなってしまうほどCI時間が長くなります。 プルリクエストの粒度が大きいと、1) CIが遅くなり、2) レビュー依頼するまでの時間が空いて忘れがちになり、3) レビュー量も多くて時間がかかるという悪循環に陥ります。 そこでプルリクエストを作る側はどうにかこれを避けようとする力学が働き、CIが高速であれば作業内容に集中し、レビュー指摘の反応や修正も楽になっていくのです。 プルリクエスト / レビューに反応する 前述の通りNxの依存管理を基盤としてCIを高速化していますが、Findy ではレビュー依頼やレビューコメントなどGitHubでのやり取りをSlackに移植してコミュニケーションのトリガーを通知しています。 実際のSlackの画面はお見せできませんが、以下のようなタイミングでSlackに状況が実況され、レビュー依頼が来た場合は通知もされます。 プルリクエストを作った時 プルリクエストにコメントされた時 プルリクエストがApproveされた時 プルリクエストをマージ(クローズ)した時 またGitHub標準のSlackアラート機能も併用していて、レビュワーにアサインされたままのプルリクエストがあれば1時間ごとに通知されます。 これによって「レビュー依頼に気づかなかった」状況を減らし、レビューが放置ぎみのときには「なにか事情があるのかな?作業に入れない状況かな?」と察したり、レビュワーを変更するなどの対処もできます。 このSlack Appは弊社テックリードの github-to-slack-notification をベースに構築されています。 Findy謹製のSlack通知App プルリクエストを分類する Findy Team+ は、開発メンバー、開発チーム、リポジトリ、プルリクエストのラベル、日時やカレンダー情報などの複数の条件をディメンションとして、個人やチームのリードタイムやアクション数などのパフォーマンスを計測できます。 プルリクエストは機能開発やリファクタリング、バグ修正、ドキュメンテーションなど内容が様々になってしまうため、パフォーマンスがどのような部分で発揮されているかを分類するのは非常に困難ですが、Team+ではプルリクエストのラベルを利用して一定の分類ができるようにしています。 しかし一方で、「プルリクエストにラベルをつけておく」という作業を定常的に、すべてのエンジニアが、漏れなく、間違いなく、実施していくのはプルリクエストの分類以上に困難なことでしょう。 Slackを見返すと入社3日目の自分がいきなり本質をついています。 ラベル運用について気づくshoota そう思っていたときが私にもありました。 Findy Team+のフロントエンドのプルリクエストは自動でラベルをつけています。 そもそもgit上の全てのコミットは Conventional Commits に則るよう、 commit lint で制約を設けています。 そしてプルリクエストのタイトルもConventional Commitsの形式にするようにルール化しているので、プルリクエストのタイトルにはこれらの接頭句が必ずあり、これを検知してGitHub Actionsで分類、対応するものにラベルをつけるようにしてます。 プルリクエストの種類は、タイトルから自動で分類させておく デプロイする 小さなプルリクエストが無事にマージされたら、デプロイしていきます。Team+ フロントエンドは、GitHub Actionsの schedule cronを利用して一日に2回定期デプロイをしています。 gitはgit-flowをベースにした運用をしつつ、プルリクエストテンプレートにリリース時に必要な確認項目を入力する欄を設け、リリース時にはこの確認項目を自動生成するようになっています。 まずはgitの運用フローを簡単に図式化してみました。 ブランチ運用 ※ の部分はリリース用プルリクエストのcommit hashをmasterからdevelopへ移動するための操作で、基本的には差分は発生しないものです developブランチを定時間でリリース用ブランチに派生して、ステージングにデプロイします。 このときの主なチェック項目として、 バックエンドで先行するプルリクエストがないか(ある場合はそのプルリクエストのリンク) APIレスポンスの変化やエラーハンドリング、後方互換性など、開発者のみが確認できる確認項目がないか 実装機能と想定仕様の合致など、機能リリースのためのQAが必要でないか をプルリクエストテンプレートで記載するようにしており、リリースプルリクエストにはこれらすべてを列挙して簡易的なリリース判定を行っています。 まとめ 今回は Findy Team+ の開発フローに焦点をあてていくつかの要素をご紹介しました。 これ以外にもさまざまなツールやサービスを利用して品質とスピードを両立させるための開発基盤を持っていますが、ひとことではお伝えしにくいものもあるので、それぞれより詳細な記事でご紹介して行ければと思います。 スピードを意識したフロントエンド領域の開発環境として高いレベルで整備し、可視化して改善するように心がけています。 自分の開発能力、スピードをフルに発揮したいという方は、ぜひ下の採用情報もごらんください。 herp.careers
アバター
こんにちは、ファインディ株式会社で機械学習エンジニアをしていますsasanoshouta( @Edyyyyon )です。この記事は、言語処理学会第30回年次大会(NLP2024)に社で初めてプラチナスポンサーとして参加してきましたので、その参加報告になります。 NLP2024とは? 言語処理学会第30回年次大会(NLP2024)は,2024年3月11~15日の期間,5日間の日程で開催いたします.チュートリアルは3月11日午後1時に開始,本会議は3月11日午後4時半から14日午後7時までの4日間です.現在,現地とオンラインのハイブリッド開催の形態で準備を進めています.現地とオンラインの両方から参加し,発表・聴講・議論をすることができます. *1 大会概要は引用文の通りで、今年は神戸ポートアイランドの神戸国際会議場で開催されました。 参加の背景 今回初参加となりますが、スポンサーするに至った背景は以下の通りです。 機械学習、データサイエンスを初めとするデータ系職種・界隈への認知拡大や打ち手が足りておらず、その足がかりとしてのスポンサード ファインディでもデータ系職種を絶賛募集しているが、そもそも機械学習やデータサイエンスにも取り組んでいる会社であるとの認知が広げきれていないので、「何を?なぜ?どのように取り組んでいるのか?」を知って頂きたい 「エンジニア開発を支援する企業」であることをアピールできている一方で、実は内部でデータ活用・機械学習を使っている事の認知を広げられていない背景があり、まずは知って頂きたいと言う事で、今回スポンサーとして参加してきました。 会場の雰囲気 前述の通り、弊社ではNLP2024を含む学会への参加経験が全くなかったので、過去のNLP202X年度の雰囲気や、他社さんの過去参加記事を参考に準備を進めていました。また、私自身や今回同行したメンバーの中にも学会参加経験者が数名いた事もあり、普段弊社が参加するカンファレンスイベントなどよりも少しかっちりとした準備を進めていました。いざ当日会場に着いてみると想像していたよりもラフに参加できそうな雰囲気でした。(筆者が最後に参加していた頃の学会がX年前で、真面目な雰囲気の印象があった為か、時が流れて参加しやすい雰囲気になっているのは驚きました。) ポスターセッションの雰囲気 弊社スポンサーブース こちらの画像の、掲示スペースが少しもの寂しげなブースがファインディのスポンサーブースになります。ポスターの用意が間に合わなかった為、苦肉の策として弊社のデータ系職種採用資料を印刷して掲示していました。 弊社スポンサーブースの雰囲気 どうしてこんなに寂しくなってしまったのかと言うと、もともと筆者がポスター準備担当だったんですが、直前の体調不良により作業を進める事ができず、敢えなく準備期間が過ぎ去ってしまった為でした。しかたない側面はありますが、沢山の方とお話できたので伸びしろと捉えて次回参加時こそは掲示物を作って参加したいと思います。 スポンサーブースでお話したこと 今回は、弊社データソリューションチームの採用資料から、機械学習による取り組み事例をいくつか持参し、モニターに投影しながらこられた方に弊社のデータ活用先とその実現方法について紹介をさせていただきました。また、言語処理学会と言う事もあり、実際に社内でも検討しているNLPも取り入れた今後の方向性も反映したものになっています。採用資料から2つ抜粋して簡単に紹介します。 1つ目は「LLMを用いたキャリアサマリの作成」です。👇 LLMを用いたキャリアサマリの作成 1年ほど前の取り組みになりますが、OpenAI API公開後に活用施策第1弾と言う事で、1週間でのリリースを目標に作成した機能になります。 職務経歴書などの情報を入力すると、それを要約して二つ名をつけてくれると言った機能でした。 私の場合は、「AIの魔術師」「AIのキラーコンサルタント」と言う名前をつけてくれました。 2つ目は「行動ログを活用した転職意欲や志向の検知」です。👇 行動ログを活用した転職意欲や志向の検知 こちらの機械学習モデルは、転職意欲が高いにも関わらず、プロフィール上転職意欲が低いままになってしまっている方向けの機能としても使うことができますが、どちらかといえば 転職活動の意欲がないのにスカウトが送られてくる 事での煩わしさを軽減する防御の役割を持った機能として運用しているものになります。 プロフィールのステータスは転職意欲が高いままになっているが、実際には 転職活動が完了したままステータスを変更するのを忘れてしまっている というケースがあったりします。 そうした方に、スカウトを追撃してしまうと転職サービスとしての不信感に繋がりかねないので、事前に予防できるものは出来る限り予防するようにしています。 ブースに立ってみて 当初想定していたよりも多くの方がブースに来てくださり、筆者もブースに立って意見交換をさせて頂きました。ブースに聞きに来て頂いた皆様ありがとうございました。 ファインディ自体が「 エンジニアに特化した中途転職サービスを中心に事業を展開している 」事と、学会に参加されている方々の属性が、 機械学習・データサイエンスを生業とされている社会人参加の方 大学・大学院大学から学生参加の方 の2属性の方々がメインであった事と相まって、「 ファインディ株式会社 」と「 何をしている会社か? 」の知名度にはかなりの伸び代を感じました。一方で、完全アウェーだった訳でもなく、「 スキル偏差値の会社 」として認知いただいている方が多かったです。データ界隈での機能とサービス・会社名の繋ぎ込みにまだまだ課題を残しつつも、機械学習・データサイエンスの界隈にも一定取り組みが届けられている事を感じられました。 個人的な聴講セッションの感想 スポンサーとしてブースに立つ傍ら、いち聴講者としてセッションをいくつか聞いたり他社スポンサーさんの企業ブースにお邪魔してお話を伺わせていただいたりしたので、メモベースにはなりますが個人的な感想を書き連ねておきます👇 オーラル・ポスターセッションそれぞれに共通して言えたのは、大前提 LLMに関する研究発表である 事がほとんどでした。 だからと言って、 OpenAI API をそのまま使って研究をしていたり、サービス実装してコア機能としている企業さんや研究もほとんどないような印象でした。 リクルートさん、サイバーエージェントさんなどのテキストデータを潤沢に持ち合わせており計算資源も豊富な企業でも、一部プロセスに取り入れられているとお聞きしました。 社内向けに、全社の業務効率化を目的としてRAGや文章埋め込み(embedding)を使い、LLMの弱点であるhallucinationを低減した仕組みを取り入れたお問い合わせbotを実装・運用している。 様々お話を伺った所、現在のLLM界隈のトレンドでは、お問い合わせbotのような活用がトレンドとなっていそうで(個人の見解)、このような流れはしばらく続きそうな雰囲気を感じた。 一方で、勢いが若干失速した感はありましたが、 自社専用のLLMをスクラッチで開発する という企業さんのお話もいくつか伺えました。 「ChatGPTとかClaude3とか低価格で高性能なAIが出てるし、今後もその流れは変わらないはずなのになぜ?」 と思い、お話を聞きいてみました。 自社でLLMを作っておく事で、GAFAMOが提供するLLMの規約が変わった時のリスクヘッジになる との方針で、研究をやめていないとの事。 どの企業さんにも共通していたのは、1つ自社のLLMを作れておくと用途に合わせて柔軟にカスタマイズができる点も、内製化のメリットの1つとの事でした。 為になったセッション 全てのセッションが興味深く考えさせられたり、明日使えるものたちが多数でしたが、個人的に1番勉強になったセッションを共有します。全体の発表スケジュールは こちら から。 文章のチャンクに基づく知識グラフを活用したRAG(産総研さん) 元論文は こちら から RAGを使ってLLMの回答精度を高める為の取り組みはいくつもありますが、LLMに与えられるクエリにcontextを付与する為に知識グラフをDBとして構築しておき、知識グラフからの情報検索プロセスをRAGに組み込む、と言うものでした。弊社でもRAGを用いた質問回答botを作って運用したりしており、オーソドックスに類似度計算した上位N件から回答を作成すると言うシンプルな構成を取ったりしています。こちらの研究にある通り「クエリの文脈はすべて類似度で測れるとは限らない」と言う事を痛感しているので、こちらの研究を参考にしてみたいと思いました。 参加してみての感想 初めて言語処理学会にスポンサーとして参加してみて、やはりデータ系職種・界隈での認知拡大の伸び代がある事を強く実感しました。 その理由はいくつかあると思いますが、これまで今回のようにデータ系職種の方が集まる学会やカンファレンスに積極的に参加していけてなかった所が大きいと感じました。 もちろんこれだけではなく、社内でもデータを活用した取り組みは沢山動いたりこれから始まったりしている最中なので、そうした取り組みをもっと積極的に発信する事で、より多くの方にファインディを知って頂きたいなと思います。 いち参加者としても、短い期間でまとまった数のNLPに関する研究発表を自分の五感で感じる機会は中々なかったですし、多種多様な取り組みや企業さんとお話できてとても良い刺激になりました。すぐに試せそうなノウハウを沢山得ることもできましたし、忘れないうちに現在の取り組みの参考にさせていただこうと思いました。 今後は、言語処理学会をはじめ学会へのスポンサー、ポスター発表などにも力を入れていきますので、どこかで見かけた際は何卒よろしくお願いいたします。 さいごに ファインディでは、機械学習エンジニア・データサイエンティスト・データアナリストを絶賛採用中です。以下のページから応募頂けますので、興味のある方は是非カジュアル面談などでお話しましょう。 findy-code.io findy-code.io *1 : ※ NLP2024公式ページ より抜粋
アバター