TECH PLAY

Findy/ファインディ

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

181

こんにちは!ファインディでFindy Team+開発チームのEMをしている 浜田 です。 この記事はFindy Advent Calendar 2024 6日目の記事です。 adventar.org 今年の上旬、フロントエンジニア向けにバックエンド勉強会を開催しました。この記事ではバックエンド勉強会を開催した目的や内容、効果について紹介します。 バックエンド勉強会を開催した背景 バックエンド勉強会の概要 バックエンド勉強会の内容 RubyやRailsの学習 VS Codeのプラグイン設定 Rails console / dbconsoleを使ってみる ruby-lang.orgを読む Railsガイドの紹介 Railsの構成を説明 バックエンドのライブコーディング DBの基礎 SQL実習 SLQ実習で書いたSQLをActiveRecordで書く N+1問題 正規形 インデックス、実行計画 トランザクションとロック 外部キー まとめ バックエンド勉強会を開催した背景 私のチームが開発しているFindy Team+はWebアプリケーションとして提供しており、フロントエンドはReact/TypeScript、バックエンドはRuby on Railsを使用しています。 ファインディでは、エンジニア個人の志向に合わせて、特定の技術領域を深く理解することでバリューを発揮している方や複数の技術領域を幅広く扱ってバリューを発揮している方など様々な方がいます。 そのため、自分の得意領域以外への挑戦を推奨しています。 ただ、現在では1チームに6〜8名ほどのエンジニアで構成され、各自が得意領域を担当することで効率よく開発が進む状況です。 このような状況では、得意領域以外にチャレンジしたい気持ちがあったとしても、最初の一歩が踏み出しづらいものです。 そこで、今回はフロントエンドを主軸としているエンジニアがバックエンドに挑戦する一歩を後押しするためにバックエンド勉強会を実施しました。 バックエンド勉強会の概要 期間: 2024年2月〜8月 頻度: 毎週30分 回数: 23回 講師: 私 RubyもReact/TypeScriptも業務で使った経験あり 参加者: 3名 バックエンドほぼ未経験、または数年触っていない。React/TypeScriptはバリバリ書いている カリキュラム RubyやRails、DBの基礎 業務で使うコードを使ったライブコーディング バックエンド勉強会の内容 バックエンド勉強会では、前半はRubyやRails、後半はDBについて学習をしました。 業務で使っているコードを使ってAPI開発の基礎知識を学ぶことで、簡単なAPIの作成や修正ができることを目指しました。 RubyやRailsの学習 VS Codeのプラグイン設定 まずはエディタを整備しました。 参加者は全員VS Codeを使っていたので次のプラグインをインストールしました。 Ruby Ruby LSP Rails console / dbconsoleを使ってみる 開発環境には全員バックエンド環境も構築しているので環境構築は不要です。 RubyやSQLをサクッと試すことができるように、Rails console / dbconsoleの使い方を紹介しました。 railsguides.jp ruby-lang.orgを読む ruby-lang.org の一部を読み合わせしました。 読み合わせした箇所をいくつか紹介します。 他言語からのRuby入門 普遍の真理 Rubyでは、nilとfalseを除くすべてのものは真と評価されます。 CやPythonを始めとする多くの言語では、0あるいはその他の値、空のリストなどは 偽と評価されます JavaScriptを書いている人にとって 0 はFalsyですが、Rubyだと 0 はTruthyでありハマりやすいので強調しておきました。 不思議なメソッド名 Rubyでは、メソッド名の最後に疑問符(?)や感嘆符(!)が使われることがあります。 珍しいルールなのでこちらも詳しく説明しました。 ?はbooleanを返すメソッドにつけることが多く理解しやすいです。 # 偶数かどうかを判定するメソッド def odd? (n) n % 2 == 1 end !は破壊的メソッドに付けることが多く、破壊的メソッドと同じ役割だが破壊的ではないメソッドがある場合には破壊的メソッド側に!が付けます。ただし、破壊的メソッドしかない場合は!をつけません。 # !あり・なしメソッドがあるパターン str = " string " str.upcase! str # => "STRING", 元の値がstrが上書きされている str = " string " upcased_str = str.upcase str # => "string", 元の値が上書きされていない upcased_str # => "STRING", upcaseの結果がupcased_strに代入されている # 同名の非破壊メソッドがないため、破壊的メソッドだけど!がつかない str = " string " str.replace " STRING " str # => "STRING", 元の値が上書きされている また、Railsだと例外を発生させるかどうかで!を付けたり付けなかったりすることがあります。 hoge.save # 保存に失敗した場合falseを返す hoge.save! # 保存に失敗した場合例外を発生させる 「存在しなかった」メソッド Rubyはメッセージに対応するメソッドを見つけられなかったとしても諦めません。 その場合は、見つけられなかったメソッド名と引数と共に、 method_missingメソッドを呼び出します。 method_missingメソッドはデフォルトではNameError例外を投げますが、 アプリケーションに合うように再定義することもできます。 Rubyではメソッドが見つからなかった場合の処理を簡単に上書きできます。 アプリケーションコードで多用することはないですが、ライブラリではよく使われている仕組みなので知っておくと良いです。 class Hoge ; end hoge = Hoge .new hoge.fuga # undefined method `fuga' for an instance of Hoge (NoMethodError) # method_missingを上書き def hoge. method_missing (name, *args) puts " メソッドが見つかりませんでした: #{ name }" end hoge.fuga # メソッドが見つかりませんでした: fuga 演算子は糖衣構文(シンタックスシュガー) Rubyにおけるほとんどの演算子は糖衣構文です。 いくつかの優先順位規則にもとづいて、メソッド呼び出しを単に書き換えているだけです。 たとえば、Integerクラスの+メソッドを次のようにオーバーライドすることもできます。 class Integer # できるけれど、しないほうがいいでしょう def + (other) self - other end end Rubyでは"+"もメソッドです。面白い仕組みなので紹介しました。 メソッドなので次のような呼び方ができます。 1 + 2 # => 3 # 1(Integerのインスタンス)の+メソッドの引数に2を渡している 1 .+( 2 ) # => 3 Ruby 3.2 リファレンスマニュアル > オブジェクト Ruby で扱える全ての値はオブジェクトです。 「全ての値はオブジェクトです」はRubyの特徴だと思うので説明しました。 クラスをインスタンス化したものがオブジェクトなのは理解しやすいですが、数値や文字列などのリテラルもオブジェクトであり、クラス自体もオブジェクトです。 全てオブジェクトなのでメソッド呼び出しや既存メソッドの挙動を上書きなどを行えます。 Ruby 3.2 リファレンスマニュアル > クラス/メソッドの定義 ここでは次の項目について説明しました。 クラス定義 モジュール定義 クラスとの違いがわかりづらいので説明 インスタンス化できず、共通の振る舞いを複数のクラスで共有したい場合などに使うことが多い メソッド定義 引数がなければ()を省略できる 特異クラス定義 / 特異メソッド定義 / クラスメソッドの定義 Railsのモデルで多用するので説明 Ruby 3.2 リファレンスマニュアル > 制御構造 if文など基本的な制御構造やunlessなどRubyの特徴的なところを紹介しました。 また、forは他の言語では多用するがRubyではあまり使わずeachを使うことが多いということも説明しました。 Ruby 3.2 リファレンスマニュアル > 変数と定数 よく使うローカル変数、インスタンス変数、定数を説明しました。 Ruby 3.2 リファレンスマニュアル > リテラル さらっと全体を眺めつつ「シンボル」「ハッシュ式」「ヒアドキュメント」「%記法」は初見だと戸惑うポイントなので詳しく説明しました。 ハッシュ式 キーとバリューのペアを保持するオブジェクト。 キーには数値・文字列・シンボルが使える。キーにはシンボルを使うことが一般的。 { 1 => 2 , 2 => 4 , 3 => 6 } { " a " => " A " , " b " => " B " , " c " => " C " } { :a => " A " , :b => " B " , :c => " C " } { a : " A " , b : " B " , c : " C " } # シンボルの場合、このような書き方ができる。この書き方が推奨。 ヒアドキュメント 複数行の文字列を表示するときに使う。 << <<-: 終端子にインデントをかける <<~: インデントをいい感じに削ってくれる Railsガイドの紹介 Railsガイドについてはこの勉強会では紹介だけ行いました。 Railsの機能が網羅されており、最新バージョンに追従しているとてもよいドキュメントなので、困ったらまずはRailsガイドを読むことをおすすめしました。 railsguides.jp Railsの構成を説明 実際に開発しているバックエンドのディレクトリ構成を説明しました。 Gemfile / Gemfile.lock package.jsonみたいにライブラリを管理しているもの Dockerfile / Dockerfile.dev / compose.yml .devは開発環境用 compose.ymlはdocker composeの設定ファイル config 各種設定ファイル db データベーススキーマやマイグレーションファイル log ログファイル dockerで実行している場合、ここに出力されないので docker compose logs -f web で確認 lib 本体とは切り離して使えるライブラリが格納されている taskにコマンド実行するタスク(ジョブ)が格納されている app admin 管理画面用のgem activeadmin で使うクラス controllers MVCのC enums localeやメトリクスのenum情報 graphql GraphQLのgem graphql-ruby のquery/mutation/typeなど interactors interactor gemを使ってCUDのビジネスロジックを実装 Controller -> Interactor -> Modelの依存関係 jobs 非同期処理 mailers メール送信処理 models MVCのM DBアクセスクラスが一般的だが、分析クラスや外部接続クラスなども入っている views MVCのV バックエンドのライブコーディング RubyやRailsの基礎的な部分は抑えたので、ここからは実際にバックエンドのコードを使ってライブコーディングを行いました。 既存のGraphQLのQueryにfieldを追加 フロントエンドを開発しているときにバックエンドを触りたくなる最も多いケースは、APIのインターフェースの変更だと思います。 そこで、既存のGraphQLのQueryにfieldを追加するというテーマでライブコーディングを行いました。 GraphQLの新規作成 次はよりがっつりバックエンドを触るケースとして、次の仕様を満たすGraphQLをライブコーディングしました。 このテーマでAPIを作成するときに必要となるDBのテーブル作成・Model作成・GraphQL作成の一連の流れを学びました。 userに紐づくメモを保存する機能 ユーザーごとにメモは複数作成可能 1つのメモは100文字まで 空のメモはNG メモは1ユーザー10件まで ユーザーIDに紐づくメモを取得できる テキスト検索できる メモIDに紐づくメモを取得できる メモを新規登録できる メモを更新できる 自分のメモしか更新できない DBの基礎 バックエンドの学習を進めるうち、データベースの知識を底上げする必要があると感じたため、ここからはデータベースについて学びました。 SQL実習 既存のテーブルを使って、お題を取得するSQLを書いてもらいました。 単一テーブルのSELECT JOINを使ったSELECT 複数のテーブルをJOINするSELECT INNER JOIN、LEFT JOINの使い分け GROUP BY、HAVINGを使った集計 SLQ実習で書いたSQLをActiveRecordで書く Railsで開発する場合、生のSQLを書かずActiveRecord経由でDBにアクセスするので、SQL実習で書いたSQLをActiveRecordで書いてもらいました。 N+1問題 N+1問題について説明し、RailsでN+1を回避するプラクティスを紹介しました。 preload / eager_load / includes 結合が不要な場合はpreload、joinするときはeager_load(left outer join) includesはpreload or eager_loadを自動判断する。 意図せずクエリーが変わってしまうことがあるので使わない方が無難 経験上、includesで発行するSQLが変わって困ったことはあってもincludes禁止で困ったことはない Rails始めた人が絶対辿り着く記事 ActiveRecordのjoinsとpreloadとincludesとeager_loadの違い GraphQLではpreloadやeager_loadを使った先読みが適さないこともあるので注意 graphql-rubyのN+1を遅延ロードで解決する 正規形 リレーショナルデータベースを使う場合、第3正規形までは必修だと思うので非正規形と第1〜3正規形を説明しました。 インデックス、実行計画 テーブル設計をするようになったらインデックスの検討も必須なので説明しました。 インデックスはとても良いスライドがあるのでこちらを共有しました。 speakerdeck.com また、発行したSQLがどのインデックスを使っているのか判断する手法として EXPLAIN を使う方法を紹介しました。 トランザクションとロック トランザクションは更新系の処理を実装する時に必須なのでRailsでトランザクションを使う方法を説明しました。 transaction do # この中の更新処理は1つでも失敗したら全部ロールバックされる # データの整合性を保つために更新系の実装では考慮が必要 end Railsを使ってWebアプリケーションを作っている場合、明示的にロックを意識することは少ないのですが、概念として楽観的ロックと悲観的ロックについて軽く説明しました。 外部キー 外部キーについてもMySQLのドキュメントをなぞりながら軽く触れました。 特にON DELETEは便利なので活用していきたい機能の1つです。 dev.mysql.com まとめ バックエンド勉強会で実施した内容をざっくりではありますが紹介しました。 勉強会に参加したメンバーから「勉強会後からバックエンドのコードがスムーズに読めるようになった」などの感想をいただくことができました。さらに次の機能開発でバックエンドに挑戦するメンバーもいたので開催して本当に良かったと感じています。 次の画像は参加したメンバーのバックエンドのプルリクを Findy Team+ で表示したものです。GraphQLのQueryやMutationの追加や、フィールドの追加・修正などを行なったことがわかります。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
アバター
ファインディ株式会社でフロントエンドのリードをしている新福( @puku0x )です。 この記事はFindy Advent Calendar 2024 4日目の記事です。 adventar.org Nxはモノレポ管理の便利なユーティリティとして @nx/devkit を提供しています。 今回は @nx/devkit を利用したStorybookの設定の自動化についてご紹介します。 Nxについては以前の記事で紹介しておりますので、気になる方は是非ご覧ください。 tech.findy.co.jp モノレポでStorybookをどのように管理するか? どうやってパス設定を自動化するか? 実装してみよう! まとめ モノレポでStorybookをどのように管理するか? 皆さんはモノレポでStorybookを運用されたことはありますでしょうか? 概ねどちらかの方法を採用することになるかと思います。 単一のStorybookで集中管理 Storybook Composition を使って管理 前者はStorybookのデプロイ設定をシンプルにできますが、全プロジェクトのStorybookへのパスを記述する必要があります。後者は設定がやや複雑であり、プロジェクト毎にStorybookのデプロイ設定も必要です。 Findyのフロントエンドはモノレポで管理されており、フィーチャ単位に細分化された多数のプロジェクトを持つという性質と運用の難易度を考慮し、前者の手法を選びました。 どうやってパス設定を自動化するか? 単一のStorybookで集中管理する際に課題となるのは↓の部分でしょう。 // .storybook/main.js const config = { addons : [ '@storybook/addon-essentials' ] , framework : { name : '@storybook/react-vite' , options : {} , } , stories : [ // 👈 ここ!👇 '../apps/app1/src' , '../libs/components/src' , '../libs/app1/feature-a/src' , '../libs/app1/feature-b/src' , '../libs/app1/feature-c/src' , // 以下、全プロジェクトのパスが続きます // : ] } ; export default config ; 数個程度であれば十分に運用できそうですが、モノレポ上となると話が違ってきます。 ちなみに、Findyのフロントエンドではプロジェクト数は 70 個でした。 ※他プロダクトでは100個近くになる場合もあります 「え?これ全部手動で管理するんですか!?」 さすがに手動での管理には限界がありますので、ここはNxの力を借りましょう。 使用するのは createProjectGraphAsync というユーティリティです。 createProjectGraphAsync | Nx Nxは各プロジェクトの依存関係を保持しており、そこからStorybookのパスを算出できます。 最終的に次のようなものを目指します。 // .storybook/main.js const config = { addons : [ ... ] , framework : { ... } , stories : getStories () , // https://storybook.js.org/docs/configure#with-a-custom-implementation // 👇 こんな感じの配列を返して欲しい // [ // { titlePrefix: 'app1', directory: '../apps/app1/feature-a/src' }, // { titlePrefix: 'app1-feature-a', directory: '../libs/app1/feature-a/src' }, // { titlePrefix: 'app1-feature-b', directory: '../libs/app1/feature-b/src' }, // { titlePrefix: 'app1-feature-c', directory: '../libs/app1/feature-c/src' }, // ... // ] } ; export default config ; 実装してみよう! 方針が決まったところで実装していきましょう。 事前の準備として、Storybookのパスを取得したいプロジェクトに tags を設定しておきましょう。Nxの @nx/enforce-module-boundaries ルールによる依存の制御を導入している場合は自然と設定されてあると思います。 // apps/app1/project.json { "name" : "app1" , "tags" : [ "scope:app1" ] , ... } // libs/app1/feature-a/project.json { "name" : "app1-feature-a" , "tags" : [ "scope:app1" , "type:feature" ] , ... } nx.dev @nx/devkit の createProjectGraphAsync を利用して、各プロジェクトの name および sourceRoot を取得します。 // apps/app1/.storybook/main.js import { createProjectGraphAsync } from '@nx/devkit' ; const getStories = async () => { const graph = await createProjectGraphAsync () ; // Storybookが不要なプロジェクトは無視 const ignoredProjects = [ 'app1-e2e' , 'app1-utils' ] ; return Object . keys ( graph . nodes ) . filter (( key ) => graph . nodes [ key ] . data . tags ?. includes ( 'scope:app1' )) // 関連するStorybookの絞り込み . filter (( key ) => ! ignoredProjects . includes ( key )) . map (( key ) => { const project = graph . nodes [ key ] . data ; return { titlePrefix : project . name , directory : `../../../ ${ project . sourceRoot } ` , } ; }) ; } ; あとはこれを stories に渡せば完成です。 // apps/app1/.storybook/main.js const config = { addons : [ '@storybook/addon-essentials' ] , framework : { name : '@storybook/react-vite' , options : {} , } , stories : getStories () , // 👈 プロジェクトの追加・削除に応じて自動的に設定されます } ; export default config ; Storybookが表示されました! Nxによる自動化のもう1つのメリットは、↓のように titlePrefix にプロジェクト名を関連付けることで、フィーチャ毎の分類がより明確になる点だと思います。 const project = graph . nodes [ key ] . data ; return { titlePrefix : project . name , directory : `../../../ ${ project . sourceRoot } ` , } ; 開発メンバーからは、 「モノレポ構造とStoryの構造がリンクすることで画面の使い勝手が非常に良い」 「検索したときにStory名が同じでもどの階層にいるか判断して目的にたどり着ける」 といったフィードバックを受けることができました👏 検索性の向上に一役買えましたね! 今回のサンプルは次のリポジトリから動作を確認できます。是非お試しください。 github.com まとめ この記事では @nx/devkit を利用したStorybookの設定の自動化についてご紹介しました。 Nxの機能を活用すれば「モノレポにプロジェクトを追加した後のStorybookの設定が漏れていた!」といった事とは無縁になるでしょう。 検証した時点では、 @storybook/test-runner と stories をAsync Functionで渡すパターンとの相性がまだ悪いようでした。今後の更新に期待したいですね。 今回はStorybookとの組み合わせでしたが、同じ仕組みを使ってGraphQL Codegenの設定自動化も可能であると確認しています。また別の機会にご紹介できればと思います。 それではまた次回! ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
アバター
【エンジニアの日常】エンジニア達の人生を変えた一冊 Part2 に続き、エンジニア達の人生を変えた一冊をご紹介いたします。 今回はPart3としまして、 Findy Freelance の開発チームメンバーから紹介します。 人生を変えた一冊 マスタリングTCP/IP―入門編 ハッカーと画家 コンピュータ時代の創造者たち UNIXという考え方 まとめ 人生を変えた一冊 マスタリングTCP/IP―入門編 マスタリングTCP/IP―入門編―(第6版) 作者: 井上 直也 , 村山 公保 , 竹下 隆史 , 荒井 透 , 苅田 幸雄 オーム社 Amazon 主にバックエンド開発と開発チームのリーダーを担当している中坪です。 私が紹介する「マスタリングTCP/IP―入門編」は通信プロトコルのTCP/IPの基礎について解説している書籍です。 私が最初にこの本を読んだのは、新卒入社した会社で、システムエンジニアとして働き始めた頃です。 当時、Webやスマホなどのアプリケーション開発部署への配属を希望していました。 しかし、実際にはネットワーク機器の設定、導入を業務とする部署に配属となりました。 最初はネットワークという分野に興味を持てず、仕事をする上での必要な知識も足りず、苦労しました。 そんなときに、先輩に勧められて読んだのがこの本です。当時、私が読んだのは第3版です。 本を読み進めながら、業務でPCとルータやファイアウォールを接続し、疎通確認をしたり、Wiresharkを使ってパケットの中身を確認する作業をしました。 本に記載されているプロトコルの仕様と、実際に目の前で行われている通信の挙動を結びつけることができました。 いくつか具体的な例を出すと次のようなことです。 IPアドレスとMACアドレスの役割 パケットのカプセル化の仕組み 代表的なプロトコルがTCP/UDPのどちらをベースにしており、何番ポートを使っているか デフォルトゲートウェイの役目やルーティングの挙動 そこから、徐々にネットワークに興味を持つようになりました。 目論見どおりに通信を制御できたときは、達成感を感じることができました。 今振り返ると、最初興味がなかったのは知識がないからであって、 理解することで後から興味が湧いてくること を体験しました。 また、 基礎を学ぶ大切さ や、本を読むことと 実践を組み合わせることで、学習が加速する ことも学びました。 エンジニアとしての原体験をもたらしてくれた一冊です。 新しい技術や役割に挑戦する際の姿勢に影響を与えてくれていると感じています。 自分がなにか新しいことを任せる立場になったときも、このときの経験を活かしていきたいと思っています。 今はネットワーク関連の仕事をしていませんが、 本ブログを書くにあたり、久々に最新の第6版を購入して読んでみました。 現在はWeb開発をしているので、その立場から見たときのおすすめの章は次の通りです。 1章 ネットワーク基礎知識 4章 IPプロトコル 5章 IPに関連する技術 8章 アプリケーションプロトコル 9章 セキュリティ ネットワークの分野でもIPv6やHTTP/3などの変化がありますが、TCP/IPはこの先も当分は通信の基盤として使われると考えています。 そのため、本書にある基礎知識は長い期間有効であり、 ネットワークを専門としないエンジニアであっても1度は読んでおいて損はないと思います。 ハッカーと画家 コンピュータ時代の創造者たち ハッカーと画家 コンピュータ時代の創造者たち 作者: ポール グレアム オーム社 Amazon インフラ・バックエンドエンジニア兼、Embedded SREの久木田です。 著者のPaul Grahamによって書かれたエッセイ集です。コンピュータ時代の革新を担うハッカーたちのものの考え方について書かれています。 各章は独立して書かれているので、どの章から読むことができます。 私がこの本を読んだのは大学1年のときです。もう10年以上前になります。 情報系の学科に入学してはじめてプログラミングにふれた自分としては、今後プログラマとして食べていけるのか、やっていけるのかを非常に悩んでいた時期でした。2011年当時は、プログラマ35年定年説やIT業界はブラックな環境が多いというネガティブな情報が目立っていたように感じ、その影響を受けていました。 そんなときに出会ったのがこの本でした。この本に書かれている「ハッカー」にすごく憧れて、勉強を続けていくこともできて、いまの職につけていると思っています。 私が一番好きな章は第16章の「素晴らしきハッカー」です。 良いハッカーとはどのようなことを好んでいるのか、何を大切にしているのかについて書かれています。良いハッカーとはプログラミングを本当に愛していて、コードを書くことを楽しんでいると書かれています。他にもどういった要素がハッカー足らしめているかを書かれているので興味を持って詳しく知りたいと思った方はぜひこの章を読んでいただきたいと思います。 また、良いハッカーを見分けるには同じプロジェクトで一緒に仕事をすることで初めてわかると書かれていて、当時はそういうものなのかと思って読んでいましたが、今は確かにそうかも知れないと思っています。エンジニアを採用する立場であったこともあるのですが、面接時にわからなかった特定の分野に関する知見の深さを同じチームで一緒に働くことで気づき、その人の凄さを初めて知ることが有りました。 この章のすべてが好きなのですが、特に好きなのは次の一節です。 何かをうまくやるためには、それを愛していなければならない。ハッキングがあなたがやりたくてたまらないことである限りは、それがうまくできるようになる可能性が高いだろう。14歳のときに感じた、プログラミングに対するセンス・オブ・ワンダー 1 を忘れないようにしよう。今の仕事で脳みそが腐っていってるんじゃないかと心配しているとしたら、たぶん腐っているよ。 大学での勉強や研究室配属後の取り組みを通して感じた、プログラミングの面白さやWebサーバ・ネットワークの仕組みを知ったときの感動が原体験となって私を形作っています。 初版が2005年 2007年 2 と古い本なので、エピソードはコンピュータ黎明期の話が多かったりしますが、ハッカーのマインドに関する説明などは今でも通じる部分は多いかと思います。ハッカーの考え方を理解したい人やコンピュータを扱う世界にいる人、飛び込もうとしている人には特におすすめしたい一冊です。 UNIXという考え方 UNIXという考え方: その設計思想と哲学 作者: Mike Gancarz オーム社 Amazon バックエンド開発を担当している金丸です。 この本はUNIXというOSの背後にある基本的な考え方を知ることができる一冊です。 UNIX自体の利用方法やコマンドについての説明はほとんどなく、UNIXがどのような思想に基づいて作られたかが説明されています。 本書と出会ったのは、新卒入社した会社で、情シスとしてFreeBSDを利用したサーバー管理の業務に従事しているタイミングでした。 当時の私は初めてのCLIに四苦八苦しており、ファイルをコピーするシェルスクリプトを作成するのにも苦戦していました。 「なんでコピー完了したことを教えてくれないんだろう」と同僚に話していたところ、この本を薦められました。 当初は疑問の答えを求めて読み進めていましたが、疑問の答えだけでなく、システムをどのように設計すべきかの指針も学ぶことができました。 プログラミング経験がなかった当時の私にとって、UNIXの考え方は初めて自分が理解できる内容で納得感のあるものでした。 この本は「定理」という形でUNIXの思想を説明しています。 9つの定理を紹介していますが、私の中で特に印象に残ったのは次の3つです。 定理1: スモール・イズ・ビューティフル 定理2: 一つのプログラムには一つのことをうまくやらせる 定理3: できるだけ早く試作を作成する 定理1と2ではUNIXというソフトウェアの大前提となる部分で、互いを補完している関係にあります。 この定理により、シンプルなコマンドを自由に組み合わせて処理を行うことができます。 例: ls , awk , sort コマンドを組み合わせて、ディレクトリ内のファイルを名前順に並べて表示するシェルスクリプト $ ls -l | awk '{print $9}' | sort スクリプトで利用されている ls コマンドはディレクトリが空の場合、何も表示せずプロンプトに戻ります。 これにより、次に組み合わせるコマンドに必要な情報だけを渡すことができ、コマンド同士がスムーズに連携できるようになっています。 $ ls $ この設計思想を知ったとき「なるほど!」と、非常に納得感がありました。 不要なメッセージを出さないことで、コマンドの組み合わせが直感的で柔軟にできるという点にUNIXの考え方への感銘を受けました。 上記の考え方を通じ、疑問と思っていた cp コマンドの役割はコピーすることであり、その機能のみをもつことが、定理2の「一つのプログラムには一つのことをうまくやらせる」に即していると理解しました。 合わせて、完了メッセージが必要であれば、コマンドを組み合わせて出力するのがUNIXらしい考え方だと解釈しました。 定理3では、プロトタイプを活用した開発の重要性を説いています。 この定理で紹介された次の一節が特に印象に残っています。 製品の完成後に百万のユーザーから背を向けられるより、少数から批判を受けるほうがはるかにいい。 当時の私は自分の仕事が批判されたように捉えてしまっていたため、プロトタイプを社内レビューで見せることに抵抗がありました。 ですが、この定理を読んで、自己本位の開発になっていることに気が付き、これではダメだと衝撃を受けました。 それ以来、プロダクトを誰のために作っているのかという意識を持つようになりました。 現在でも、機能の根幹となる部分から優先的に開発し、早い段階からPdMに都度確認してもらいながら実装を進めるスタイルを取っています。 実際の画面を確認していただくことで必要な情報が欠けていたことに気づくこともあり、細かく試作することでより良いプロダクトを作ることができると感じています。 紹介される定理はいずれもシステム設計の指針を示しており「設計の思想とは何か」を本書を通じて学ぶことができます。 設計を担当される方はもちろん、設計の指針となる考え方を学びたい方にもおすすめの一冊です。 まとめ いかがでしたでしょうか? 偶然ですが、今回はどれもメンバーの初期キャリアに影響を与えた本の紹介となりました。 このブログを読んでくださった方で、そのような本がある方も多いのではないでしょうか。 久々に読み返してみると、原点に立ち返ったり、新たな気づきを得ることができるかもしれません。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers ここでいうセンス・オブ・ワンダーとはプログラミングに触れたときに感じた感覚や感動を意味していると私は解釈しています。 ↩ 正しくは2005年でした。はてぶのコメントでご指摘があり気が付きました。ありがとうございます! ↩
アバター
ファインディ株式会社でフロントエンドのリードをしている新福( @puku0x )です。 皆さん、GitHub ActionsのLarger runnerはご存知でしょうか? 高性能なマシンを使ってCIを実行できる一方、変更の少ない場合や計算負荷の低いCIではコストパフォーマンスが悪くなってしまいがちですよね?🤷‍♂️ この記事では、Nxの機能を利用してLarger runnerを動的に切り替える方法をご紹介します。 Nxについては以前の記事で紹介しておりますので、気になる方は是非ご覧ください。 tech.findy.co.jp Larger runner(より大きなランナー) 課題 解決策 結果 まとめ Larger runner(より大きなランナー) Larger runnerは、「GitHub Teamプラン」または「GitHub Enterprise Cloudプラン」の場合に利用可能です。 docs.github.com プライベートリポジトリ用の通常のランナー(GitHub-hosted runner)は、Linuxマシンの場合、CPUは2コアとなりますが、Larger runnerでは4コアや8コア、16コアなどより高いスペックのマシンを選択できます。 Larger runnerの例 最近は ArmベースのCPUも利用できるようになり 、x64ベースのCPUを使う場合よりもコストを抑えられるようになりました。積極的に使っていきたいですね。 課題 Larger runnerは強力ですが、その分コストがかかります。 コードの変更が少ない場合や、CI全体が数分で終わってしまうような場合では、せっかくの高いスペックも宝の持ち腐れとなってしまうでしょう。 負荷が高い時だけLarger runnerのスペックを上げるにはどうすれば良いか? というのが今回の課題となります。 現在のGitHub Actionsには、変更の規模や負荷に応じて動的にランナーを切り替える機能が標準で備わっていないため、自分で組まなくてはいけません。 解決策 単純に変更されたファイル数や行数をカウントしても実際の影響範囲とは乖離があるため、より高度な制御が必要となります。 ✨そこで、Nxの登場です。✨ Nxはモノレポ内のプロジェクトの依存関係を Project Graph として保持しており、コードの変更から影響範囲を割り出すことが可能です。 次のコマンドを実行すると、影響されるプロジェクトをJSON形式で取得できます。 npx nx show projects --affected --json show - CLI command | Nx あとは実行結果をパースしてLarger runner切り替えの条件を組めば実現できそうです。 ワークフローの例を示します。 on : pull_request : jobs : check : runs-on : ubuntu-latest timeout-minutes : 5 outputs : runs_on : ${{ steps.output.outputs.runs_on }} steps : - uses : actions/checkout@v4 with : fetch-depth : 0 - uses : actions/setup-node@v4 with : node-version : 20 - uses : nrwl/nx-set-shas@v4 with : main-branch-name : ${{ github.base_ref }} - run : npm ci - name : Get affected projects id : get_affected_projects # 1. 影響されるプロジェクト数を算出 run : | length=$(npx nx show projects --affected --json | jq '. | length' ) echo "length=$length" >> "$GITHUB_OUTPUT" - name : Output id : output # 2. 影響されるプロジェクト数に応じたLarger runner名をセット run : | if [ $ {{ steps.get_affected_projects.outputs.length }} -gt 20 ] ; then echo "runs_on=arm64-4-core-ubuntu-22.04" >> $GITHUB_OUTPUT else echo "runs_on=arm64-2-core-ubuntu-22.04" >> $GITHUB_OUTPUT fi build : needs : check # 3. 指定されたLarger runnerで実行 runs-on : ${{ needs.check.outputs.runs_on }} timeout-minutes : 30 steps : - uses : actions/checkout@v4 with : fetch-depth : 0 - uses : actions/setup-node@v4 with : node-version : 20 - uses : nrwl/nx-set-shas@v4 with : main-branch-name : ${{ github.base_ref }} - run : npm ci - run : npx nx affected --target=build ※ actions/checkout や actions/cache は適宜最適化しましょう(1分ほど速くなる余地あり) ワークフローを実行すると、まず影響されるプロジェクト数が算出されます。 後続のジョブでは、その結果を元に runs-on: ${{ needs.check.outputs.runs_on }} で利用するLarger runnerを設定します。 やりたかったことが実現できていますね!🎉 負荷の高い時は高スペックのLarger runnerが動くため、CI時間の短縮が見込めます。負荷が低い時は、Larger runnerのスペックを落としてコストを節約できます。 結果 直近100回のCI結果を元に、4コアマシン固定の場合と2〜4コア可変の場合でコストを計算してみました。 Before(4コア固定) After(2〜4コア可変) $6.53 $4.79 ※ArmベースのCPUを使用する想定でコストを算出しています ※直近100回中、約1割が高負荷なCI(約12分)、残りが低負荷なCI(約5分)でした Larger runnerを動的に切り替える方法を採用することにより、コストを3割ほど削減できました。 以前の状態と比較してコストパフォーマンスが改善されたと思います。 まとめ この記事では、Nxの機能を活用してLarger runnerを動的に設定することで、コストパフォーマンスを改善する方法を紹介しました。 今回は runs-on の切り替えのみ紹介しましたが、他にもNx CLIの --parallel オプションの動的設定など応用は様々です。 皆さんの参考となりましたら幸いです。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
アバター
こんにちは。 Findy で Tech Lead をやらせてもらってる戸田です。 このテックブログでは開発生産性を向上させるための取り組みや、開発テクニックを紹介してきました。 意外に思われるかもしれませんが、弊社では全てのことを100%でやってるわけではなく、ユーザーへの価値提供を最優先するために後回しにしている部分もあります。 しかし、その影響で障害が多発したり、困ったことになることは滅多にありません。 そこで今回は、ユーザーへの価値提供を最優先するために弊社で実践していることを紹介していこうと思います。 それでは見ていきましょう! 綺麗なコードは後。アプリケーションの振る舞いが先。 コミットの粒度は不問。Pull requestの粒度は維持。 実装途中のコードでもマージOK まとめ 綺麗なコードは後。アプリケーションの振る舞いが先。 Pull requestをレビューする中で、「もっと良い書き方がありそうで、議論が長引いて中々マージされない」といったケースがあるかと思います。 そのような場合、弊社ではテストが網羅されていればマージしてOKというスタンスを取っています。 もちろん、パフォーマンスやセキュリティ等に悪影響が出るようなコードはマージしませんが、基本的にはアプリケーションの振る舞いに影響がなければマージします。キレイなコードを書くことは非常に重要ですが、ユーザーが求めているのはアプリケーションの振る舞いであるからです。 マージ後に良い書き方を思いついたら、そのタイミングでリファクタのみの修正を行ったPull requestを出します。 後でリファクタするとしても、アプリケーションの振る舞いをテストコードで守っているので、強気でリファクタできます。 弊社のとあるリポジトリでは、リファクタのPull requestが1ヶ月で50個程度作成されていました。思いついたり気づいたりしたら日常的にリファクタを行う文化が根付いている証拠です。 このように、最初の段階で綺麗なコードを突き詰めることよりも、ユーザーに早く価値提供をすることを優先しています。テストコードの存在が、このような開発手法を許しているのです。 コミットの粒度は不問。Pull requestの粒度は維持。 Pull requestの粒度については以前、 別の記事 で触れましたが、コミットの粒度に関してはレビュー対象にはしていません。 コミットの粒度まで指摘してしまうと気軽に修正できなくなり、開発そのものを楽しむことが難しくなってしまうと考えているからです。 もちろん、試行錯誤のコミットが多すぎてgitのログに悪影響を及ぼすと判断したらrebaseなどで纏めることはありますが、基本的にローカル環境では自由に色々と試して欲しいと考えています。 その代わり、コミットメッセージには一定のルールを設けています。コミットメッセージのルールやprefixには Conventional Commit や Semantic Versioning などがあります。 例えば既存のAPIに対する機能追加があった場合のコミットメッセージは、 minor-feat: add hoge feature のようになります。 コミットメッセージのprefixに悩むということは、1つのコミットの中でたくさんのことをやりすぎているということに気づくことができます。 このようにコミットメッセージにルールを設けることによって、自然に一定内のコミットの粒度を維持できるようになっています。そのため、コミットの治安が悪くなることはほとんどありません。 実装途中のコードでもマージOK 実装途中のコードであっても、次の条件の全てを満たす場合はマージを許容しています。 テストコードがあり、CIが通っている 本番環境の振る舞いに影響がない 実装途中であることが、コードやPull requestのコメントなどで明確になっている 対応スコープ内の全てが完了してからマージするのではなく、Pull requestを細かく作成し、少しずつ作成、修正をしていくような流れです。 マージしてしまうとどうしても本番環境の振る舞いに影響が出てしまう場合は、Feature Flagを使ったりtopic branch運用を行うこともあります。 Feature Flagやtopic branch運用に関しては、↓の記事を参照してください。 tech.findy.co.jp tech.findy.co.jp 対応予定の全てが完了するまでマージを止めてしまうと、base branchとの差分が大きくなりやすく、conflictや不具合が発生しやすくなり、ユーザーへの価値提供までのスピードが遅くなってしまいます。 本番環境で利用されないコードをマージしてしまうのは抵抗感があるかもしれませんが、本番環境の振る舞いに影響がないということは悪影響もないはずであり、ユーザーへの価値提供を最優先するためにこのような開発手法になっています。 本番環境への影響がないということについては、既存コードに対するテストコードによって守られているため、強気でマージ出来るようになっています。 まとめ いかがでしたでしょうか? 開発組織によっては「そんなことやっていいの?」と思われるかもしれませんが、弊社ではユーザーへの価値提供を最優先するために、このような開発手法になっています。 もちろん、しっかりとしたテストコード、CI/CD環境が整っているからこそ、このような開発手法が可能となっています。 他にも、このような開発手法を支えている開発テクニックを別記事にて紹介していますので、興味がある方は是非読んでみてください。 tech.findy.co.jp tech.findy.co.jp 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
はじめに こんにちは。ソフトウェアプロセス改善コーチでFindy Tech Blog編集長の高橋( @Taka_bow )です。 経済産業省の2019年発表によると、日本のIT人材不足が2030年には79万人に達する可能性があると予測され、しばしばメディアにも引用されてきました。 この調査レポート発表から5年以上が経過しましたが、果たして79万人という人材不足は現実となるのでしょうか? 今回は最新のデータからこの予測を検証してみたいと思います。 2023年11月2日のNHKニュース www3.nhk.or.jp 2024年7月9日 5:00 (2024年7月13日 17:40更新) 日経新聞 [会員限定記事] www.nikkei.com はじめに 「IT人材需給に関する調査」とは 労働生産性の低さ 最新のデータを読む 「人月の神話型請負」が生産性向上を阻む 受託開発でもアジャイル開発はできる お知らせ! 「IT人材需給に関する調査」とは このレポートは、みずほ情報総研株式会社が2015年に経済産業省からの受託調査研究として実施した調査レポートです。 2019年3月に経済産業省から発表されました。当時話題となったのは、このデータです。 出典:「IT 人材需給に関する調査」(経済産業省)( https://www.meti.go.jp/policy/it_policy/jinzai/houkokusyo.pdf ) これは 「今後、IT需要が高位に推移した場合約79万人の人材不足になる可能性がある」 という試算でした。なお、需要が中位シナリオで約45万人、低位シナリオでも約16万人の不足する可能性があります。 この背景には2つの要因があります。 日本の労働人口(特に若年人口)が減少 (新卒IT人材の入職率は一定右肩上がり) 日本の労働生産性が低い (2022年、OECD 加盟 38 カ国中 30 位) 日本の労働人口減少に関しては、IT業界に限らず日本全体の問題です。 冒頭のNHKニュース「日本のIT人材79万人が不足? インドで始まった人材獲得戦略」は、まさしくこの問題に対応するための動きのひとつと言えます。 労働生産性の低さ さて、もうひとつの要因「労働生産性の低さ」ですが、次の表をごらんください。これは上記のグラフの一部を表にしたものです。 出典:「IT 人材需給に関する調査」(経済産業省)( https://www.meti.go.jp/policy/it_policy/jinzai/houkokusyo.pdf )を加工して作成 IT需要が 高位 と仮定したとき、 赤で囲んだ部分: 生産性上昇率が0.7% だった場合、 78.7万人の人材不足 青で囲んだ部分: 生産性上昇率が5.23% だった場合、 人材不足はゼロ を表しています。生産性上昇率が5.23%以上アップしていれば人材不足は起きない、という予測です。 では、最新の労働生産性はどうなっているでしょう。 最新のデータを読む 現時点で最新の日本生産性本部が公表している「労働生産性の国際比較2023」から、情報通信業の労働生産性の推移分析を見てみました。 出典:日本生産性本部「労働生産性の国際比較2023」から情報通信業の労働生産性の推移 https://www.jpc-net.jp/research/detail/006714.html 日本の2000年から2021年にかけての生産性上昇率(年率平均)は「-0.1%」にとどまっています。これは、経済産業省が予測した平均成長率0.7%を大きく下回る結果です。 そして、調査レポートが予測する「78.7万人の人材不足」というシナリオよりも深刻な状況である可能性があります。 日本の特徴は、 付加価値額は拡大傾向にある 就業者数も同等ペースで増加している ことから、付加価値が就業者数の増加に見合った成長をしておらず、生産性の上昇を抑えてしまっていると考えられます。 「人月の神話型請負」が生産性向上を阻む なぜ、比較的安定推移すると言われる情報通信業において、日本の生産性上昇率は低いのでしょうか。 ひとつの要因として、日本におけるソフトウェア産業の構造的な問題が挙げられます。 まず、多くの企業はソフトウェア開発を外部の受託企業に委託する傾向があります。その際、事前に詳細な仕様を確定し、その通りに開発を進める「ウォーターフォール型」の手法が一般的です。 *1 この手法では変更に柔軟に対応することが難しく、生産性向上に課題があると指摘されています。 さらに、多くの受託開発は「人月」に基づく見積もりと請負契約に依存しており、多重下請け構造が蔓延しています。この構造は、柔軟な対応や効率的な開発を妨げる要因となっています。 私はこれを「人月の神話型請負」と呼んでいます。 定義:「人月による見積もりを前提とし、事前合意の仕様とプロセスを厳守するため柔軟な変更対応が難しい請負契約」 「ブルックスの法則」や「銀の弾などない」で有名なフレデリック・ブルックスは、著書「人月の神話(The Mythical Man-Month)」の中で、このように述べています。 私たちが使っている見積もり手法は、コスト計算を中心に作られたものであり、労力と進捗を混同している。人月は、人を惑わす危険な神話である。なぜなら、人月は、人と月が置き換え可能であることを暗示しているからである。(第二章「人月の神話」) この本が書かれたのは1975年、私でさえ幼少期であり、読者の多くは生まれる前の話ではないかと想像します。 しかし、現代日本のIT業界では「人月で見積もる」が未だに健在である事実に着目せざるをえません。 なぜなら「人月の神話型請負」には、次のような負の側面があると考えられるからです。 負の側面 内容 要求仕様の硬直化 事前合意した仕様に縛られ、 状況に応じた柔軟な改善が難しい 形式的なプロセス管理 決められた手順の遵守が優先され、 効率化や創意工夫の余地が少ない 人月ベースの評価 投入工数で報酬が決まるため、 生産性向上への意欲が生まれにくい 人員増加による解決 課題への対応を人員増員で行うため、 チームの効率が低下しやすい エンジニアの裁量不足または制限 顧客からの指示が中心となり、 技術的な改善提案を行いにくい 技術力の蓄積不足 技術資産が顧客側のものであり、 自組織内にナレッジが貯まらない これらの負の側面は、開発チームの「自立」を損ない、エンジニア個人の「自律」も抑え込みます。 結果として、組織全体の生産性が低下し、改善や変革が難しくなる要因となります。 また、「人月の神話型請負」の問題は、すぐには目に見えにくい「遅効性の毒」のように、ゆっくりと組織全体を蝕むのが特徴です。 この構造的な問題に対処するためには、柔軟で価値を重視した新たな開発体制への転換が不可欠です。少なくとも、次の2点を変えることが必要だと思います。 事前確定型から状況適応型の開発プロセスへの移行 形式的な遵守よりもビジネス価値を重視した評価の導入 つまり、「人月の神話型請負」から脱却し生産性向上を図るには、 アジャイル開発を基盤に据え、委託側と受託側双方で「価値を共に創り出す体制」を築く ことが重要です。 受託開発でもアジャイル開発はできる しかし、「人月の神話型請負」をビジネスモデルの柱としてきた多くのソフトウェア受託企業にとっては、どこから手をつけるべきかが大きな悩みだと思います。 そんな難題に光を当てるヒントが、2024年10月30日に開催された「 プロジェクト成功への挑戦 」というイベントで共有されています。 イベントでは、株式会社永和システムマネジメント Agile Studio プロデューサー/アジャイルコーチの木下史彦さん( @fkino )が、「アジャイル開発と契約」のテーマで、主に「モデル契約」についてお話しくださいました。 木下さんは、従来型の契約が「決めたことを守る」ことを重視するのに対し、アジャイル開発における契約では「変化に対応する」柔軟な仕組みが重要であると説明。 さらに、IPAのモデル契約を例に、準委任契約の採用や、スプリントごとの計画調整が可能な体制づくりをどう進められているかについても詳しく解説されました。 speakerdeck.com 続いて、株式会社永和システムマネジメントのエンジニア、藤田みゆきさんからは「受託開発でのアジャイル奮闘記」と題して、実際の受託開発におけるアジャイル導入の取り組みをご紹介いただきました。 speakerdeck.com 永和システムマネジメントの皆様が示してくださった変化に対応する具体的なアイデアや実践方法は、『人月の神話型請負』から脱却するための大きな一歩となると感じました。 以下に、イベントの録画ビデオもリンクしていますので、ぜひご覧ください。 youtu.be お知らせ! 現在、私と一緒にイネイブリングチームの立ち上げを行うメンバーを探しています! イネイブリングチームは、エンジニア組織だけではなくファインディ社全体を支援するチームとしていく予定です。 組織全体のエンジニアリング力を向上 開発スキル向上のためのトレーニングやワークショップを実施 プロセス改善の提案とコーチングを行い、開発生産性とDevExを向上 社内外のエンジニアを対象とした活動を展開 このチームで、ファインディの成長エンジンとなりませんか?興味がある方は、ぜひこちらをクリックしてみてください↓ herp.careers エンジニアのポジションは他にも色々あります。ファインディでは一緒に働くメンバーを募集中です! herp.careers では、本日はこのへんで。 See you! *1 : ウォーターフォール型の元となったと言われるウィンストン・W・ロイスによる論文”Managing the Development of Large Software Systems(1970)”では「手戻り」が推奨されています
アバター
こんにちは。 ファインディでソフトウェアエンジニアをしている栁沢です。 ファインディの各プロダクトでは、1日に複数回デプロイしています。 例えば、私が所属するFindy転職のプロダクトでは、1日に6回ほど本番環境にデプロイしています。 高いデプロイ頻度でもデプロイ起因による障害や不具合がほぼ発生しておらず、開発スピードと品質の両立を実現できています。 今回はファインディ社内でのFeature Flagの使い方について詳しく解説します! Feature Flagを使うことのメリット Feature Flagの実現方法 Feature Flagを使った開発の流れ 1. Feature Flagを追加する 2. Feature Flagを使って新機能を実装・テストコードを書く 3. 検証用の環境で動作確認を実施する 4. 動作確認が完了したら、本番環境で機能を有効化させる 5. 一定期間の安定稼働を確認できたら、Feature Flagの処理を削除する まとめ Feature Flagを使うことのメリット Feature Flag(フィーチャーフラグ)は、コードを書き換えることなく特定の機能を有効化や無効化できる開発テクニックです。 Feature Flagで機能を無効化しておくことで、ユーザーに影響がでないように本番環境にコードをガンガン反映させることができるようになります。 これにより次のようなメリットがあります。 開発途中でもどんどんメインブランチにマージできる。そのため、複数人の開発でもコンフリクトがほぼ起こらない。 本番環境で特定のユーザーやセグメントのみに機能を限定公開できるので、リスクを抑えながら機能を公開できる。 万が一本番環境で問題があった場合、機能を無効化させることですぐに切り戻しができる。 Feature Flagの実現方法 Feature Flagの実現方法は、ライブラリやSaaSを使うなど様々な方法がありますが、現在のFindyでは「環境変数」と「ライブラリ」の2パターンが採用されています。 Findy転職プロダクトでは、導入や運用がシンプルな「環境変数」を使ってFeature Flagを実現しています。 環境設定ファイルで環境変数を追加することで機能を有効化させる 環境変数の値で条件分岐を実装して機能をだし分ける 実際のコード例を見てみるとシンプルなことがわかると思います。 envファイルに環境変数を追加することで機能を有効化させます。逆に、環境変数を削除することで機能を無効化できます。 # .env FEATURE_NEW_LABEL=true 実装コードは、環境変数の値によって条件分岐を実装することで振る舞いを変えています。 export const SampleComponent = () => { // 特定の環境変数の値が'true'なら"NEW"というラベルを表示する const isEnabledNewLabel = process .env.FEATURE_NEW_LABEL === 'true' ; return ( < div > { isEnabledNewLabel && < span > NEW </ span > } < span >Sample</ span > </ div > ); } ; 補足:この実現方法の伸びしろとしては、「環境設定ファイルの修正が手間」、「フロントエンドとAPIでFeature Flagが分散している」、「一部のユーザーに部分的に公開がしづらい」という課題があります。そのため別チームでは、ライブラリを使ってAPIから機能のON/OFF情報を取得する仕組みでFeature Flagを実現しているようです。 Feature Flagを使った開発の流れ ここからは、より具体的にFeature Flagを使った開発の流れを説明していきます。 Feature Flagを追加する Feature Flagを使って新機能を実装・テストコードを書く 検証用の環境で動作確認を実施する 動作確認が完了したら、本番環境で機能を有効化させる 一定期間の安定稼働を確認できたら、Feature Flagと条件分岐を削除する 1. Feature Flagを追加する 環境変数を追加し、ローカル環境や検証用環境で機能を有効化させます。 # .env.local や .env.qa FEATURE_NEW_LABEL=true Tipsとして、早めに検証用環境で機能を有効にしておくことでバグを早く発見しやすくなり、バグ対応の工数を減らせます。(シフトレフトを進められる) 2. Feature Flagを使って新機能を実装・テストコードを書く 環境変数の値によって条件分岐をすることで機能のON/OFFをできるようにします。 export const SampleComponent = () => { // 特定の環境変数の値が'true'なら"NEW"というラベルを表示する const isEnabledNewLabel = process .env.FEATURE_NEW_LABEL === 'true' ; return ( < div > { isEnabledNewLabel && < span > NEW </ span > } < span >Sample</ span > </ div > ); } ; また、機能をON/OFFしたときに、それぞれのケースで振る舞いが壊れないように自動テストを書いておきます。 describe ( 'SampleComponent' , () => { it ( 'should render "NEW" label when FEATURE_NEW_LABEL is true' , async () => { process .env.FEATURE_NEW_LABEL = 'true' ; render(< SampleComponent />); expect ( screen .getByText( 'NEW' )).toBeInTheDocument(); } ); it ( 'should not render "NEW" label when FEATURE_NEW_LABEL is false' , async () => { process .env.FEATURE_NEW_LABEL = 'false' ; render(< SampleComponent />); expect ( screen .queryByText( 'NEW' )).not.toBeInTheDocument(); } ); } ); 自動テストを書いておくことで、開発途中でもどんどんメインブランチにマージして、自信をもって本番環境にデプロイできます。 3. 検証用の環境で動作確認を実施する 機能開発が完了したら、検証用の環境で動作確認を実施します。 本番環境では機能が無効化されているため、ユーザーに影響ない形で検証を進めることができます。 4. 動作確認が完了したら、本番環境で機能を有効化させる 検証環境で動作確認が完了したら、本番環境で機能を有効化させます。 # .env.production FEATURE_NEW_LABEL=true 本番環境で深刻な不具合が発生し、機能を緊急で無効にする必要がある場合は、環境変数を削除することで簡単に切り戻しができます。 # .env.production # 削除 # FEATURE_NEW_LABEL=true 切り戻しを素早くできるのもFeature Flagの強みです。 5. 一定期間の安定稼働を確認できたら、Feature Flagの処理を削除する 最後に、機能が実際に使われて安定稼働していること確認できたら、環境変数と分岐の処理を削除していきます。 export const SampleComponent = () => { return ( < div > < span >NEW</ span > < span >Sample</ span > </ div > ); } ; Feature Flagの削除時に間違って必要な処理を削除してしまいデグレをおこしてしまうミスはやりがちです。しかし、自動テストがあることで自信をもって削除作業を進められることができます。 まとめ 今回は、Findyの爆速開発を支えるFeature Flagの使い方を紹介しました。 Feature Flagを使うことで、ユーザーに影響ない形で、開発途中でもどんどんメインブランチにマージできます。これにより、本番環境へのデプロイも1日に複数回実施することが可能になります! 個人的に爆速開発を支える1つの要素として、Feature Flagの活用は必須だと感じています! もしまだ導入していない場合は、この記事をきっかけにぜひトライしてみてください :) 他にもファインディではいろいろなテクニックを使っているので、興味がある方は次の記事も合わせて読んでみてください。 Findyの爆速開発を支える「システムを守るテストコード」の実例3選 - Findy Tech Blog Findyの爆速開発を支えるPull requestの粒度 - Findy Tech Blog 現在、ファインディでは一緒に働くエンジニアを募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
こんにちは。 Findy Tools の開発をしている林です。 私たちのプロジェクトではフロントエンドのフレームワークにNext.js App Routerを使っており、AWSのECSへデプロイして運用しています。 そして、一部のレンダリングの処理が重いページのキャッシュを実装する際に、直面した課題と解決策を紹介します。 Next.jsのキャッシュ機構について 今回実現したいこと 課題と解決策 課題1: Next.jsの機能では要件に合わない 解決策1: CloudFrontのみでキャッシュ 課題2: エラーページがキャッシュされる 解決策2: Lambda@Edgeを用いたCache-Controlヘッダー制御 まとめ Next.jsのキャッシュ機構について まず、Next.jsのキャッシュ機構について簡単に説明します。 Next.jsではサーバサイドで使えるキャッシュ機構が次の3種類あります。 Request Memoization 同一リクエストの中で外部APIリクエストをメモ化する Data Cache 外部APIのレスポンスなどを一定時間キャッシュする Full Route Cache HTMLおよびRSC Payloadをユーザセッションまたは共通で一定時間キャッシュする キャッシュの細かい仕様に関しては詳しくは公式ドキュメントを参照してください。 Building Your Application: Caching | Next.js また、Full Route Cacheでは revalidatePath APIで任意のタイミングでキャッシュを削除できます。 Functions: revalidatePath | Next.js 今回実現したいこと 今回の要件は、 一部の処理の重いページのレンダリング結果を丸ごとキャッシュしてレスポンスを高速化したい というものです。 これには、Findy Toolsはメディアサイトという側面もあり、多くのページは更新頻度がそれほど高くなく、フルページでのキャッシュがやりやすいという背景もあります。 さらに、ログインが必要なユーザ固有の情報はすべてクライアントフェッチをしているため、ユーザごとのキャッシュを考慮する必要もありませんでした。 また、エンドユーザーが利用するフロントエンドアプリケーションの他に、コンテンツ管理用のアプリケーションがあります。そして、この管理用アプリケーションからコンテンツが更新された際に、フロントエンド側にすぐに反映させたいという要件もありました。 補足: 現状のFindy Toolsのインフラ構成図は次の図のようになっています。 すべてAWS上に構築されており、CloudFrontを経由してNext.jsへリクエストを送っています。 また、コンテンツ管理用のアプリケーション(Rails)もECSへデプロイしています。 Findy Tools インフラ構成図 2ヶ月でリリースしたFindy Toolsの技術選定の裏側 - Findy Tools より 課題と解決策 今回の要件を実現するにあたり、直面した2つの課題と解決策をそれぞれ紹介します。 課題1: Next.jsの機能では要件に合わない まず、ページのレンダリング結果を丸ごとキャッシュしたかったので、Full Route Cacheを検討しました。 しかし、セルフホスティングのNext.jsではキャッシュをメモリと .next/cache ディレクトリに保存しているため、ECSのコンテナを水平スケールして複数台でリクエストを受ける構成にすると、キャッシュの一貫性がなくなります。 負荷軽減目的のキャッシュで、一貫性が問題にならない場合はこれでも良いですが、キャッシュの削除をする revalidatePath が全てのコンテナで実行されず、一部で古いキャッシュが残るのは今回の要件を満たせません。 このため、ドキュメントにはコンテナオーケストレーションプラットフォームではRedisやS3のような外部のストレージにキャッシュを保存するようcache-handlerを実装する必要があると紹介されています。 Building Your Application: Deploying | Next.js Data CacheやFull Route Cacheをフルに活用したい場合には、カスタムのcache-handlerを用意しても良いと思いますが、今回の要件にはオーバースペックすぎると判断しこれは見送りました。 また、Next.jsのFull Route Cacheを使った場合にもレスポンスヘッダーに Cache-Control: stale-while-revalidate が付与されるため、 削除の際にcache-handlerのデータと後術するCDNのキャッシュの2つを削除する必要もあり、複雑になるということもFull Route Cacheを見送った理由の1つです。 解決策1: CloudFrontのみでキャッシュ 私たちの構成ではNext.jsへのリクエストは静的ファイルを配信するため、CDNであるCloudFrontを経由させています。 そこで、Next.jsのアプリケーションレベルではキャッシュをせず、CDNでレンダリングされたHTMLやRSC Payloadをキャッシュすることにしました。 当初、Next.js App Routerでは特定のページにカスタムのCache-Controlヘッダーを付与するにはmiddlewareの処理で対処する必要がありましたが、v14.2.10 からPagesRouterと同様に next.config.js の headers() で指定できるようになりました。 Add ability to customize Cache-Control by ijjk · Pull Request #69802 · vercel/next.js next.config.js で特定のパスにCache-Controlヘッダーを設定するコードの例を以下に示します。 module . exports = { async headers () { return [ { source : '/path/:slug' , headers : [ { key : 'Cache-Control' , value : 's-maxage=86400, stale-while-revalidate' , } , ] , } ] } , } これで、CloudFrontの設定でオリジンのCache-Controlヘッダーを元にキャッシュするよう設定すると、CDNでレスポンスをキャッシュができます。 コンテンツが更新された際のキャッシュの削除は、CloudFrontのAPIで特定のパスのキャッシュを削除するようにコンテンツ管理用アプリケーションから呼び出しています。 課題2: エラーページがキャッシュされる CloudFrontではHTTPレスポンスコードが404や一部の5xxの場合も、Cache-Controlヘッダーに基づいてキャッシュされる仕様になっています。 CloudFront がオリジンからの HTTP 4xx および 5xx ステータスコードを処理する方法 - Amazon CloudFront 特に私たちのプロジェクトでは、公開前のコンテンツのURLにアクセスされることがあり、404ページがキャッシュされてしまうという課題がありました。 公開前にキャッシュの削除をすれば良いのですが、時間を指定して公開するケースではキャッシュの削除が複雑になってしまいます。 一見するとNext.jsでバックエンドのAPIから情報を取得して、404やエラーの時はCache-Controlヘッダーをno-cacheに指定すれば解決しそうなのですが、Next.jsではレスポンスをストリーミングしており、リクエストの最初に処理されるmiddlewareやnext.config.jsのheadersの設定を条件に基づいて変更できません。 これに関してはページごとにカスタムヘッダーを設定できるような generateHeaders() というAPIが必要か、という議論がNext.jsのGitHub Discussionsで行われています。 App Router Custom Header Use Cases · vercel/next.js · Discussion #58110 解決策2: Lambda@Edgeを用いたCache-Controlヘッダー制御 Next.jsではCache-Controlヘッダーを条件に基づいて変更することが出来ないため、別のアプローチとしてLambda@EdgeでステータスコードによってCache-Controlヘッダーを書き換えることにしました。 Lambda@EdgeはオリジンとCloudFrontのキャッシュ(Regional Edge Cache)の間で動作し、リクエストやレスポンスを操作することが出来ます。 次のコードはオリジンのNext.jsのレスポンスを受け取り、ステータスコードが4xx, 5xx系の時にCache-Contorolヘッダーをno-cacheに上書きしてCloudFrontへレスポンスを返すような例です。 'use strict' ; export const handler = async ( event , context , callback ) => { const response = event . Records [ 0 ] . cf . response ; const headers = response . headers ; if ( response . status >= 400 && response . status <= 599 ) { headers [ 'cache-control' ] = [{ key : 'Cache-Control' , value : "private, no-cache, no-store, max-age=0, must-revalidate" }] ; } callback ( null , response ) } ; これにより、Next.jsのステータスコードによってCache-Controlヘッダー書き換え、CloudFrontにキャッシュしないということが実現可能になりました。 まとめ この記事では、App Routerを用いた場合の「コンテンツ更新の際のキャッシュ削除」という課題に対して次の工夫をして、エラーレスポンスまで含めた柔軟なキャッシュ制御を実現しました。 AWS CloudFrontを用いてApp Routerのレスポンスをキャッシュ Lambda@Edgeを用いてCache-Controlヘッダーを書き換え また、レンダリングの重いページや更新頻度の低いページをCloudFrontから返すことで、レスポンスタイムが改善され、ユーザ体験の向上やサーバ負荷の軽減も達成できました。 Next.js App Routerは一部の機能においてまだ発展途上ですが、将来的に、セルフホスティングやCDNでのキャッシュが扱いやすくなることに期待ですね。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
アバター
ファインディ株式会社でフロントエンドのリードをしている新福( @puku0x )です。 弊社では Nx を活用してCIを高速化しています。この記事では、最近導入した Nx Agents でフロントエンドのCIをさらに高速化した事例を紹介します。 Nxについては以前の記事で紹介しておりますので、気になる方は是非ご覧ください。 tech.findy.co.jp フロントエンドのCIの課題 Nx Agents Nx Agents導入の結果 Nx Agents利用上の工夫 プロジェクトを細かく分割する Node.jsのバージョンを揃える キャッシュの活用 特定のステップの省略 高度なエージェント割り当て まとめ フロントエンドのCIの課題 これまで「キャッシュの活用」や「並列化」「マシンスペックの向上」といった工夫により、フロントエンドのCIを高速化してきました。 しかし、コードベースの増大により時間のかかるタスクにCI時間が引きずられてしまう問題が顕著になってきました。 次の図は、キャッシュヒットしなかった場合のCI時間の一例です。 他のタスクが早く終わっても一番時間のかかるタスクを待つ必要があるため、結果としてCI時間が伸びる傾向にありました。 Nx Agents タスク単位の並列化では、CI時間のボトルネックを解消するのが困難です。 「DTE(Distribute Task Execution)」はその問題を解決する手法であり、Nx CloudにはDTEのマネージドなサービスである「 Nx Agents 」が提供されています。 Nx Agentsの動作イメージ(Nx公式ドキュメントより抜粋) Nx Agentsは今年2月にリリースされました。費用はGitHub Actionsより安価となっています。 blog.nrwl.io Nx Agents GitHub Actions(Larger runner) Linux 2コア $0.0055/min $0.008/min Linux 4コア $0.011/min $0.016/min 参考: Nx Cloud - Available Plans GitHub Actions の課金について - GitHub Docs 使い方は非常に簡単で、NxワークスペースをNx Cloudに接続した後、CIのタスク実行前に次のようなコマンドを追加するだけです。 npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" 利用するマシンの数やスペックは、変更の影響範囲に応じて動的に設定できます。 npx nx-cloud start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yml" # .nx/workflows/dynamic-changesets.yml distribute-on : small-changeset : 3 linux-medium-js medium-changeset : 6 linux-medium-js large-changeset : 9 linux-large-js DTE自体はGitHub Actionsの matrix の機能を用いて構成することも可能ですが、CIが失敗した場合は 必ずDTEホスト側のマシンをRe-runする必要があります 。検証した限りでは誤操作を確実に防ぐ方法が無かったため、Nx Agentsの利用をおすすめします。 Nx Agents導入の結果 Nx Agentsを有効にした場合のCI時間の例を示します。 このワークフローでは、アプリケーションのビルドやテスト、型チェックなどを全てNx Agentsで実行する構成となっています。 npx nx-cloud start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yml" npx nx affected --target=build,build-storybook,test,lint,stylelint,typecheck --configuration=ci npx nx-cloud complete-ci-run 元の構成では約18分かかっていたものが約11分になりました。 CI本体が約10分、後述する事前処理が追加で約1分かかる計算です。 CI時間は約7分削減されており、およそ 40% ほど高速化できたということがわかりました。 各タスクが複数のエージェントで分散実行されたことで、タスク単位で並列化していた場合よりも高速化できました。 Nx Agents利用上の工夫 Nx Agentsは今年リリースされたばかりのサービスであるため、ドキュメントの不備やノウハウの不足といった課題があることに注意しましょう。ドキュメントに示されているセットアップの例は、最低限のものしかないため最適化の余地があります。 ここでは、弊社で実践している利用上の工夫を紹介します。 プロジェクトを細かく分割する DTEの性質上、単体のタスクの実行時間が長くなるほどエージェントの利用効率が下がります。 コードを全て apps/** 側に置くとキャッシュヒット率が下がりCI時間も延びるため、まずは libs/** に分離することから始めると良いと思います。 共有ライブラリについては、コンポーネント系、ユーティリティ系で別のライブラリとして作ると良いでしょう。 弊社の場合、例えばビルド時間が 2分 を超えるようなものを確認した場合は、モジュールの移動や分割を実施しました。 分割が難しい場合は、Nx Agentsの実行対象から外すといった工夫が必要かもしれません。 Node.jsのバージョンを揃える nx-cloud-workflows の workflow-steps/install-node は nvm に対応しています。nvm以外の管理ツールを利用している場合は自前でセットアップ用のスクリプトを書く必要があります。 例として asdf を用いる場合のスクリプトを示します。 # .tool-versions nodejs 20 . 18 . 0 # .nx/workflows/agents.yml launch-templates : custom-linux-medium-js : resource-class : 'docker_linux_amd64/medium' image : 'ubuntu22.04-node20.11-v10' init-steps : - name : Checkout uses : 'nrwl/nx-cloud-workflows/v4/workflow-steps/checkout/main.yaml' - name : Setup Node.js script : | git clone https://github.com/asdf-vm/asdf.git $HOME/.asdf --branch v0.14.1 source $HOME/.asdf/asdf.sh asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git asdf install nodejs echo "PATH=$PATH" >> $NX_CLOUD_ENV CI時間は5〜10秒ほど延びますが、Node.jsのバージョン不一致によるエラーを回避できます。 キャッシュの活用 workflow-steps/cache を利用した依存ライブラリのキャッシュはほぼ必須と言えるでしょう。 node_modules をキャッシュすることで大幅な時間削減が可能です。 # .nx/workflows/agents.yml launch-templates : custom-linux-medium-js : (中略) - name : Restore NPM Cache uses : 'nrwl/nx-cloud-workflows/v4/workflow-steps/cache/main.yaml' inputs : key : '.tool-versions' paths : ~/.npm base_branch : '<デフォルトブランチ名>' - name : Restore Node Modules Cache uses : 'nrwl/nx-cloud-workflows/v4/workflow-steps/cache/main.yaml' inputs : key : '.tool-versions|package-lock.json|yarn.lock|pnpm-lock.yaml' paths : node_modules base_branch : '<デフォルトブランチ名>' - name : Restore Browser Binary Cache uses : 'nrwl/nx-cloud-workflows/v4/workflow-steps/cache/main.yaml' inputs : key : '.tool-versions|package-lock.json|yarn.lock|pnpm-lock.yaml|"browsers"' paths : | ~/.cache/ms-playwright base_branch : '<デフォルトブランチ名>' ここで示した例では、以前の記事と同様に ~/.npm をキャッシュすることで、 node_modules がキャッシュヒットしなかった場合でも可能な限り高速動作するようにしています。 tech.findy.co.jp workflow-steps/cache の使い勝手は actions/cache とほぼ同じです。デフォルトブランチ以外にキャッシュを共有する場合は、デフォルトブランチへのpush時にキャッシュを更新すると良いと思います。 # .github/workflows/update-cache.yml on : push : branches : - '<デフォルトブランチ名>' paths : - package-lock.json jobs : cache : runs-on : ubuntu-latest steps : (中略) - uses : actions/cache@v4 id : cache (中略) - run : npx nx-cloud start-ci-run --distribute-on="3 linux-small-js" - name : Install dependencies if : steps.cache.outputs.cache-hit != 'true' run : npm ci - run : npx nx-cloud complete-ci-run 設定可能な最小エージェント数は 3 である点に注意しましょう。 安く済ませたいところではありますが... 特定のステップの省略 2024年10月現在、Nx AgentsではGitHub Actionsのような条件分岐の構文はサポートされていません。適宜スクリプトを書いて対応しましょう。 # .nx/workflows/agents.yml launch-templates : custom-linux-medium-js : (中略) - name : Install Node Modules (if needed) script : | if [ ! -d node_modules ] ; then npm ci fi 高度なエージェント割り当て --distribute-on=".nx/workflows/dynamic-changesets.yml" は記述が簡潔である一方、現状では small medium large の3段階しか設定できません。また、負荷に応じてマシンスペックを上げるといった高度な割り当てもサポートされていません。 nx.dev Nx Agentsの今後のアップデートに期待しても良いですが、待ちきれないという場合は次のように、メインジョブの前段で nx show projects --affected を実行し、 outputs 経由でオプションを渡すと良いでしょう。 jobs : check : runs-on : ubuntu-latest outputs : distribute_on : ${{ steps.output.outputs.distribute_on }} parallel : ${{ steps.output.outputs.parallel }} steps : (中略) - uses : nrwl/nx-set-shas@v4 with : main-branch-name : ${{ github.base_ref }} - name : Get affected projects id : get_affected_projects run : | length=$(npx nx show projects --affected --json | jq '. | length' ) echo "length=$length" >> $GITHUB_OUTPUT - name : Output id : output run : | if [ $ {{ steps.get_affected_projects.outputs.length }} -gt 20 ] ; then echo 'distribute_on="4 custom-linux-large-js"' >> $GITHUB_OUTPUT echo 'parallel=4' >> $GITHUB_OUTPUT elif [ $ {{ steps.get_affected_projects.outputs.length }} -gt 10 ] ; then echo 'distribute_on="3 custom-linux-medium-plus-js"' >> $GITHUB_OUTPUT echo 'parallel=3' >> $GITHUB_OUTPUT else echo 'distribute_on="3 custom-linux-medium-js"' >> $GITHUB_OUTPUT echo 'parallel=2' >> $GITHUB_OUTPUT fi main : needs : check runs-on : ubuntu-latest steps : (中略) - name : Setup Nx Cloud run : npx nx-cloud start-ci-run --distribute-on=${{ needs.check.outputs.distribute_on }} - run : npx nx affected --target=build,test,lint -parallel=${{ needs.check.outputs.parallel }} この手法は dynamic-changesets.yml による判定と nx affected で検出された影響範囲に乖離がある場合にも有効です。 まとめ この記事では、Nx Agentsの導入によりフロントエンドのCIを高速化した事例を紹介しました。 Nx Agentsの提供するDTEの仕組みは、タスク単位の並列化を超えた高速化が可能です。 一方で、プロジェクトの細分化が十分でない場合や、単一のタスクが非常に時間のかかる場合では期待する効果が得られないため、適材適所で利用するのが良いでしょう。 国内のNx Agents導入事例はまだ少ないと思われますが、検証中に得られた知見は今後も発信していきますので、是非お役立てください。私たちの取り組みが少しでも皆様の助けとなれば幸いです。 現在、ファインディでは一緒に働くメンバーを募集中です。 ご興味がある方は↓こちらからご応募ください。 herp.careers
アバター
【エンジニアの日常】エンジニア達の人生を変えた一冊 Part1 では大変ご好評をいただきました。 今回はPart2としまして、弊社エンジニアの人生を変えた一冊をご紹介いたします。 ぜひ、読書の秋のお供としてご参考にしていただければ幸いです! 人生を変えた一冊 SRE サイトリライアビリティエンジニアリング―Googleの信頼性を支えるエンジニアリングチーム プログラマが知るべき97のこと この本を読んだきっかけ Clint Shankさんのエッセイ「学び続ける姿勢」 Karianne Bergさんのエッセイ「コードを読む」 この本から学んだこと Clean Coder プロフェッショナルプログラマへの道 まとめ 人生を変えた一冊 SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム オライリージャパン Amazon こんにちは。ファインディでSREを担当している大矢です。ファインディ一人目のSREとして入社し、SRE歴は8年目になります。 「Site Reliability Engineering -- How Google Runs Production System」の日本語訳は、多くのエンジニアに愛読されている名著です。 GoogleのSREチームの取り組みも書かれており、これからSREを目指す方はもちろん、既にSREとしてご活躍されている方も、まだ本書を読まれていないようでしたら是非一度手に取って頂きたい一冊です。 私はかつて2010年代前半から中盤まで大規模なシステムでインフラエンジニアとして働いていましたが、実際はSREに近い業務を担っていました。 同システムでは開発メンバーとのコミュニケーションは活発でモニタリングやCI/CDも導入していましたが、運用面に対して次のようなプレッシャーを感じていました。 システム変更に対する失敗は許容されない 手作業による環境構築や運用が多い 24/7でのオンコール担当 これらのプレッシャーに対する答えの1つが「SRE Book」でした。 この本ではSLIやSLO、エラーバジェット、ポストモーテム、トイルといったSREにとって重要な概念や、オンコール体制の考え方が解説されています。 例えば前述のプレッシャーに対しては次のようなアプローチをとることができます。 SLI/SLOを定めエラーバジェットに基づいた運用をおこないポストモーテムを活かす トイルを減らすため自動化を進める オンコール体制は適切な人数で適切な持ち回りを行う 上記は当たり前のことかもしれませんが、私自身の考えを整理するうえで強力な後押しをしてくれました。 ところで、ファインディではプロダクト毎にSLI/SLOを設定しエラーバジェットに基づいた振り返りを推進しています。 弊社SREチームは立ち上げから日が浅いですが、これからもこの一冊で得た知識を活かし、ファインディのサービスをより信頼性の高いものにしていきたいと考えています。 プログラマが知るべき97のこと プログラマが知るべき97のこと オライリージャパン Amazon 開発推進チームで、RubyとRustとTypeScriptを書いているバックエンドエンジニアの 西村 です。エンジニア歴は4年目です。 この本はエンジニアとして仕事していく中で素晴らしい知見を共有してくれるエッセイ集です。 エッセイ集であるため、タイトルが気になるエッセイから読んでいく形でも問題ないです。 どのエッセイも経験豊かなエンジニア達が執筆しています。例えば、システム設計に熟練したエンジニアやプログラミング言語のコミッタが執筆している豪華な一冊になっています。 エッセイのテーマは、リファクタリング、設計原則、エンジニアとして仕事をしていく姿勢、テスト等の様々なものがあります。 この本を読んだきっかけ 私がエンジニアとして働いて半年ほど経った頃、自分の理想の姿へ近づくために、どうすればいいのか分からなくなった時期でもありました。 そんな中、毎週通っていた ジュンク堂書店池袋本店 でたまたま「プログラマが知るべき」という文字が目に入って、この本を読み始めました。 次のエッセイを読んで、自分がどうすればいいのかを思いつくきっかけになりました。 当時の私を助けてくれたエッセイは次のものです。 Clint Shankさんのエッセイ「学び続ける姿勢」 このエッセイでは、自分自身で学び続けるために、書籍やインターネットを利用した学習やレベルの高い人と仕事をすることなどが推奨されています。 また、技術の進化に対応できるように、常に学び続ける重要性についても書かれています。 このエッセイを読んで、当時は次のことを毎日欠かさずやっていました。 仕事でわからなかったライブラリのメソッドや仕組みをその日のうちにライブラリのドキュメントを読む ライブラリの挙動を理解するためにローカルPCでコードを動かす このエッセイにある「普段利用しているライブラリについての知識を深める」という記述を参考にしていました。 現在、数ヶ月前から新規プロダクトでRustの検証をしています。そんな中、Rustのことをもっと知りたいと思い、 Osaki.rs というコミュニティを立ち上げて、勉強会を開催しています。 この勉強を立ち上げた背景のひとつに、このエッセイにある「勉強会を自ら立ち上げる」という記述を参考にしています。 このOsaki.rsは、connpassのメンバー数が40人ほどになりました。これからもRustの勉強会を続けていきます。 Karianne Bergさんのエッセイ「コードを読む」 このエッセイでは、エンジニアが他のエンジニアや自分の過去のコードを読むことの重要性について述べられています。他のエンジニアが書いた読みやすいコードを読むことで、自身の成長につながると強調されています。 また、過去に自分が書いたコードを読み返すことで、スキルの向上を確認でき、さらなるやる気が湧くとも述べられています。 このエッセイを読んで、当時は次のことを毎日欠かさずやっていました。 自分が関わるプロダクトのリポジトリの全てのプルリクエストに目を通す チームメンバーが作成したプルリクエストを読むことで、チームメンバーがどのようなコードを書いているのかを知ることができ、自分のスキルアップにつながると考えていました。 現在、Rustらしい書き方と読みやすいコードの書き方が分からない時、スター数が多いライブラリのソースコードを読んで、Rustらしい書き方を日々学んでいます。 この本から学んだこと 上記のエッセイを読んで、当時悩んでいた自分がどうすればいいのかを思いつくきっかけになりました。 ぐずぐず悩んでなにも動かないより、少しだけでもいいから知らなかったことを潰していく・挑戦していくことの大切さを学べたと思います。 キャリアやこれから何を学ぶ?と悩んだときは、今でもこの本を読み直します。 また、定期的に書店へ行って、素晴らしい書籍を探しています。 Clean Coder プロフェッショナルプログラマへの道 Clean Coder プロフェッショナルプログラマへの道 作者: Robert C.Martin KADOKAWA Amazon 2024年4月にファインディへ入社して、 Findy Tools の開発をしている林です。 私からは Clean Coderプロフェッショナルプログラマへの道 という本を紹介します。 私はエンジニアとして働き始めて2年目に本書を読みましたが、自身が業務の中で感じていた課題とその解決策のヒントの多くを得られました。 そして、この本の考え方が日々の業務に大きな影響を与えてくれたと考えています。 本書は、タイトルの通り「プロフェッショナルプログラマ」になるための技術的なスキルだけでなく、コミュニケーション力やエンジニアリングに対する向き合い方などのソフトスキルに焦点を当てて紹介されています。 ここでいう「プロフェッショナル」とは、「責任を取る」姿勢を持つことと定義されています。これは、エンジニアが求められた仕様を実現するためのコードを書くだけでなく、最終的にビジネスの成果を達成する責任を果たすことを意味します。 本書では、エンジニアが直面しやすい具体的な課題とその解決策が解説されています。例えば: プロダクトにバグが生じる テスト駆動開発(TDD)や、効果的なテスト戦略の構築が重要。 無理な要求や納期に間に合わない場合 明確に「ノー」と言い、仕様を交渉することがプロの姿勢。 精度の高い見積もりができない PERTやプランニングポーカーなどで具体的な数値を挙げ、現実的な見積もりをする。 チームワークの問題 プログラミングは一人で完結するものではなく、他者との円滑なコミュニケーションが不可欠。 特に印象に残ったのは、本書の第2章にある無理な要求に対して「ノー」と言うことの重要性です。 私は本書を読む前、プロフェッショナルなエンジニアとは、どんな要求にも対応する技術力を持つべきだと考えていました。 しかし、本書を読んで、「できないことは断る」ということもプロフェッショナルの責務であることに気づかされました。 さらに、「試しにやってみる」という曖昧な言葉も問題視されています。 このフレーズは、調査の時間を指しているのか、あるいは実装を試すことを意味しているのかが不明瞭で、誤解を招きかねません。 私自身、何気なく使っていた言葉なのでより明確に何が問題で何をするのかを具体的に言うようにしました。 本書の内容は一朝一夕で身につくものではありませんが、私は業務で困ったときに何度も読み返しています。 特に、最近エンジニアになった方や「プロフェッショナルとは何か?」と漠然としている方にとって、強い指針となる一冊です。ぜひ手に取ってみてください。 まとめ いかがでしたでしょうか? ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
アバター
こんにちは。こんばんは。 開発生産性の可視化・分析をサポートする Findy Team+ 開発のフロントエンド リードをしている @shoota です。 先日、 END が 【フルスタックエンジニアへの道!】React と TypeScript の修行をした話 というタイトルで、フルスタックエンジニアを目指すためのフロントエンドの修行の記事を投稿いたしました。 こちらの記事では React / TypeScript において個人学習程度のレベルにあった END が、機能開発を自走可能になるまでの内容が書かれています。 そこで本記事では、END の成長と挑戦をサポートし、実際に指導にあたった私がメンター視点での話をいたします。 育成のはじまり 下準備 ゴール設定 助走をしてもらう 実践 育成の方針と実践 トレードオフ 3 ヶ月の成果と分析 プルリクエストの可視化 メンティーの分析 メンターの分析 所感とまとめ 成長したいエンジニアを探しています!! 育成のはじまり Findy Team+の開発メンバーはバックエンド・フロントエンドにそれぞれ主軸を置きつつ、多くのメンバーがその垣根を超えた貢献ができることを目指しています。 そのなかでバックエンドを主軸としていた END がフロントエンド領域に集中して挑戦したいという話が持ち上がりました。 フロントエンドメンバーの比率が少なく、「片手間ではなく集中して挑戦する」という決意もあり、自分としても前向きに指導担当をさせてもらいました。 下準備 ゴール設定 急に育成をするとなっても何となく始めてしまうとちゃんと成長できているのか、育成をサポートできているのかどうかがわからなくなります。 まずはじめに私がしたのは、エンジニアリングマネージャー(EM) とのすり合わせでした。 具体的には次のような質問から「どのくらいの時間でどこまでできるようになるのか?」をある程度の幅で決めていきました。 メンティーが目指すゴールはどのレベルか? EM の期待するゴールはどのレベルか? どれくらいの育成期間をとるか? ゴールのレベルもざっくりと 3 段階を想定しました。 基本はバックエンドを主軸としつつも、状況にあわせてフロントエンドも書けるようになりたい バックエンド・フロントエンドに分け隔てなく両輪で活動できるようになりたい フロントエンド主軸に移りたい 結果 2 番目のレベルをイメージしながら、 「フルスタックな動きをキャリアの目標として想定しつつ、まずはフロントエンドを自走できるレベル」 を 「とりあえず 3 ヶ月」 で目指してみようとなりました。 助走をしてもらう もともと個人で React は書いたことがあるといっても、Team+の膨大なコードの前に立つとなかなかどこから手を付けたらよいかと不安になるものです。 なのでまずはメンティーの心の準備体操として、フロントエンドの考え方をまとめた読み物を書きました。 読み物 MVC アーキテクチャやオブジェクト指向といった馴染みの概念と照らしながら、どんなパラダイム・シフトが待っているのかをブログのように書いておきました。 このほか、これまでにもフロントエンドの設計や React の思想などに関する社内記事を書いていたので、それらも合わせて共有しておきました。 実践 育成の方針と実践 自分で調べたり考えたりしたことがいちばん身につくので、基本的には寄り添い過ぎず、私のなかでいくつかのゲートを設けて見守ることを計画しました。 私はこれまでもジュニア層のフロントエンドエンジニアの育成経験があったので、これらのゲートが多くの人がぶつかる壁であることを知っており、これをクリアできてそうかをみながらサポートの強弱をつけていく算段です。 成長ステップ React に Props を渡して単純な HTML を出力するコンポーネントを書ける コンポーネントを組み合わせた大きなコンポーネントを型を含めて書ける ユーザーインタラクションのハンドラーを純粋な関数として書ける バックエンド API との通信インターフェースを理解し、コード生成ができる データとコンポーネント間を TypeScript をつかって型安全かつ整合してつなぐことができる 多くの場合に Step. 3 と Step. 5 がよく詰まるポイントで、ここを乗り切るための知識や理解のための伏線を張っていくように、ドキュメントなどをプルリクエストや Slack でどんどん渡していきました。 特に Step. 5 がジュニア層の鬼門で、それまでの React Component の積み上げと API の型定義を同時に整理しなければならないため、データとビジュアルの責務分離、そのための関数と型の設計などで苦戦します。 渡した資料の中で、誤読しやすいものや前提知識が多く必要なものは口頭で補足するなどしながら、これまでの理解度を測って次のステップに必要なものを選んでいくようにしました。 こうして Step. 5 を乗り越えるための理解が積み上がるように進路補正をしていきました。 トレードオフ このように挑戦するメンティー側にはいろいろな壁を超えるための頑張りが必要になってきますが、一方でサポートするメンター側も、自分のパフォーマンスを維持し続けるのが難しくなってきます。 どれくらいの厚みのサポートが必要かは個々人によって違いますが、少なくともメンターの 20%くらいはサポートに力を注ぎたいものです。 Findy では Findy Team+ を使って自分のパフォーマンスを計測する習慣がありますが、私は 育成に際してこの数値がある程度は落ち込むことを覚悟 の上で、その落ち込み幅をできるだけ小さくすることにも挑戦していました。 私のマネージャーにもこの想定の落ち込み幅のすり合わせをし、育成と自己パフォーマンスのバランスをとっていることを 1on1 などで話しています。 パフォーマンスを可視化することで数値の変動にきちんと理由付けをできるのが Findy Team+ を使っていく最大の利点です。 3 ヶ月の成果と分析 プルリクエストの可視化 実際には想定よりも成長速度が安定していたので最初の 2 ヶ月弱で設定したゴールに到達しそうでしたが、とりあえずはそのまま 3 ヶ月間みっちりと進めてみました。 難易度が高すぎないタスクを中心に実施してもらってはいたものの、最終的にフロントエンドへの プルリクエスト数は私とほぼ同等かそれ以上 が出せるようになっていました。 Team+のグラフで可視化してみると次のようになりました。 青が私、オレンジがEND メンティーの分析 先ほどのメンター(私)とメンティー(END)の両方の PR 作成数でした。これをメンティー(END)だけに絞ると、中盤に数値の谷があります。 実はこれが狙い通りで、前述の想定した Step と期間を照らし合わせることで成長の過程が見えてくるようになります。 育成方針とパフォーマンスの谷 はじめは単純な HTML 合成のための Component 作成(Step. 1, 2)で伸びていく数値が、ユーザーインタラクションの実装 (Step. 3) が始まると徐々に鈍化していきます。 インタラクションの実装に慣れてくるとバックエンド API との連動 (Step. 4) のイメージもつきやすきなってきますが、いざこれらをつなごうとすると考慮することが一気に増えるため、数値が下がっています。 そしてこの鬼門を乗り越えると、またぐんぐんと順調に伸びていく事がわかります。 メンターの分析 次に自分がメンターをしている期間にどれくらいのパフォーマンス影響があったかを見てみます。 同じ期間での私の PR 作成数をグラフにすると、何ともつまらないくらいに変化が少ないグラフになりました。 メンターのPR作成数 しかしこれには秘密があります。 まずメンターを始めてからメンティーのプルリクエストはすべてレビューしており、すぐに自分のパフォーマンスに対する影響を感じていました。 具体的には次のようなことを、プルリクエストがあがるごとに都度実施していたためです。 設計意図を汲み取りにくい構造になっている部分を読み解く 厳密でない型定義によって型チェックをすり抜けている箇所がないかチェックする 指摘内容とともに実装サンプルを書いたり、その理由を示すリファレンスを探して提示する。 そこでマネージャーと相談して、自身のプルリクエスト作成数が 4 以下にならないように水準を維持していくことを宣言し、レビューの濃度を調整していったのです。 (それまでの私の平均プルリクエスト作成数は 5.6〜5.8 程度でした) 「そこまで狙い通りにプルリクエスト数を調整できるものか?」と思われるかもしれません。 しかし自分のパフォーマンスを落としては開発が進まず、また一方でメンバーの成長にも投資しなければ全体の生産性が高まりません。この 実績と投資のバランスを自分の数値を軸にする ことで全体最適を図っていったのです。 もっと自分がプルリクエストを書ける状況でもその分は育成への投資に使いました。こうしてマネージャーと合意したパフォーマンスを維持しながら、最大限の育成を進めるのは意外と難しくありません。 所感とまとめ 今回の育成期間の全体を通して、おおむね計画どおりに進めることができました。もちろんメンティーのポテンシャルの高さやメンターの経験もあってのことですが、次のような点が成功への道しるべになったと思います。 メンティーのゴール設定を 技術リードとエンジニアリングマネージャーの両者 で合意してから開始したこと 適切なステップとぶつかる壁を用意したこと 成長のための「適度なストレス」を感じ続けられるようにすることで、モチベーションを維持 最も大きな谷を超えるための準備としてサポートを意識した メンターのパフォーマンスへの影響とその保証ラインも適切に設定し、 そのパフォーマンスラインを指標として サポートの厚みを可変できるようにしたこと これらによって、開始時に設定したゴールを超えて爆速で成長してもらえました。 卒業の様子 最後に、「メンティーの分析」であげた END のプルリクエスト作成数グラフをもう一度みてみると、非常にゆるやかなダニングクルーガー曲線になっており、非常に興味深いなと思いました。 成長したいエンジニアを探しています!! バックエンド主軸のあなたでもフロントエンド開発を自走できるレベルまで成長することが出来ます。 もちろんフロントエンドが大好きなあなたも、リードエンジニアを目指してスキルアップできることでしょう! ぜひ、ご応募お待ちしております (∩´∀ `)∩ herp.careers
アバター
こんにちは。 Findy で Tech Lead をやらせてもらってる戸田です。 既に皆さんも御存知かと思いますが、弊社では開発生産性の向上に対して非常に力を入れています。 以前公開した↓の記事で、弊社の高い開発生産性を支えている取り組み、技術についてお話させていただきました。 tech.findy.co.jp ありがたいことに、この記事を多くの方に読んでいただき反響をいただいております。 そこで今回は、↑の記事でも紹介されている「Pull requestの粒度」について更に深堀りしてお話しようと思います。 Pull requestの粒度は、弊社にJOINしたら最初に必ず覚えてもらう最重要テクニックの1つです。 それでは見ていきましょう! 大きなPull request 適切な粒度とは 適切な粒度を維持するために タスク分解 迷ったら小さく レビューを最優先にする CI高速化 feature flag運用 topic branch運用 まとめ 大きなPull request Pull requestのレビュー依頼が飛んできて確認したら、大きなPull requestでブラウザをそっと閉じた経験がある方は少なくないと思います。 大きなPull requestにおけるデメリットは数多く存在します。 パッと思いつくデメリットを列挙してみます。心当たりがある読者の方もいるのではないでしょうか? topic branchの生存期間が長くなり、結果的にbase branchとの差分が大きくなりconflictの発生確率が高くなる 変更内容が多岐に渡るため、問題発生時の原因特定に時間が掛かってしまう revert時に余計な内容までrevertされてしまう レビューの質が落ちる レビュワーが見るべき範囲が広くなるため、認知負荷が大きくなる あとで見とこ、、、となりがち 結果的にtopic branchの生存期間が長くなる base branchとの差分が大きくなる conflictの発生確率が高くなる 指摘内容が増え、結果的に手戻りが増える etc このようにデメリットは数多く存在します。システム開発において大きなPull requestの存在は、円滑な開発を阻害する要因となるでしょう。 では「大きなPull request」とは具体的にどんなPull requestのことを指しているのでしょうか? 適切な粒度とは ここでサイズと粒度の違いを説明する必要が出てきます。 そのPull requestが1つのことに注力できているかどうかが、粒度を語る上で非常に重要なポイントになります。 Pull requestのサイズとは、コードの変更行数や変更ファイル数のことを指します。 しかし、コードの行数や変更ファイル数が多かったとしても、変更内容が一意であれば問題ないはずです。 例えば、関数名を変更して一括置換した場合を例に挙げましょう。変更ファイル数は多くなりますが、変更内容は一意です。 こういったケースの場合、Pull requestの概要欄に一括置換した旨を記載しておけば、変更内容を全て確認せずとも変更後の関数名をレビューし、CIが通ればマージ出来るはずです。 一方、Pull requestの粒度とは、コードの変更内容のことを指します。 例えば、変更ファイル数が少ない一方、データ取得・データ加工・描画処理を同じPull requestで対応した場合を例に挙げて考えてみましょう。 この場合、それぞれの処理は全く異なる内容です。しかし同じPull request内で対応してしまったため、変更内容全てを一度にレビューする必要があり、レビュワーに対する認知負荷が大きくなってしまいます。 極論ですが、たとえ変更行数が1万行を超えていたとしても、変更内容が一意であれば問題は無いと考えています。 逆に変更行数が20行程度の不具合修正の中に「ついでにリファクタ」した内容が含まれていた場合、粒度が大きいと判断しPull requestを分割すべきなのです。 なぜならば、もし不具合修正に失敗していてrevertしようとした際に、ついでにリファクタした内容もrevertされてしまうからです。こういったケースの場合、リファクタをしたPull requestを別で作成します。 つまり本質はコードの変更行数や変更ファイル数ではなく、変更内容そのものにあるということを理解できたかと思います。 粒度が適切であれば、1行だけの修正でも、1万行の修正でも問題ありません。 10のプルリクを1回レビューするよりも、1のプルリクを10回レビューする方が、作成者、レビュワー共に負担が少ないのです。 適切な粒度を維持するために タスク分解 Pull requestの粒度について完全に理解したので、これからは適切な粒度でPull requestを作るぞ!と思い立っても、すぐに実現させるのは難しいものです。 理解はしているけど、やってみるとやることがどんどん増えていき、結果的にPull requestが肥大化しがちです。 それを解決するのがタスク分解です。概要は↓のページを参照してください。 tech.findy.co.jp タスク分解に関しても、別の機会で詳細にお話できればと思います。 迷ったら小さく とは言え、最初のうちは粒度に対して悩むことが出てくると思います。 もっと作り込んでからレビュー依頼を出す?それとも今の段階で出す?でも修正内容が小さすぎないか?などといった葛藤は自分にも経験があります。 そういう時は、より小さい粒度の段階でレビュー依頼を出すことをオススメします。レビューのやり取りの中で粒度に対する議論を行い、そこで認識を合わせればOKです。 Pull requestを大きく作って後から分解するより、小さく作って後から修正内容を追加する方が圧倒的に楽だからです。 まずは小さすぎても良いので小さく作り、そこから肉付けしていくようなイメージでPull requestを作成していくと良いでしょう。 レビューを最優先にする Pull requestの粒度が小さくなった場合、レビュー依頼に対して最優先で取り組む必要があります。 なぜならば、マージしないと次の修正に着手出来ない場合に、レビューがボトルネックになり結果的に開発スピードが遅くなってしまうからです。 自分の作業を一時中断して、10分程度レビューして自分の作業に戻るとコンテキストスイッチを戻すのが非常に難しいと思います。 でも心配しなくてよいです。Pull requestの粒度が適切であれば、レビュワーが確認する内容も小さくなるためレビューに掛かる時間が短縮されます。 そのため、自分の作業を一時中断することになったとしても、すぐまた自分の作業に戻ることができるようになります。 弊社では1つのPull requestのレビューに掛ける時間はほんの数分程度となっています。1分以内で終わることもあります。 粒度が適切なPull requestが当たり前となれば、レビューを最優先にする習慣、文化が組織に根付くはずです。 CI高速化 粒度が小さくなってくると、当然ながら作成されるPull requestの数が増えます。つまりCIの実行回数も比例して増えていきます。 CIの実行回数が増えた状態で実行速度が遅いと、逆に開発効率が落ちてしまいます。CIが遅いからPull requestの粒度も大きくなってしまうといったケースも見たことがあります。こうなってしまうと本末転倒です。 そこでCIの高速化も同時に進めた方が良いでしょう。詳細はこの記事では割愛しますが、↓の別記事を参考にしてみてください。 tech.findy.co.jp feature flag運用 Pull requestの粒度は適切にしたいが、本番環境には影響を出したくない。というパターンはfeature flagを使うと良いでしょう。 ローカル環境でのみ実行されるようなコードにしておいて、その状態を維持しつつbase branchにマージし続けます。本番公開OKのタイミングでfeature flagを解除し、本番環境に反映します。 feature flagの運用方法はSaaSを使う、ライブラリを使うなどの方法がありますが、今回は一番カンタンに導入できる方法の、コード上にフラグを埋め込んで切り替える手法を紹介します。 実際にコードの例を見てみましょう。 export const SampleComponent = () => { const isEnabledNewLabel = process .env.FEATURE_NEW_LABEL === 'true' ; return ( < div > { isEnabledNewLabel && < span > NEW </ span > } < span >Sample</ span > </ div > ); } ; 環境変数に埋め込まれている FEATURE_NEW_LABEL の値によって、 NEW の表示を切り替える単純なcomponentです。 この仕組みにより、ローカル環境の環境変数をtrue、本番環境の環境変数をfalseにすることで、本番環境に影響を与えずに新機能の開発を進めることが可能になります。 開発が完了して本番環境に反映する際には、本番環境の環境変数をtrueにすることで新機能を公開できます。何かしらの問題が発生した場合、本番環境の環境変数をfalseに戻すことで、新機能を非公開に変更可能です。 本番環境で問題が起きなければ、環境変数のフラグとコード上の分岐を削除します。 この手法はfeature flagの管理が煩雑になったり、コード内の分岐やテストケースが増えるといったデメリットもありますが、Pull requestの粒度を適切に維持するためには非常に有効な手段です。 本番環境に影響を出さずに、Pull requestの粒度を適切に維持し続け、スピード感を持って開発する手段の1つとして活用してみてください。 topic branch運用 様々な事情によってfeature flagを使えない、使いたくない場合の手段として、topic branch運用を紹介します。 base branchからtopic branchを切って、そこから更にdevelop branchを切ります。develop branchからtopic branchへのPull requestを作成し、そこで粒度を維持しつつレビューをします。 本番反映OKのタイミングでtopic branchからbase branchへのPull requestを作成し、マージします。このタイミングでのレビューは必要最低限でOKです。なぜなら、develop branchからtopic branchへのPull requestでレビューしているからです。 この手法により、base branchにはリリースのタイミングまで変更内容が反映されません。そのため本番環境への影響を与えず、Pull requestの粒度を適切に維持し続けることが可能になります。 定期的にbase branchからtopic branchへ変更をmergeしておくのがポイントです。topic branchの生存期間が長くなるので、base branchとの差分が大きくなりconflictが発生しやすくなるからです。最低でも1日1回はbase branchの修正内容を取り込んでおくと良いでしょう。 まとめ いかがでしたでしょうか? 粒度を適切に維持することで、レビュワー、レビュイーの両者に対して優しいPull requestを作成し続けることができます。 弊社では最重要テクニックの1つとしていますが、比較的カンタンに覚えることができ、コツさえ掴めば誰でも実践できる内容です。 是非、皆さんも試してみてください。 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers
アバター
こんにちは、ファインディでFindy Team+(以下Team+)を開発しているEND( @aiandrox )です。 普段はバックエンドの開発をメインで担当しているのですが、3ヶ月間フロントエンドの開発に挑戦する機会がありました。短い期間でしたが、フロントエンドテックリードから直接指導してもらいながら実装をすることで、フロントエンドの開発を一人でできるくらいに慣れることができました。 今回は、その経験と学びについて書いていきます。 フロントエンドに挑戦する前の自分について フロントエンドに挑戦することになった経緯 フロントエンドを学ぶ上で助けられたこと フロントエンドのノウハウが溜まった記事の充実 開発ツールが揃っている テックリードとマンツーマンでタスクをやっていく react.devの輪読会 つまづいた点 タスク粒度を適切に分割すること Team+のフロントエンドの責務の考え方 TypeScriptで慣れる必要があったこと 3ヶ月の挑戦の成果 自身の伸びしろ おわりに フロントエンドに挑戦する前の自分について もともとはバックエンド(Ruby on Rails)の開発をメインでしていました。 前職では特にバックエンドとフロントエンドが分かれておらず、Rails View / CoffeeScript / FlowType / jQuery / Reactの混在したフロントエンドを触っていました。また、個人開発でReact / TypeScriptを触ったことはあったものの、あくまでも動くことを重視していたため、アーキテクチャの意識はあまりしていませんでした。 Team+のフロントエンドでは、前年に修正プルリクをいくつか出したので、アーキテクチャはなんとなく把握していました。しかし、既存の実装を踏襲したまま一部を修正する程度だったので、新規でComponentを作ったことはありませんでした。 フロントエンドに挑戦することになった経緯 もともと個人開発などもしていたので、フロントエンドへの興味もありました。将来的にはフルスタックエンジニアとしてスキルを広げたいという思いもありましたが、まずはバックエンドの技術を身に付けることを優先していました。そんな中、「フロントエンドの手が足りないんだけどやってみない?」という提案がありました。 いい機会!ということで、「やりたいです〜」「テックリードと一緒にやってもらおうと思ってます」「やったー」とトントン拍子で進んでいきました。 フロントエンドを学ぶ上で助けられたこと フロントエンドに挑戦するにあたり、以下のサポートがあることにより、スムーズに実装を進めることができました。 フロントエンドのノウハウが溜まった記事の充実 社内記事に「フロントエンドを書くときに何を考えているか、どうしているか」というものがあり、これを参考にできました。「データ層を扱うものはデータ層の責務に閉じ込め、プレゼンテーション層は見た目に注力する。つまり、Pureな Presentational Componentは原則としてロジックを持ってはならない」といった基本的な考え方と、実際に画面を作るときの作業工程を書いていたので、とても役立ちました。 基準の考え方があったので、迷ったときにこれに立ち返ったり、「この場合はどうなのか?」と具体的に質問することができました。 開発ツールが揃っている 開発中はJavaScriptの統合テストツールWallaby.jsを使用していたので、デバッグがスムーズに進み、開発作業が非常に効率的になりました。 tech.findy.co.jp また、CIによって一定の品質が自動的に担保されるので、レビュー前に自分で気付けることも多かったです。ESLintは実装に寄ってくれている感じがあり、hooksの依存などを自動で補完してくれるため初学者としては安心できました。 テックリードとマンツーマンでタスクをやっていく テックリードとは同じチームなので、朝会で進捗の共有をするとき悩みポイントを共有したりしていました。また、実装方針に悩んだときは分報チャンネルに投げて拾ってもらったり、都度ペアプロなどでサポートしてもらいました。 また、自分が作成したプルリクに関してはすべて見てもらっていたので、私の理解度を把握されていました。そのため、プルリクの実装についての指摘や、理解度が浅そうなところなどがあったタイミングで呼び出していただきました。都度口頭で説明されたり自分からも「この理解で合ってます?」と壁打ちすることで、具体的な話から理解度を高めることができました。 ※「体育館裏」は一見怖そうに見えるかもしれませんが、怒られたりすることはありません。コワクナイヨ react.devの輪読会 もともと、フロントエンドをメインとするメンバーでReact公式の Learn React の輪読会を行っていたので、自分も途中から参加するようになりました。 書かれている文章だけだと言葉や概念が難しかったのですが、輪読会では具体的な解説があったりわからない点を質問できたりしたので、実践を理論で補強できました。これにより、プルリクで指摘された内容について「つまりそういうことなのか」と理解できるようになりました。 つまづいた点 タスク粒度を適切に分割すること バックエンドに関しては、技術的にもドメイン的にも慣れているので、1つのクラスごとにプルリクを作成することができていました。しかし、フロントエンドに関しては、ローカルの画面で動く状態まで実装すると大きすぎるプルリクになることがありました。 これを解消するために、1つずつ実装した箇所のテストを書くことで安心してプルリクを出すことができました。バックエンドでは当然のように考えていたことでしたが、フロントエンドではつい画面でのデバッグをしようとする心が働いてしまっていたことを自覚しました。 また、最初はComponentを分割して実装するという意味をあまり理解できておらず、中途半端な状態で1プルリクにしたりもしていました。 Team+のフロントエンドの責務の考え方 フロントエンドの設計は以下のようになっています。 コンポーネント名 カスタムフック名 扱うデータ Page Component Params Hook ブラウザURL Container Component Facade Hook API や ストレージ等 Presentational Component Presenter Hook フォーム Findy転職フロントエンドの開発生産性を向上させるためにやったこと - Findy Tech Blog この思想をなんとなく頭に入れていたものの、実際に実装している途中で、「この記述はFacadeで書くべきなのか、Presenterで書くべきなのか」と迷うことがありました。 例えば、GAトラッキングの記述を最初はFacadeに書いていたのですが、「GAの実行はUIのクリックアクションをトリガーとするので、Presenterに書くのが適切」といったフィードバックを受けました。propsで渡された関数をPresenterでwrapするといったやり方が、最初は選択肢になかったので、新鮮に感じました。 また、ContainerからContainerを呼んでいる場合もあり、どのようにコンポーネントを分割するべきか迷う場面もありました。画面内のAPIを基準にFacadeを分割し、それに応じてContainerを組み立てていくとわかりやすかったです。 TypeScriptで慣れる必要があったこと 今まで書いてきたRuby / Railsと比較して、TypeScriptには型があるというのが大きな違いですが、個人的にはその堅牢さの中でJavaScriptの関数などを使いこなすのが難しかったです。 例えば、以下のような点です。 null や undefined だけでなく、 0 もfalsyな値になる 配列に対して find を実行すると戻り値が undefined になりうるため、必ず値が存在する場合に find を使うと、型と実態が合わなくなってしまう 3ヶ月の挑戦の成果 この機会に、1つの新機能をテックリードと2人で分担して開発しました。私は、以下のような一覧画面と分析画面をそれぞれ2画面ずつ担当しました。 一覧画面 分析画面 また、安定してプルリクを作成することもできるようになりました。 この3ヶ月を通して、React全体やTeam+のフロントエンドでの設計思想を学び、コンポーネントの責務の分離や状態管理の方法について、実践を通して理解することができました。これにより、他のエンジニアへのレビューもある程度自信を持って行えるレベルになりました。 改めて感じたのは、ただ動けばいいのではなく、Reactで可読性・設計を考慮したコードを書くのは難しいということでした。記述方法の自由度が高い分、意図を設計として乗せる必要がある(テックリードからの受け売り)というのを実感しました。 自身の伸びしろ 今回の挑戦では表示するデータを取得するものばかりだったので、フォーム処理やデータの更新などに今後は積極的に挑戦したいです。 また、react.devの輪読会や共通コンポーネントを変更しようとしたときに、自分の実力不足を感じました。TypeScriptの知識不足や型の伝播を読み解けなかったり、既存の実装の変更対象がわかっても修正方法がわからない、概念はわかるが名称を知らない、などの伸びしろを見つけました。これに関しては、実際にやってみたり他の人のプルリクを参考に考えることを積み重ねていくことで、自身の糧にしていくつもりです。 ライブラリに関する理解もまだ浅いので、今後は開発にくわえて、フロントエンド環境のメンテナンスをできるような知識も増やしていきたいです。 おわりに このように、ファインディではフルスタックを目指す環境で働くことができます。また、現在ファインディでは共に働く仲間を募集中です。興味のある方は、こちらのページからぜひどうぞ! herp.careers
アバター
こんにちは。 2024/7/1 からファインディに入社した斎藤です。 ファインディでは、 Findy Team+ という、エンジニア組織の開発生産性を可視化し、開発チームやエンジニアリングメンバーのパフォーマンスを最大化するためのサービスの開発に携わっています。 今回は、私が入社初月からさくさくアウトプットできた理由についてご紹介します! 入社初月からプルリク1日4件出せました ファインディでは1日あたり4件プルリクを作成するというのを1つの指標としています。 入社直後は慣れるまでは結構厳しい指標だなと思っていたのですが、この記事で紹介する様々な仕組みのサポートもあり気がついたら初月から達成できていました。 中途入社あるあるだと思いますが、入社直後になかなか成果が出せなくてプレッシャーを感じてしまうということがあります。 簡単なタスク中心ではありますが、初月から貢献しているという実感が持てたので心理的にもたくさんプルリクが出せて良かったなと感じています。 ※1日4件という数値はあくまでアウトプット量の参考です。 闇雲にプルリクをたくさん出せばいいということではありません。アウトプットをプロダクトの成果につなげることが大切です。 入社1ヶ月目プルリク数 1日平均4.6件!! 入社2ヶ月目のプルリク数 1日平均5.7件!! 入社初月からさくさくアウトプットできた理由 Good First Issueの粒度が適切であったこと 新しく参画した人向けのIssueとしてGood First Issueを準備する文化がファインディにはありますが、粒度として難しすぎず簡単に終わりすぎず良い粒度で取り組めるタスクになっていました。 その中で複数箇所に同じ修正を加える作業があり、それをなるべく速く終わらせる意識で対応しました。 例:同じリファクタリングを複数ファイルに渡って対応するタスク ある処理を別ファイルに移動するというリファクタリング 対応すべきファイル名が列挙されていてわかりやすい スムーズな開発を支えるレビューとCI/CDがあること 入社前は開発生産性を可視化するプロダクトを開発しているくらいなので、さぞかし開発がしやすいんだろうなーと思っていましたが、 入社後はその想像を超えて開発がしやすい環境だなと感じました。 特に次のことが要因で開発をサクサク進めることができています。 レビューが速い!とにかく速い! 1PRあたりオープンからレビューまでの平均時間:3.3h プルリクの粒度を小さくして出す文化が根付いているためレビュー負荷が少ない リリース作業が楽で、時間が取られない リリースPR作成、e2eテストが自動化されていて基本的にマージボタンを押すだけで完了できる この辺りは他の方の 入社エントリー で詳しく紹介されているのでもしよかったら見てみてください。 メンターと密にコミュニケーション メンターとの1on1で不慣れな点を解消しつつ、やることを明確に設定していただけたことで開発に集中できました。 1on1で印象的だったのは一度に多くの情報をインプットしないようにしていただけたことです。 入社後に必要な情報を適切なタイミングで適切な量に分けてインプットしていただけたので、ストレスなく業務に慣れることができました。 適切な頻度(私の場合は週1回)でメンターとの1on1があり、そこで課題を解消できた 1on1以外でも都度メンターに質問して迅速に疑問点を解消でき、開発に集中できた ふりかえりをして日々改善が行われる 私のチームでは2週間に1回の頻度でFindy Team+の「KPTふりかえり」機能を使ってふりかえりを行っています。 そこで出た課題に対する打ち手を議論して改善に繋げる取り組みがとてもいいなと思いました。 ふりかえりで出たアクションはまずは試しにやってみましょうという感じですぐに実行されます。 アクションがすぐに実行されるというスピード感がとてもいいなと思いました。 そしてやってみたアクションについてどうだったかも合わせて次のふりかえりで確認しているので 継続的に改善が行われる仕組みになっています。 新しく参画した私が感じた課題はすでに改善のアクションが進んでいるという状態でした。 (なので課題が思いつかないという課題があります。。。) このように現状に満足せず、より良くするにはどうしたら良いかを全員で考え行動していることが高い開発生産性に繋がっているんだろうなと感じました。 挙げられたProblemに対してTryを設定 全てのコードベースを把握していなくても既存を壊さない仕組み またテストカバレッジが99%で、自分の改修で他へ影響があった場合にはちゃんとテストが落ちるようになっています。 このように、自分の開発に集中できる仕組みが整っているため、 最初から全てを把握していなくても、安全に開発を進めることができます。 なので、全くプロダクトの知識がない入社直後でも 自分の担当作業に集中できるので、サクサクプルリクを出すことができました。 不明点は遠慮せずにすぐに聞いたり相談できる 前述したメンターとの1on1もありますが、定例以外でも不明点はすぐにSlackのメッセージやハドルで聞くことが推奨されています。 オンボーディング資料にも「不明点は都度Slackやハドル、(出社している場合は)対面で確認しましょう!」と明記されており、みなさん快く応じてくれます! また、ファインディではSlackでtimes(分報)チャンネルを各自持っています。 自分のtimesで疑問をつぶやくといろんな人が助けにきてくれる文化があるので助かっています! 今後の抱負 ファインディのバリューであり、自分の強みでもある「スピード」を大切に爆速で開発をやっていきたいです! ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
アバター
ファインディ株式会社でフロントエンドのリードをしております 新福( @puku0x )です。 GitHub Actionsは、CI/CD以外にも様々な業務の効率化に役立ちます。 この記事では、弊社で実施しているGitHub Actionsを使った自動化について紹介します。 自動化 担当者アサイン ラベル設定 リリース QAチェック項目の抽出 定期実行 まとめ 自動化 担当者アサイン 開発フローの中では、Pull requestを作ってからレビューに出すまでにいくつかのタスクを行うことがあります。 弊社では、Pull requestの作成者がAssignee(担当者)となる場合が多いため、↓こちらのActionを用いてアサインの自動化をしています。 github.com - uses : kentaro-m/auto-assign-action@v2.0.0 with : repo-token : ${{ secrets.GITHUB_TOKEN }} addAssignees : author runOnDraft : true Assigneeのみ自動化しているのは、レビューに出すタイミングをPull requestの作成者側で制御したかったためです。 レビュアーについては、GitHubの標準機能でレビュー用のGitHubチームを作ってランダムアサインするようにしています。 ラベル設定 弊社では、Pull requestをラベルを用いて管理しているチームもあります。 付与するラベルについては、セマンティックバージョニングと相性の良いものが用いられることが多いです。 GitHub - azu/github-label-setup: 📦 Setup GitHub label without configuration. ラベル設定の自動化は、公式のActionがありますので比較的導入しやすいでしょう。 github.com actions/labelerは、最近の更新でブランチ名を基にラベルを設定する機能が追加されました。 この例では、ブランチ名の先頭が feat や fix 等であった場合に対応するラベルを付与するようにしています。 'Type: Feature' : - head-branch : [ '^feat' ] 'Type: Bug' : - head-branch : [ '^fix' , '^bug' ] モノレポの場合、ファイルのパスに対応するラベルを設定すると、どのプロジェクトに影響するか判断しやすくなります。 'Scope: App1' : - changed-files : - any-glob-to-any-file : - apps/app1/**/* - libs/app1/**/* 'Scope: App2' : - changed-files : - any-glob-to-any-file : - apps/app2/**/* - libs/app2/**/* actions/labelerは、Pull requestのタイトルを基にしたラベル設定には、2024年9月現在だと対応していないようです。 その場合は、手動でGitHubのAPIを呼ぶシェルを組む必要があります。 run : | pr_title=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Accept: application/vnd.github.v3+json" \ "https://api.github.com/repos/Findy/***/pulls/${{ github.event.pull_request.number }}" \ | jq -r '.title' ) if [[ $pr_title =~ ^feat ]] ; then pr_type='Feature' fi curl -X POST -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ -H "Accept: application/vnd.github.v3+json" -d '{"labels": ["Type: '"$pr_type"'"]}' \ "https://api.github.com/repos/Findy/***/issues/${{ github.event.pull_request.number }}/labels" いつか公式のActionにも欲しいですね! リリース 手順の多いリリース作業も、弊社では自動化を取り入れて負担を減らしています。 ここではフロントエンド系のリポジトリに採用されているものを紹介します。 バージョニング用ワークフローを実行 バージョニング用Pull requestが自動生成される バージョニング用Pull requestをマージ バージョニング用Pull requestのマージを起点にリリース用Pull requestが自動生成される (プロダクトによってはここでStaging環境へのデプロイも実行されます) リリース用Pull requestをレビュー後、マージ リリース用Pull requestのマージを起点に本番デプロイ 開発者は「ワークフローの起動」と「自動生成されるPull requestのマージ×2」というシンプルな手順で本番デプロイまでできるようになります。 また、 Conventional Commits に準拠したコミットメッセージを採用しており、自動バージョニングやリリースノートの自動生成といった恩恵も得られています。 デプロイ頻度はFour Keysの指標にも入っていることから、チームの健康状態を知る手掛かりになります。可能な限り高い頻度でデプロイできるよう、仕組みはしっかりと整備していきましょう💪 参考: https://dev.to/puku0x/github-actions-1mi5 QAチェック項目の抽出 リリースの際に、QA担当者がチェックすべき項目をPull requestの履歴から自動抽出しているチームもあります。 まず、 .github/PULL_REQUEST_TEMPLATE.md に次のような項目を設定しておきます。 次に、リリース時に起動するGitHub Actionsから、チェック済みの項目を抽出するスクリプトを起動します。 def category_by_pull (pull) return :planner_qa_pr if body.include? " - [x] 企画側QA " return :developer_qa_pr if body.include? " - [x] 開発側QA " return :other_pr end リリース用のPull requestの本文に反映されるため、QA担当者が何を見るべきかがわかりやすくなります。 定期実行 cron が使えるのもGitHub Actionsの良いところです。 on : schedule : - cron : '30 0 * * 1-5' # 平日09:30 (JST) 休日・祝日の考慮が必要な場合は、次のように前段に判定用のジョブを仕込んで needs で繋ぎます。 check : outputs : is_holiday : ${{ steps.check_holiday.outputs.result }} steps : - run : npm install @holiday-jp/holiday_jp - uses : actions/github-script@v7 id : check_holiday env : TZ : 'Asia/Tokyo' # タイムゾーン固定必須 with : script : | const holidayJp = require('@holiday-jp/holiday_jp'); return holidayJp.isHoliday(new Date()); some_job : needs : check if : needs.check.outputs.is_holiday == 'false' outputs は string で返ってくるため、 boolean で扱いたい場合は fromJson(needs.check.outputs.is_holiday) のように変換すると良いでしょう。 まとめ この記事では、GitHub Actionsを使った様々な自動化の手法を紹介しました。 公式が提供するActionの他にも、世の中には有用なActionがたくさんあります。 サードパーティ製のActionの利用については、「要件を満たせるか」「セキュリティ上の懸念はないか」「継続的なメンテナンスが期待できるか」等が選定の基準となるでしょう。 前回の記事では GitHub Actions高速化の事例 を書いておりますので、合わせてご覧いただけたらと思います。 弊社の他のGitHub Actions活用事例は、次のスライドで紹介しております。 ファインディでのGitHub Actions活用事例 - Speaker Deck こちらの発表については、connpassのイベントページにアーカイブ動画へのリンクを載せております。皆様の参考となれば幸いです。 findy.connpass.com ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
アバター
はじめに こんにちは。プロセス改善・アジャイルコーチで、Tech Blog編集長の高橋( @Taka_bow )です。 皆さんは、2021年6月に生まれた GitHub Copilot を利用していますか? この生成AIベースのコーディング支援ツールは、コードの自動補完や生成、関数の自動生成、エラー修正支援など、開発者の作業を多面的にサポートします。 ファインディでは2023年3月から導入し、開発チーム全員が日常的に活用しています。Findy Team+で効果を測定した結果、コーディングの効率化やコミュニケーションコストの削減、さらには開発者の満足度向上など、多くの利点が確認されました。 今回は、このような ソフトウェア開発における生成AIの影響を分析した最新の論文を紹介 します。GitHub Copilotが開発プロセスにもたらす変化や、開発者の生産性への影響についての研究が書かれた、興味深い論文です。 はじめに 生成AIが高度なスキルを要する仕事に与える影響 実験の概要 具体的な成果 シニア開発者には控えめな効果 ジュニア開発者への影響 シニア開発者への影響 詳細な比較 ツール採用パターンの違い 補足:Accentureの実験初期のデータ破棄(論文付録D) おわりに:試されるのは「人間」 お知らせ! 生成AIが高度なスキルを要する仕事に与える影響 2024年9月5日、GitHub Copilotに関する1つの論文がプレプリントサーバーのひとつ SSRN(Social Science Research Network) に公開されました。 papers.ssrn.com 直訳すると 「生成AIが高度なスキルを要する仕事に与える影響:ソフトウェア開発者を対象とした3つのフィールド実験からの証拠」 といったところでしょうか。 査読前論文なので、現在は誰でも読むことが出来ます。 この論文は、Kevin Zheyuan Cui氏(プリンストン大学)、Mert Demirer氏(マサチューセッツ工科大学)、Sonia Jaffe氏(マイクロソフト)、Leon Musolff氏(ペンシルベニア大学ウォートン校)、Sida Peng氏(マイクロソフト)、およびTobias Salz氏(マサチューセッツ工科大学)によって執筆されたもので、 大規模な実験 の結果を分析した科学的アプローチの成果が記されています。 実験の概要 この研究では、GitHub Copilotの効果を検証するため、大規模なランダム化比較試験が行われました。実験の舞台となったのは、Microsoft、Accenture、そして匿名のFortune 100エレクトロニクス製造会社の3社です。 驚くべきことに、この実験には実に 4,867人 もの開発者が参加しました。 参加者は2つのグループに分けられ、一方には GitHub Copilot が提供され、もう一方は 従来の開発手法 を継続しました。この比較により、GitHub Copilotが本当に開発者の生産性を向上させるのか、客観的に検証することが目的でした。 また、各社の実験期間は次の通りです。 Microsoft :2022年9月初旬から2023年5月3日まで(約7ヶ月間) Accenture :2023年7月末から2023年12月まで(約4ヶ月間) 匿名の大手企業(Fortune 100) :2023年10月から2ヶ月間(段階的にロールアウト) このような実験の設計により、異なる企業環境や期間での GitHub Copilotの効果を包括的に評価することが可能となりました。 具体的な成果 実験結果は、GitHub Copilotの効果を明確に示しています。 GitHub Copilotを使用した開発者グループでは、次のような顕著な改善が見られました。 タスク完了数: 平均で26.08%増加 (標準誤差: 10.3%) コミット数: 13.55%増加 (標準誤差: 10.0%) コードコンパイル回数: 38.38%増加 (標準誤差: 10.0%) これらの数字は、GitHub Copilotが開発作業全体の効率を確実に向上させていることを示しています。特筆すべきは、経験の浅いジュニア開発者への効果です。このグループでは、 GitHub Copilotの採用率がより高く 生産性の向上がより顕著 であったことが報告されています。このことから、GitHub Copilotが特にキャリア初期の開発者のスキル向上と生産性増加に大きく貢献する可能性が示唆されています。 シニア開発者には控えめな効果 この研究で特に注目すべきは、GitHub Copilotがジュニア開発者とシニア開発者に与えた影響の違いです。 ジュニア開発者への影響 GitHub Copilotは、経験の浅い開発者に対して特に大きな効果を示しました。 新しいコードの書き方や構文を効率よく学習 タスクの進捗が大幅に向上 プルリクエスト数が40%増加(シニア開発者は7%) これらの結果から、GitHub Copilotはジュニア開発者にとって単なる補助ツールを超えた 学習ツール としての役割を果たしていると考えられます。 シニア開発者への影響 一方、シニア開発者に対する影響は比較的小さいものでした。これには次のような要因が考えられます。 すでに確立された作業スタイルがある 新しいツールの採用に対する慎重さ 詳細な比較 次の表は、ジュニアとシニア開発者におけるGitHub Copilotの効果の違いを示しています。 項目 ジュニア開発者 シニア開発者 プルリクエスト増加率 40% 7% コミット数増加率 21% 16% ビルド数増加率 29% 13% GitHub Copilot採用率 82.1% (±2.1pp) 76.8% (±2.1pp) 採用後1ヶ月後の継続使用率 84.3% 74.8% GitHub Copilot提案受け入れ率 25.2% 24.7% 注:この研究では、会社での採用時の職位や在職期間に基づいて開発者を「ジュニア」と「シニア」に分類しています。具体的な基準については詳細が明らかにされていません。 ツール採用パターンの違い 実験結果から、GitHub Copilotの初期採用率は予想外に低かったことが分かりました。 Microsoftでは最初の2週間で採用率が42.5%にとどまり、 リマインダー後に64%まで上昇 。Accentureでは全体で約60%の採用率でした。 この結果は、新しいツールの導入には単なる提供以上のものが必要だということを示しています。適切なトレーニングやサポートが不可欠で、段階的な導入や定期的なリマインダー、効果的な使用法の教育セッションなどが有効な戦略となりそうです。 これらの知見は、GitHub Copilotに限らず、新技術の導入時に直面する一般的な課題を反映しています。単にツールを導入し、現場任せにするだけでは、効果的に活用できないでしょう。急速に進化する開発環境では、新ツールを戦略的に導入し、継続的にサポートすることが、組織の競争力を保つうえで極めて重要です。 補足:Accentureの実験初期のデータ破棄(論文付録D) Accentureの実験には意外な展開がありました。2023年4月に同社が実施した大規模なレイオフ(19,000人の従業員削減)により、当初の実験が中断を余儀なくされたのです。 このレイオフは実験にも大きな影響を与えました。 実験参加者の42%が影響を受ける データの質に問題が生じる GitHub Copilotの使用状況や採用データの記録が不十分に 結果として、この初期実験のデータは信頼性に欠けるものとなりました。しかし、204人の開発者に絞って行った分析では、次のような傾向が見られました。 プルリクエスト数: 39.18%減少 (標準誤差: 36.78%, 統計的に有意ではない ) コミット数: 43.04%増加 (標準誤差: 38.80%) ビルド数: 12.33%増加 (標準誤差: 53.60%) これらの結果は統計的な信頼性が低く、実験結果としては参考程度にとどめるべきでしょう。 おわりに:試されるのは「人間」 この研究から、GitHub Copilotがソフトウェア開発者の生産性向上に確かな効果をもたらすことが明らかになりました。特に、ジュニア開発者への顕著な効果は注目に値します。 一方で、シニア開発者への効果が限定的だった点も興味深い発見です。これは、経験豊富な開発者がすでに高いスキルを持ち、AIのサポートをそれほど必要としていない可能性を示唆しています。 しかし、ジュニア開発者がAIの提案を鵜呑みにするリスクも考慮する必要があります。ここで、シニア開発者によるコードレビューの重要性が一層高まると言えるでしょう。 調査会社の 米ガートナーが発表した内容 によると、AI コードアシスタント(生成AI)に関するマジック・クアドラントと共に、次のような予測がなされていました。 戦略的計画の仮説 2027年までに、ソフトウェア開発ライフサイクル(SDLC)のあらゆるフェーズを拡張するためにAIを活用するプラットフォームエンジニアリングチームの割合は、5%から40%に増加する。 2027年までに、80%の企業がAIで拡張されたテストツールをソフトウェアエンジニアリングツールチェーンに統合しており、これは2023年初頭の約15%から大幅な増加となる。 2027年までに、AI生成コードに対する”ヒューマン・オーバーサイト”(人間がAIに対して動きを見たり止めたりできる) *1 が不足しているために、本番環境に流出するソフトウェア欠陥が25%に達し、2023年の1%未満から大幅に増加する。 2028年までに、90%の企業のソフトウェアエンジニアがAIコードアシスタントを使用するようになり、2024年初頭の14%未満から増加する。 2028年までに、生成AI(GenAI)の使用により、レガシーアプリケーションのモダナイゼーションコストが2023年の水準から30%削減される。 Figure 1: Magic Quadrant for AI Code Assistants ここでも、人間側によるチェックが不足することでソフトウェア欠陥の上昇が予測されており、結局のところ、 AIツールの活用と人間の経験や判断力のバランスが、質の高い開発プロセスの鍵 となりそうです。 私たちに求められるのは、技術の進化を単に観察するだけでなく、それを最大限に活用しつつ、人間ならではの創造性や洞察力を発揮していくことです。この新しい時代の開発環境で、私たちはどのような価値を生み出していけるでしょうか。その答えを、皆さんと一緒に見つけていきたいと思います。 お知らせ! 現在、私と一緒にイネイブリングチームの立ち上げを行うメンバーを探しています! イネイブリングチームは、単なる開発支援を超えた重要な役割を担います。 組織全体のエンジニアリング力を向上 開発スキル向上のためのトレーニングやワークショップを実施 プロセス改善の提案とコーチングを行い、開発生産性とDevExを向上 社内外のエンジニアを対象とした活動を展開 このチームで、ファインディの成長エンジンとなりませんか?興味がある方は、ぜひこちらをクリックしてみてください↓ herp.careers 他にも、ファインディでは一緒に働くメンバーを募集中です。 herp.careers *1 : AIの文献で良く出てくる"human oversight"は適した日本語訳がなく、一旦このように訳した
アバター
ファインディ株式会社でフロントエンドのリードをしております 新福( @puku0x )です。 弊社では、数年前に社内のCI環境をすべてGitHub Actionsに移行しました。 この記事では、弊社のGitHub Actions活用事例の内、CI高速化についてご紹介します。 なぜCI高速化に力を入れるのか CI高速化 キャッシュの活用 ジョブの並列化 Larger Runners まとめ なぜCI高速化に力を入れるのか 当ブログをはじめ弊社では、たびたびCI高速化の大切さについて言及しています。 Findyの爆速開発を支えるテクニック - Findy Tech Blog RailsのCIのテスト実行時間を 10分から5分に高速化した話 - Findy Tech Blog Findy転職フロントエンドの開発生産性を向上させるためにやったこと - Findy Tech Blog これはなぜでしょうか? 開発が進むにつれて、コードベースが肥大化し、CIの待ち時間が増えていくのは皆さんにも経験があると思います。 CIの待ち時間が長いとついレビューを放置してしまいがちです。 レビューが遅いとブランチの生存期間が伸び、コンフリクトの発生確率が上がります。 コンフリクトを解決しても、CIが遅い状態ではまた同じことの繰り返しとなるでしょう。 GitHubの調査では、開発者は多くの時間をCI待ちに費やしていると報告されています。 github.blog 見方を変えると、CI高速化はコーディングの効率化と同程度のインパクトがあると言えます。 チームの開発生産性を支える基盤として、弊社はCI高速化に力を入れているのです。 CI高速化 キャッシュの活用 弊社では、 actions/cache を使って、依存関係のインストールを省く工夫を取り入れています。 ここでは例として、フロントエンド系のリポジトリのワークフローを紹介します。 - uses : actions/setup-node@v4 id : setup_node with : node-version : 20 - uses : actions/cache@v4 id : cache with : path : node_modules key : ${{ runner.arch }}-${{ runner.os }}-node-${{ steps.setup_node.outputs.node-version }}-npm-${{ hashFiles('**/package-lock.json') }} - if : steps.cache.outputs.cache-hit != 'true' run : npm ci フロントエンド周りのCIを組んだことのある方はピンと来たかと思います。 このワークフローでは、 node_modules ディレクトリをキャッシュしています。 npm公式では非推奨とされていますよね? なぜこのような書き方でも大丈夫なのでしょうか?その秘密はキャッシュのキーにあります。 key : ${{ runner.arch }}-${{ runner.os }}-node-${{ steps.setup_node.outputs.node-version }}-npm-${{ hashFiles('**/package-lock.json') }} キャッシュキーとして、OSやNode.jsのバージョン、パッケージマネージャーの種別などが細かく設定されています。 node_modules ディレクトリのキャッシュが非推奨とされる理由は、異なる環境で実行されることによるファイルの不整合を防ぐためです。これに気を付けていればキャッシュしても良いのです。 ※最近のGitHub ActionsではArmランナーが利用できるため、キャッシュキーにCPUアーキテクチャを追加するとより堅牢になるでしょう。 node_modules がキャッシュヒットしなかった場合を考慮して、 .npm ディレクトリのキャッシュも含めると次のようになります。 - uses : actions/setup-node@v4 id : setup_node with : node-version : 20 - uses : actions/cache@v4 id : cache with : path : node_modules key : ${{ runner.arch }}-${{ runner.os }}-node-${{ steps.setup_node.outputs.node-version }}-npm-${{ hashFiles('**/package-lock.json') }} - uses : actions/cache@v4 if : steps.cache.outputs.cache-hit != 'true' with : path : | ~/.npm key : ${{ runner.arch }}-${{ runner.os }}-node-${{ steps.setup_node.outputs.node-version }}-npm-${{ hashFiles('**/package-lock.json') }} restore-keys : ${{ runner.arch }}-${{ runner.os }}-node-${{ steps.setup_node.outputs.node-version }}-npm- - if : steps.cache.outputs.cache-hit != 'true' run : npm ci 弊社の場合では、これでおよそ20秒〜30秒ほど高速化できました。 ジョブの並列化 バックエンドのテストを例に挙げます。このワークフローでは、 matrix 機能を用いてテストを10並列で動作させるようにしました。 strategy : matrix : ci_node_index : [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] steps : - name : Test env : CI_NODE_TOTAL : ${{ strategy.job-total }} CI_NODE_INDEX : ${{ matrix.ci_node_index }} run : | # get spec files order by filesize TEMP_FILE_PATH=$(mktemp) git ls-tree -r -t -l --full-name HEAD | grep '_spec.rb' | sort -n -k 4 | awk '{ print $5 }' | ./scripts/ci/rspec_split_files.sh > $TEMP_FILE_PATH # echo outputs echo "====SPEC FILES COUNT====" cat $TEMP_FILE_PATH | tr ' ' '\n' | wc -l echo "====SPEC FILES====" cat $TEMP_FILE_PATH | tr ' ' '\n' # run rspec bundle exec parallel_rspec -- --format progress -- $(cat $TEMP_FILE_PATH) #!/bin/bash i = 0 ret = () while read -r line do if [ $[ i % $[ CI_NODE_TOTAL ]] = $[ CI_NODE_INDEX ] ] ; then ret += ($line) fi let i++ done echo ${ret[ @ ]} 単純にテストを均等配分するのではなく、ファイルサイズでソートするという工夫が施されています。これは、テスト実行時間がファイルサイズに比例するという仮定に基づいています。 これらの工夫により、実行時間が従来の約半分になるまで高速化できました 🚀 詳細は↓こちらの記事をご覧ください。 tech.findy.co.jp Larger Runners 基本的には並列化でCI高速化を目指しますが、難しい場合はLarger Runnersを使うのも手です。 (並列化の可否の判定は、テスト対象ファイルの分割を外部から制御可能かどうかで決めています) Larger Runnersは、契約しているプランが「GitHub Teamプラン」または「GitHub Enterprise Cloudプラン」の場合に利用可能です。 最大で64コアまでスペックアップできるらしいです。いつか使ってみたいですね! docs.github.com Armランナーについては先日GAとなったこともあり、積極的に移行を検討するようになりました。 github.blog 3割程度のコスト削減ができることから、既に社内のいくつかのプロジェクトではLinux Armランナーに完全移行しています。 2024年9月現在では、 ruby/setup-ruby などサードパーティの対応が進んでいないものもあります。移行の際は動作検証を十分にしておくと良いでしょう。 参考までに、弊社のフロントエンドでの高速化の例を示します。 Build Test 2コア 15m8s 12m7s 4コア 8m38s 6m56s スペックアップするとコストは増えますが、その分CIの待ち時間の削減が期待できます。実質的な負担に大きな変化が無い場合は強気でスペックアップしていきましょう 💪 まとめ いかがでしたでしょうか? この記事では、弊社のGitHub Actions活用事例の内、CI高速化についてご紹介しました。 CIの待ち時間については、「継続的デリバリー」の書籍を参考に 10分以内 を目指すと良いでしょう。 www.kadokawa.co.jp 実際に弊社では、これらの取り組みを行う前は1PRあたり15分〜20分ほどかかっていたCIが、10分以内の完了を目指して取り組んできた結果、平均5分程度まで高速化できた例もあります。 弊社にはCIの整備に関心の高いメンバーが多く在籍しております。勉強会等でお会いする機会がありましたらぜひお声がけください。 CIは5分以内!素早い開発サイクルを支えるCI - Speaker Deck ファインディでのGitHub Actions活用事例 - Speaker Deck こちらの発表については、connpassのイベントページにアーカイブ動画へのリンクを載せております。 findy.connpass.com 次回へ続きます!👋 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
アバター
こんにちは。 Findy で Tech Lead をやらせてもらってる戸田です。 突然ですが皆さんは本を読みますか? エンジニアという職業柄、技術書やビジネス書など、様々なジャンルの本を読む機会が多いのではないでしょうか? そこで今回は、人生を変えた一冊と題して、弊社エンジニア達のお気に入りの一冊を紹介していきます。 それでは見ていきましょう! 人生を変えた一冊 戸田 ソフトウェア・ファースト あらゆるビジネスを一変させる最強戦略 ジョナサン・アイブ 高橋 1兆ドルコーチ シリコンバレーのレジェンド ビル・キャンベルの成功の教え 森 アジャイルサムライ――達人開発者への道 まとめ 人生を変えた一冊 戸田 ソフトウェア・ファースト あらゆるビジネスを一変させる最強戦略 ソフトウェア・ファースト 作者: 及川 卓也 日経BP Amazon 及川卓也さん著の本で、DX(デジタルトランスフォーメーション)の本質を理解するためには必読の一冊です。 2024年9月には内容を大幅改定した第2版が発売予定とのことで、こちらも合わせてチェックしておきたいですね。 ソフトウェアファースト第2版 あらゆるビジネスを一変させる最強戦略 作者: 及川 卓也 日経BP Amazon 内容としては、日本のIT史の振り返りから始まり、現在の問題点の指摘、そしてDXの在り方、進め方について詳しく解説されています。 また、これからの時代の強い開発組織の在り方や作り方にも言及されており、エンジニアだけではなくマネージャーや経営者の方にも是非読んで欲しい一冊になっています。 実際に読み進めていくと共感できる部分が多く、「わかっているけど中々実行に移すことができない」という方にヒントを届けてくれるような内容でした。 この本が世に出た当時、自分は30代に入ったばかりで、いわゆるマネージャーとしてのキャリアを少しかじっていた頃でした。 経営陣がやって欲しいことと開発組織がやりたいことがズレている感覚が少なからずあり、この本を読んだことによってそれらがクリアになり、実際に実行に移すことが出来たことがありました。 自分が抱いていた感覚というものは当時所属していた組織特有のものではなく、社会全体の問題であるということを再確認できたため、逆に割り切って行動に移すための勇気を貰うことができました。 おそらくこの本と出会っていなかったら、今でも違和感と葛藤して何も進められないままだったと思います。そういうこともあり、エンジニア人生のターニングポイントとなった1冊になったと思います。 ジョナサン・アイブ ジョナサン・アイブ 偉大な製品を生み出すアップルの天才デザイナー 作者: リーアンダー ケイニ― 日経BP Amazon 1冊と言っていながら、もう1冊の紹介をさせてくださいw かのスティーブ・ジョブズが絶対的な信頼を寄せたデザイナー、ジョナサン・アイブ氏の生い立ち、学生時代、アップル入社後のiMac、iPhone、iPad、MacBook Airなど数々の革新的な製品づくりでの試行錯誤、社内での争いまでを描いた一冊です。 モノづくりに対する思考や姿勢、デザインに対する考え方など、エンジニアにも多くのヒントを与えてくれる内容になっています。 この本が世に出た当時、自分は20代中盤くらいで若く、これから何をしてキャリアを考えるべきなのか模索していた頃でした。その時にこの本と出会い、何気なしに読んでいくとアイブ氏のモノづくりに対する姿勢に驚かされました。 詳細は本を読んでいただきたいのですが、例えば新製品のデザインをする際、彼は数百、数千パターンのプロトタイプを作るそうです。 そのパターンなのですが、素人からみたらパッと見だと全く同じデザインに見えるそうです。しかしこれらは全て数ミリ単位で違うそうで、その違いを比べつつ、最終的なデザインに辿り着くそうです。iPhoneやMacbookなどのデザインも最初はこのようにして生まれたとのことで驚かされました。 これを読んだ時、自分が悩んでいたことが実はまだ甘い考えだったと気付かされました。世界的プロダクトを生んだデザイナーだとしても、まず最初に泥暗いことをやり切っているからです。 自分程度のエンジニアが今の段階でウダウダ悩んでいる暇があったら、目の前のことに集中して1個ずつ泥暗いことをやり切り続け、引き出しを増やすことに集中しようと決意できました。 おそらくこの本と出会っていなかったら、今でもウダウダ悩んで手を動かしていなかったかもしれません。この本もまた、エンジニア人生のターニングポイントとなった1冊になったと思います。 高橋 ファインディでエンジニア兼プロセス改善コーチをしている高橋( @Taka_bow )です。 1兆ドルコーチ シリコンバレーのレジェンド ビル・キャンベルの成功の教え 1兆ドルコーチ シリコンバレーのレジェンド ビル・キャンベルの成功の教え 作者: エリック・シュミット , ジョナサン・ローゼンバーグ , アラン・イーグル ダイヤモンド社 Amazon シリコンバレーの伝説的コーチでありスティーブ・ジョブズの親友でもあったビル・キャンベル。 『1兆ドルコーチ』は、彼の様々な教えを記録した本です。著者は元Google会長のエリック・シュミット、元Google SVPのジョナサン・ローゼンバーグ、元Googleで経営コンサルタントのアラン・イーグルの3名。(すでに著者がすごい面子!) この3人のみならず、ビルの指導を仰いだ起業家たちの多くが今日のテクノロジー業界を牽引しています。現在のGAFAMの隆盛は、ビルの影響力なくしては語れないと言われています。 私が好きなエピソードの1つに、Googleがマネジャーを「全廃」し管理職のいない組織を作った直後の、ラリー・ペイジとビルのやりとりです。 ビルは言います。 「ここにはマネジャーを置かないとダメだ」 ラリーは答えにつまった。ちょうどマネジャーを全廃したばかりで、彼は結構満足していたのだった。(後略) 二人はどちらも譲らず、しばらく堂々めぐりの議論を続けた。とうとうビルはラリーの流儀にならって、それならエンジニアに直接聞いてみればいいと言った。(中略)ビルはその一人に、マネジャーがほしいかと訪ねた。 ええ、という返事だった。 なぜだ? 「何かを学ばせてくれる人や、議論に決着をつけてくれる人が必要だから」 その日彼らは数人のソフトウェアエンジニアと話したが、答えはほとんど同じだった。 このエピソード 1 は、たとえテック企業でもマネージャーがきわめて重要な存在であることを示しています。エンジニア組織のマネジメントで悩みがあると、今でも読み返します。 森 FIndy バックエンドエンジニアの森 @jiskanulo です。 アジャイルサムライ――達人開発者への道 アジャイルサムライ――達人開発者への道 作者: JonathanRasmusson , 西村直人 , 角谷信太郎 オーム社 Amazon アジャイル開発を進める上でのチーム作り、見積もり計画、日々の開発と多岐にわたって解説があり入門書としておすすめです。 アジャイルサムライ日本語訳版が出版された2011年当時、IT業界のエンジニアが集まり各章を読み合わせる輪読会のコミュニティが日本各地で生まれました。 このコミュニティは次第に盛り上がり、翌2012年には原著者のJonathan Rasmussonさんを日本にお招きして参加者100人超えの大イベント Agile Samurai Dojo Gathering 2012 を開催するに至りました。 蒔かれた種子は今も世界でも芽吹いているでしょう。 私個人の当時の思い出を記します。 2010年に当時勤めていた企業を退職、数ヶ月の無職期間を経て新しい会社に勤めていました。 ユニットテストを導入して安全に開発するための仕組みづくり、営業メンバーの業務効率化のためのツール作成、社内サーバーの管理やデータセンターへのラッキングなどなど会社とプロダクトに貢献するべく取り組んでいました。 そんな日々を1年ほど過ごし、ただ作業をこなすだけで1日終わってしまう作業者になっているなと感じていました。  一人でできることは限界があるとも感じており、チームを組んでよりよい課題解決のやり方を模索しどうすればいいプロダクトを作れるのか、そもそもいいプロダクトとは…と色々なことに思い悩んでいました。 思い悩んでいるなかでアジャイルサムライに出会い、この本を通じてコミュニティに参加して他の方と交流を深めることで同じような悩みを抱えている仲間がたくさんいることを知りました。 ファインディ社に入社してから、この本にある「達人開発者」の振る舞いをあらためて心がけようとしています。 まとめ いかがでしたでしょうか? 現在、ファインディでは一緒に働くメンバーを募集中です。 興味がある方はこちらから ↓ herp.careers (引用)エリック・シュミット. ジョナサン・ローゼンバーグ. アラン・イーグル. 櫻井 祐子 (翻訳). 1兆ドルコーチ シリコンバレーのレジェンド ビル・キャンベルの成功の教え. ダイヤモンド社, 2019/11/14, p.63 ↩
アバター
はじめに 皆様、はじめまして。Findyでプロダクト開発部/SREとしてジョインしました安達( @adachin0817 )と申します。今年の6月に入社し、ちょうど3ヶ月が経ちました。本日は、SREチームの立ち上げに関する0から1のプロセスと、今期の取り組みについてご紹介させていただきたいと思います。 SREチーム発足 2023年までは、バックエンドチームがインフラを担当していました。しかし、サービスの拡大に伴い、バックエンドチームのリソースが不足し、SRE的な改善が十分に行えない状況が続いていました。そこで、昨年からSREの大矢とチームリーダーの下司( @gessy0129 )がジョインし、現在は3名体制で活動しております。 SREチームの位置づけとミッション SREチームは横断的なSRE活動をしており、これを「横断SRE」と指しています。一方で、各プロダクトにおいてSRE的な役割を担っていたメンバーは「Embedded SRE」と呼ばれ、引き続き改善活動を担当しています。 SREチームは現在、チームを作り上げていく段階にあり、まだまだやることが多く残されています。こうした背景を踏まえ、SREチームの短期および中期におけるミッションを設定しました。 短期ミッション 「ファインディの事業成長を支えるための、SRE組織のあり方の確立」 中期ミッション 「社員全員が事業成長に集中できような仕組みを構築し、提供する」 SREの存在意義 SREは道を作るために存在する リスクを受け入れ、管理する SLOを計測する トイルの削減 モニタリングする 自動化する 他プロダクトの支援 SREチームの業務の進め方 アジャイル開発手法を採用しており、各メンバーにはそれぞれIssue(タスク)が割り当てられます。一週間イテレーションで管理し、Issueのクローズを目指しています。毎朝、GitHubのProjectsを使用してカンバンボードを確認し、今日のタスクや困っていることを共有や、雑談をしています。また、毎週金曜日にはチーム全体で振り返りを行い、Findy Team+を活用しながら内容をKibelaに記録し、全員が閲覧できるようにしています。さらに、隔週でEmbedded SREチームとのお茶会も実施しており、取り組みたいことを共有する場を設けています。 カンバン/ステータス ステータス 内容 To Do Issueを作成した状態、未着手 Parent In Progress 親子関係のあるIssueの親 In Progress 進行中のIssue Done 対応が完了したIssue では、今期SREチームの取り組みについてご紹介していきたいと思います。 今期の取り組みについて 全環境のTerraform import化 これまで全ての環境がTerraformで完全にコード管理されていない状態でしたが、現在は全リソースをコード化することで一元管理を実現し、環境間での設定の整合性が保たれるようになりました。まずは既存の設定を棚卸しし、リソースをインポートすることで、今後の変更や拡張がより容易になっています。さらに、Terraform Cloudを活用しながら、今後はmodule化を進めて、効率的でスケーラブルなインフラ管理を目指しています。 Findy Toolsのインフラ改善 私が入社してからFindy Toolsのインフラ改善として、いくつか対応していきました。まずは、RDSのSSL/TLS証明書更新とAuroraのマイナーバージョンアップを開発環境から本番環境まで3日で実施しました。次に監視とオブザーバビリティの強化では、元々はインフラリソースをモニタリングしておらず、SentryのエラートラッキングとAPMのみでした。そこでDatadogでのインフラリソース(ECS、外形監視、CloudFront、RDS、イベントログ、WAF)、APMなどを対象に、アラートをTerraformで管理し、ダッシュボードを手動で運用するようになりました。普段見えていなかった部分がオブザーバビリティの向上によって可視化されていきました。 また、SLI/SLOの策定を進め、ページ表示速度やリクエスト成功率の目標を設定し、エラーバジェットとバーンレートの管理を進めていきました。SLOの振り返りは隔週で実施しており、アラートが発生した際に、開発メンバーと改善案を話し合えることは非常に重要だと感じています。 最後に、Rails serverやNode.js、MySQL8の開発環境の構築を改善し、Justfileを活用して自動化を進めました。JustはMakefileと比較すると、シンプルで直感的な構文で、シェルスクリプトに似ています。また、依存関係の管理が簡単なため、学習コストも低いです。これにより、開発メンバーはスピーディーに構築できるようになりました。以下フロントエンドのJustfileになります。 Frontend Justfile # Justfile for setting up development environment # Install Homebrew and essential packages install_homebrew: @echo "Installing Homebrew if not already installed..." @if ! command -v brew >/dev/null 2>&1; then \ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"; \ else \ echo "Homebrew is already installed."; \ fi install_essential_packages: @echo "Installing essential packages..." brew install coreutils curl git # Configure Git configure_git: @echo "Configuring Git..." git config core.ignorecase false # Install asdf and Node.js install_asdf: @echo "Installing asdf if not already installed..." @if ! command -v asdf >/dev/null 2>&1; then \ brew install asdf; \ echo -e "\n. $(brew --prefix asdf)/libexec/asdf.sh" >> ${ZDOTDIR:-~}/.zshrc; \ else \ echo "asdf is already installed."; \ fi install_nodejs: @echo "Installing Node.js..." @if ! asdf plugin list | grep -q 'nodejs'; then \ asdf plugin add nodejs; \ else \ echo "Node.js plugin is already added."; \ fi asdf install nodejs asdf reshim nodejs # Install mkcert and create certificates install_mkcert: @echo "Installing mkcert if not already installed..." @if ! command -v mkcert >/dev/null 2>&1; then \ brew install mkcert; \ mkcert -install; \ else \ echo "mkcert is already installed."; \ fi create_certificates: @echo "Creating local certificates..." mkcert -cert-file localhost.pem -key-file localhost-key.pem localhost 0.0.0.0 hoge-web # Install npm packages install_npm_packages: @echo "Installing npm packages..." npm ci # Run all tasks all: install_homebrew install_essential_packages configure_git install_asdf install_nodejs install_mkcert create_certificates install_npm_packages これらの改善については、7月の弊社DevRelイベントでLTを行いましたので、まだご覧になっていない方はぜひスライドをご確認ください。 findy.connpass.com speakerdeck.com AWSセキュリティ周り向上 AWSセキュリティの強化策として、Security Hubの実装を検討しましたが、予想以上の導入工数と既存ダッシュボード機能の現状を考慮した結果、我々の要件により適した別のアプローチを模索することにしました。複数のSaaSセキュリティサービスを検討し、最終的にShisho Cloudを選定しました。Shisho Cloudは使いやすさとコスト面で現在のファインディ社の課題にマッチしており、現在はAWSセキュリティポリシーガイドラインを作成し、Embedded SREチームと協力して対応を進めています。 「Findy Team+」オブザーバビリティ周り強化 Findy Toolsで培ったオブザーバビリティやSLOの経験を活かし、Findy Team+のSRE支援も行いました。元々設定されていたDatadogのアラートはTerraformで管理されていなかったため、まず棚卸しをし、全てをTerraformでimport化しました。しきい値などは次のように環境変数で管理し、テンプレート化することで他のプロジェクトにも適用可能にしました。SLOの策定にはまだ改善の余地がありますが、今後はミッションクリティカルな領域にも取り組んでいき、開発メンバーと振り返りを実施していく予定です。 rds.alert.tf resource "datadog_monitor" "rds_cpu_alert" { name = "[${var.service_name}]rds_cpu_alert" type = "metric alert" query = "avg(last_5m):avg:aws.rds.cpuutilization{rds:${var.service_name}-${var.environment}} by {dbinstanceidentifier} > ${var.rds_cpu_critical_threshold}" escalation_message = "RDS CPU usage for ${var.service_name}_${var.environment} instance has exceeded ${var.rds_cpu_critical_threshold}%" notify_no_data = false notify_audit = false timeout_h = 1 include_tags = true monitor_thresholds { critical = var.rds_cpu_critical_threshold } message = <<-EOT @${var.slack_channel} EOT tags = local.combined_tags } 今期まだやりきれていないタスク DevSecOpsの推進に向けたセキュリティ基盤のさらなる整備 AWSコスト最適化 開発メンバー生産性の向上 開発環境 完全Docker化とJustfile化 ECS Arm化 Stg複数環境 自動化 デプロイ高速化 後半では、AWSコスト最適化や開発メンバーの生産性向上のために、さらなる効率化とやりやすさを追求し、インフラ管理や開発プロセスの改善を進めています。 まとめ SREチームの発足と今期の取り組みについて、簡単にご紹介させていただきました。チームの立ち上げからインフラの改善、セキュリティ対策まで、多岐にわたる課題に取り組んできましたが、まだやるべきことがたくさんあります。(Issueが100個以上あります) 今後も引き続き、SREチームとしてサービスの信頼性向上に努めていくのと同時にSREに興味のある方は、ぜひ一緒に働きましょう!カジュアル面談お待ちしております🙏 herp.careers findy-code.io findy-code.io
アバター
こんにちは。エンジニアの佐藤( @t0m0h1r0x )です。 今回は、弊社で現在進めている Emotion から CSS Modules への移行について紹介します。 移行の背景、検討した代替ライブラリ、そして最終的な決定について話していきます。 移行の検討理由 代替ライブラリの検討 Panda CSS Pigment CSS CSS Modulesへの移行 今後の展望 まとめ 移行の検討理由 弊社では現在、CSS-in-JSライブラリとしてEmotionを使用しています。ピュアなCSS記法を好むメンバーが多いので、EmotionのTagged Template Literal記法がチーム文化との相性も良く、これまで活用してきました。 一方で、フロントエンド開発フレームワークに Next.js を採用しており、そちらではApp Routerへの移行を進めています。 App RouterのメリットはやはりReact Server Components(RSC)の活用だと思います。RSCはユーザー体験と開発者体験の向上につながる重要な機能ですが、2024年9月現在、その仕様上EmotionでRSCをスタイリングできません。 このような背景から、RSCに対応できる新たなCSSライブラリの検討を始めました。 代替ライブラリの検討 代替ライブラリの選定にあたっては、パフォーマンスやAPIの使い勝手といった要素も大切ですが、チームとの相性も重要な要件としました。特に、Tagged Template Literalをサポートしていて、Emotionとの書き心地に互換性があることをポイントとしました。 Panda CSS Panda CSS は Chakra UI のチームが開発しています。CSSの記法としてObject LiteralかTagged Template Literalを選択できますが、後者には機能制限があるようです。 詳細については 公式ドキュメント を参照してください。 Pigment CSS Pigment CSS は Material UI のチームが開発しています。 こちらも要件と合致しそうなのですが、開発初期段階のためプロダクション利用には検討が必要です。 ちなみにPigment CSSは、最近リリースされたMaterial UI v6に、experimentalなopt-inとして組み込まれました。さらなる開発が期待できそうです。 CSS Modulesへの移行 上記の検討を踏まえ、弊社では一時的に信頼と実績があるCSS Modulesへの移行を決定しました。その理由は次の通りです。 プロダクトとチームの要件に合致 他ライブラリへの将来的な移行が比較的容易 一方でCSS Modulesの採用にあたっては、特に次の点に留意しています。 TypeScriptによる型定義 動的スタイリングの実装 仕様がメンテナンスモード 型定義には Happy CSS Modules を使用して、自動生成することで対応しています。 動的スタイリングについては、コード中にpropsベースのスタイリング実装が多くなかったことや、コンポーネントのルールを整備していたことで不要なcomponent targetingを予め減らせていたため、現時点では大きな支障は出ていません。 仕様についてはメンテナンスモードであるものの、長らくこの状態が続いていながら今の所大きな問題は起きていないため安定しているのではないかと思っています。 参考: Future of CSS Modules · Issue #187 · css-modules/css-modules · GitHub Interoperability across tools and support plain JS modules imports · Issue #1050 · webpack-contrib/css-loader · GitHub 今後の展望 CSSを取り巻く環境は日々進化しています。例えば、React v19からは<style>のホイスティングがサポートされる予定であり、それを活かした RESTYLE というライブラリも登場しています。 このような状況を踏まえ、当面はCSS Modulesへの移行を進めつつ、新たなライブラリの登場にも注目していきたいと思います。 まとめ EmotionからCSS Modulesへの移行は、弊社のフロントエンド開発環境を大きく変える重要な取り組みでした。 RSCとの互換性という課題はありましたが、暫定的な解決策を見つけつつ、より良い選択肢を模索し続けていきたいと思います。 弊社では一緒に働いてくれるメンバーを募集中です。興味を持っていただいた方は是非こちらのページからご応募お願いします。 herp.careers
アバター