Classi開発者ブログ

教育プラットフォーム「Classi」を開発・運営するClassi株式会社の開発者ブログです。

朝当番をやめた話

こんにちは。プロダクト本部プラットフォーム部SREチームのid:ut61zです。

サービスを運用していくうえで監視は避けて通れません。
Classiでは長らく朝当番という制度を設け、平日朝のピークタイムにエンジニアを監視担当としてアサインし、何らかの問題が発生した際、即座に対応できる体制を整えてきました。

2020年9月からスタートした朝当番制度ですが、2024年4月をもってこの制度を終了しました。

今回はその経緯や理由、現在のClassiの運用体制などをご紹介します。

朝当番とは

あらためて朝当番制度とは、平日朝のピークタイムにエンジニアが常に1名待機し監視する制度になります。担当エンジニアは各種メトリクスに異常がないかチェックし、問題や気になったことがあれば関係各所に情報連携を行う役割を担います。

朝当番制度の目的、背景、期待される効果など、以下のブログに詳細が記載されていますので興味があればぜひご覧ください。

tech.classi.jp

上記ブログ執筆時点(2022年)でもすでに19人のエンジニアが朝当番を経験し、今日に至るまでには述べ51人のエンジニアが朝当番を担当してきました。

Classiのシステム、そしてエンジニア組織の変化と共に朝当番もアップデートされてきました。

遍歴を追ってみましょう。

朝当番の遍歴

朝当番発足当初の運用は以下のようなかたちでした。仮にV1としましょう。

朝当番運用 V1

  • 朝当番担当を5人募集し、平日の各曜日に1人アサイン(3ヶ月で交代)
  • 朝当番担当は、08:00~09:30の時間帯、Datadogを見ながら異常がないか確認。Datadogのmonitorやダッシュボードの改善、その日のピークアクセス数や気になった点などをドキュメントにまとめる等を実施
  • Infra Performance Working Group(以下 Infra PWG) という会を隔週で実施し、朝当番のふりかえりを行い、朝当番をブラッシュアップしていく

しばらく運用を続け、アラートに反応できるメンバーが増えてきたなかで「サービスダウンが発生することはなく、朝当番中にインシデント対応する機会もほぼなくなった」という声があがるようになってきました。
そこで、朝当番担当の時間を短縮、さらに毎日ドキュメントにまとめていた内容を簡素化し、ピークアクセス数、気になったこと、次の曜日の担当者への申し送りをSlackに記述するという運用に変更しました。V2です。

朝当番運用 V2

  • 朝当番担当を5人募集し、平日の各曜日に1人アサイン(3ヶ月で交代)
  • 朝当番担当は、08:15~08:45の時間帯、Datadogを見ながら異常がないか確認。可能であれば、Datadogのmonitorやダッシュボードの改善、その日のピークアクセス数や気になった点などをSlackで報告
  • Infra PWG で朝当番のふりかえりを行い、朝当番をブラッシュアップしていく

同時に朝当番の目的のひとつであった担当エンジニアの育成は徐々に Infra PWGで吸収し、参加者の学習機会となるようにアップデートしてきました。
後ほど詳細について触れようと思います。

さて、朝当番経験者が増え、2周目、3周目を担当するエンジニアも現れ始めました。
朝当番担当でなくてもピークタイムにアラートが発砲されたときは、担当チームのエンジニアが自然と反応し、対応するケースも多く見受けられるようになってきました。
朝当番を経験し、どう振る舞えばよいかが各チームのエンジニアに知見として根付いてきたと言ってよいでしょう。
さらに朝当番の運用を縮小しました。

朝当番運用 V3

  • 朝当番担当を3人募集し、月・水・金に1人アサイン(3ヶ月で交代)
  • 朝当番担当は、08:15~08:45の時間帯アラートがあれば反応して確認、担当チームにSlackで確認
  • Infra PWG で朝当番のふりかえりを行い、朝当番をブラッシュアップしていく

火・木は朝当番なしでしばらく運用をしてみました。

朝当番をやめる

徐々に縮小していった朝当番制度ですが、朝当番がいない曜日があっても特別不安を感じなくなってきました。

朝当番制度のそもそものモチベーションはなんだったのか振り返ると、以下2点が大きな割合を占めていました。

  • 朝のピークタイムに不安定な状態になることが多発しており、エンジニアが張り付いて対応をスムーズに行えるようにしたかった
  • アラートに反応できるエンジニアがシニアエンジニアに偏っていたため、対応できるエンジニアを育成する機会を設けたかった

この2点は大きく改善されました。

ピークタイムの安定状態の獲得

朝のピークタイムはかなり安定している状態を維持できるようになってきました。

Classiのサービスは、複数のアプリケーションコンポーネントで構成され、多くのコンポーネントがAmazon ECS Fargate上で稼働しています。

ピークタイムにサービスダウンするような事態は解消されつつも、アクセスが急増することでコンポーネントのうちのいずれかが不安定な状況に陥ることはしばしば発生していました。

特に Fargate タスクのスケールアウトが間に合わず、レイテンシの悪化や接続エラーを招くような事態が頻繁に発生していました。

対して、各チームが自チームのECSサービスに対して以下のような取り組みを実施しつつ微調整を重ね、最適化を進めてきました。*1

  • オートスケーリングのターゲット追跡ポリシーの調整
  • 時間指定でのオートスケーリングの導入
  • タスク数を一気に増やすステップスケーリングの導入

最近では、直接的なコンピューティングリソースやリクエスト数とは別の、独自のCloudWatch Alarmを作成・利用してスケーリングを実現するなど、さらなる改善に取り組む事例も出てきています。それについてはまた別の機会に触れたいと思います。

また、Infra PWGで、年間のおおよそのアクセス数のトレンドを把握し「来週はアクセス数が例年スパイクする時期なので事前にECSタスク数を増やしておこう」といった会話がなされ、緩やかに人為的な調整も行っています。
これは、Classiが学校向けのサービスということもあって、比較的年間イベントサイクルが安定しているからできることではありますが、学校の年間イベントの理解を深めることが安定性に寄与するという体験は、事業会社ならではという感触があります。

そのようなシステム面、運用面での改善を重ねた結果、現在の安定した状態を維持できています。

アラートに反応できるエンジニアの増加

各プロダクトチームのエンジニアの監視への理解や姿勢が成熟し、アラートに反応できるエンジニアが増えました。

朝当番としてもっとも称賛されるべき振る舞いは「なにかおかしい、誰か見て!」と誰よりもはやくSlackのオープンチャンネル等、社内のメンバーにすぐ伝わる場で発言することです。
そうすることで、アラート対応に巻き込まれることこそ朝当番の肝だと私は思っています。
当事者になることで、仮に自身ではどこからどう調査すべきかわからなかったとしても、その後他の誰かと一緒にメトリクスを見て原因を突き止めていくという体験をすることが、サービス全体の理解や、メトリクスの見方の学習に繋がります。

そのような学習の積み重ねにより、現在ではSREやシニアエンジニアが反応せずとも、各チームでアラートに対応することが当たり前になってきています。

場合によっては、CSチーム等、関係部署への情報連携を要求されることもあります。
Classiでは「障害報」と呼んでいますが、問題発生時、起こっている事象をまとめ、すばやく関係部署に情報連携する運用を立て付けています。
朝当番として障害報を発する機会を得たエンジニアは、有事の際どうアクションすべきかという、ある種の筋肉が鍛えられてきたと言えるでしょう。

他にもアラートのメンテナンス(不要なものの削除、しきい値の調整、メンション先を都度適切なチームに変更するなど)が各チームのエンジニアにて、日常的に行われるようになってきました。

それらの状況を踏まえ、朝当番を終了することを決定しました。

やめてみて

朝当番を終了してみて半年以上経ちましたが、朝当番がいなくて対応が遅れたということは今のところ起きていません。
特に警戒している学校の長期休暇明けの日であっても、問題も起きず安定したサービス提供ができています。

もちろんアラートがゼロになったわけではないし、まだまだ信頼性やパフォーマンスの改善の余地はあります。

しかしアラートに誰も反応できないということは少なくとも起きておらず、常に担当チームによって適切な対応がなされていると言えます。

また、チーム単位でInfra PWGが開催されるなど、チームによる自律的な運用体制の強化も進んでいます。

プラットフォーム部SREチームとして

現在SREチームは、監視運用体制という観点では、Infra PWGにて各チームが監視運用をスムーズに行えるようにアラートの原因を深堀りして知見を共有したり、Datadogの便利なTipsの紹介や年間イベントの確認などを行っています。

広くエンジニアが監視運用に携わるというステージからステップアップし、各チームの運用力を洗練させていくということがSREチームに求められていると思っています。
プラットフォーム部として、各チームの運用力を継続的に底上げすることに今後さらにリソースを割いていきたいです。

また、ここまで触れてきていませんでしたが、昨年からSLI/SLOを段階的に各コンポーネントに対して定義していくことを主導しています。
今後の展望としては、SLI/SLOとエラーバジェットをベースにしたアラートの構築・運用を強化していくことを推進したいと思っています。

さいごに

朝当番は、人的リソースを充ててサービスレベルを底上げする取り組みでもありました。

人的リソースを割く以上そこには少なくないコストが発生しています。
単純なエンジニアの稼働時間というのに加え、フレックスに働けるという働きやすさを一部手放すという面も人によってはあったと思います(3クール担当した私はありました)。

しかし、それと同時にサービスと監視・運用に対する理解や姿勢の醸成のための時間でもありました。それが各チームに浸透した結果、エンジニアの時間の使い方を再配分しながらサービスレベルを向上させることができたとも言えるので、とても実りある時間の使い方だったと思います。

「また朝当番を配置しなきゃね」という事態にならないようにこれからも気を引き締めたいと思います。

最後までお読みいただきありがとうございました。

*1:もちろんECSタスク数は多ければ多いほど安定性は増しますが、コストとのトレードオフになるので、適切な時間帯に適切なタスク数が稼働していることを目指すという前提のもと調整を重ねてきました。

GraphQL スキーマから API ドキュメントを自動生成して GitHub Pages へデプロイする

Classi で提供している学習トレーニング機能を裏で支えているコンテンツ管理システム ( 以下、内部 CMS ) では、バックエンドに GraphQL を採用しています。 この GraphQL は Classi 内の様々なシステムで広く利用されています。

tech.classi.jp

内部 CMS の開発チームでは、この GraphQL スキーマの API ドキュメントを自動生成して GitHub Pages でホスティングしています
GitHub Pages は GitHub Actions ワークフローを作成するだけで簡単に静的サイトをデプロイすることができます。また、 Classi では GitHub Enterprise Cloud を利用しており GitHub Pages のアクセス範囲を制限することもできるため、社内向けドキュメントも安全にホスティングすることが可能です。

docs.github.com

GitHub Pages でホスティングすることで社内メンバーはいつでも簡単に API ドキュメントを参照できるため、チーム内外を問わず GraphQL スキーマに基づいた議論やコミュニケーションを円滑にする上で非常に役に立っています。

API ドキュメントの自動生成には Magidoc を使用しています。

この記事では Magidoc を利用して GraphQL スキーマから API ドキュメントを自動生成し、生成したドキュメントを GitHub Pages でホスティングする方法を紹介します。

Magidoc について

Magidoc は GraphQL スキーマから API ドキュメントを自動生成するためのツールです。API ドキュメントは HTML 形式で生成されます。

実際にどのようなドキュメントが生成されるのかは以下のサイトで確認できます。

Magidoc の使い方

1. インストール

Magidoc CLI は npm パッケージとして提供されているため、 npm コマンドでインストールできます。

# グローバルにインストールするコマンド例
$ npm install -g @magidoc/cli@latest

インストールが完了すると magidoc コマンドが実行できるようになります。

$ magidoc --help
Usage: Magidoc [options] [command]

Magidoc CLI helps you build beautiful and fully customizable GraphQL static documentation websites in seconds.

Options:
  -V, --version       output the version number
  -h, --help          display help for command

Commands:
  generate [options]  Generates a full static website using a template. Using this command gives you access to a limited
                      range of customization. If you wish to customize the website further than what is available, use the
                      eject command.
  preview [options]   Preview the documentation website generated with the generate `generate` command.
  dev [options]       Starts a development server with hot-reload as changes occur to watched files.
  eject [options]     Ejects from Magidoc basic template configuration, to allow for full customization of the template.
                      This will initialize a folder from a template of your choice, which can then be modified however you
                      wish.
  help [command]      display help for command

もちろん npx コマンドで実行することもできます。

$ npx @magidoc/cli@latest --help

2. 設定ファイルを作成する

Magidoc の設定は magidoc.mjs というファイルに記述します。

// magidoc.mjs
export default {
  introspection: {
    type: "sdl", // スキーマをファイルから読み込む場合は "sdl" を指定する
    paths: ["schemas/**/*.graphqls"], // スキーマファイルのパスを指定する
  },
  website: {
    // テンプレートを指定する
    // v6.1.0 時点でデフォルトで用意されてるのは "carbon-multi-page" のみ
    template: "carbon-multi-page",

    // Web サイトのルートパスを指定する
    // Public な GitHub Pages の URL は https://<オーナー名>.github.io/<リポジトリ名>/ のようになるので、
    // ここには "/<リポジトリ名>" を指定する必要がある
    siteRoot: "/example-repository",
  },
};

他にも様々な設定が用意されています。以下はその一例です。

  • URL から GraphQL スキーマを読み込む
  • 設定ファイルに直接 GraphQL スキーマを記述する
  • カスタムページを追加する
  • etc.

詳細については公式ドキュメントをご参照ください。

magidoc.js.org

3. API ドキュメントを生成する

magidoc generate コマンドで API ドキュメントを生成することができます。

$ magidoc generate
# npx を使用する場合
$ npx @magidoc/cli@latest generate

生成されたドキュメントは ./docs/ ディレクトリに出力されます。 ( 出力先は設定ファイルで変更可能 )

docs/
├─ index.html
├─ _app/
│  └─ ...
├─ introduction/
│  └─ ...
├─ mutations/
│  └─ ...
├─ queries/
│  └─ ...
└─ types/
   └─ ...

生成されたドキュメントをローカルで確認するには magidoc preview コマンドを実行します。サーバーが起動し、 http://localhost:4000 からドキュメントを閲覧することができます。

$ magidoc preview
# npx を使用する場合
$ npx @magidoc/cli@latest preview

localhost:4000

生成したドキュメントを GitHub Pages にデプロイする

GitHub Pages にデプロイするには以下の 2 通りの方法がありますが、今回は GitHub Actions からデプロイする方法を紹介します。

  • ブランチからデプロイする
  • GitHub Actions からデプロイする ← 今回紹介するのはこちら

それぞれの方法の詳細については公式ドキュメントをご参照ください。

docs.github.com

1. GitHub リポジトリの設定

GitHub リポジトリの Settings をクリックして設定画面に遷移します。

左サイドメニューから Pages をクリックして、 SourceGitHub Actions に設定します。

2. GitHub Actions ワークフローを作成する

GitHub Actions から GitHub Pages をデプロイするには以下の 2 つのアクションを利用します。

ワークフローは以下のようになります。この例では、 main ブランチに変更が push されるたびに GraphQL API ドキュメントを生成して GitHub Pages にデプロイします。

name: Publish API Docs

on:
  push:
    branches:
      - main

jobs:
  publish-api-docs:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      # actions/deploy-pages に必要な権限
      # 参考: https://github.com/actions/deploy-pages#security-considerations
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      # setup
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20

      # build
      - run: npx @magidoc/cli@latest generate

      # publish
      - uses: actions/upload-pages-artifact@v3
        with:
          path: ./docs
      - uses: actions/deploy-pages@v4
        id: deployment

GitHub Pages へのデプロイが完了すると https://<OWNER>.github.io/<REPOSITORY> から API ドキュメントを閲覧できるようになります。


冒頭で紹介した内部 CMS の GraphQL API ドキュメントのデプロイでは、実際には以下のようなワークフローを作成しています。内部 CMS の GraphQL スキーマのリリースには googleapis/release-please-action を利用しているので、 release_created output を参照することで「新しいバージョンがリリースされるたびに API ドキュメントを更新する」ということを実現しています。

tech.classi.jp

name: Release Please

on:
  push:
    branches:
      - main

jobs:
  release-please:
    runs-on: ubuntu-latest
    outputs:
      release_created: ${{ steps.release-please.outputs.release_created }}
    steps:
      # ...省略
      - uses: googleapis/release-please-action@v4
        id: release-please
        with:
          release-type: node

  publish-api-docs:
    needs:
      - release-please
    # 新しいリリースが作成されたときに API ドキュメントを更新する
    if: ${{ needs.release-please.outputs.release_created == 'true' }}
    # ...省略

まとめ

リッチな API ドキュメントがあるとテンションが上がります。
よりイケてるドキュメントにするために設計や説明文もしっかり書こうという気持ちになりますね。モチベーション大事。

sql.Openとsql.OpenDBの違い、そしてドライバーごとにsql.OpenDBを使うべきかの検討

id:aerealです。Goの話をします。

sql.Openとsql.OpenDBの違い

GoでRDBMSなどに繋ぐ際にはふつうdatabase/sqlを使います。 ORMを使う場合でも内部的にはこのパッケージに依存していることがほとんどです。

特定のデータベースに対して接続を確立したりクエリを実行する実装をドライバーと呼び、契約によって定められたインターフェースを実装したドライバーを利用者がdatabase/sqlに渡すことで、拡張性と独立性を実現しています。

sql.DBa database handle representing a pool of zero or more underlying connections.と説明されており、いわゆるアプリケーションレベルのコネクションプールを表現した構造体といってよいでしょう。 Perl monger的にはsql.DBはDBIx::Handlerに相当するものというとわかりやすいでしょう。

sql.DBを得る方法は2つあり、ひとつはsql.Open, もうひとつはsql.OpenDBです。 sql.OpenDBはGo 1.10で追加されたAPIでより新しいです。

新しいAPIが導入された動機は以下の通りです。

Drivers that want to construct a sql.DB for their clients can now implement the Connector interface and call the new sql.OpenDB function, instead of needing to encode all configuration into a string passed to sql.Open.

Drivers that want to parse the configuration string only once per sql.DB instead of once per sql.Conn, or that want access to each sql.Conn’s underlying context, can make their Driver implementations also implement DriverContext’s new OpenConnector method.

sql.Openはdata source name (以下、単にDSN) と呼ばれるDBへ接続するための情報を文字列として受け取ります。

DBへ接続する際は、ふつうドライバーが文字列から構造体へ変換してたとえば接続先のホストやポートなどを抽出して使用します。

この変換処理は新しい接続を確立する度に行われますが非効率です。

加えて単純な文字列であるDSNを用いる場合、関数やポインタなどリッチな言語表現が使えません。

たとえばイベントに応じて呼ばれるコールバック関数を受け取りたいという場合、標準のdatabase/sqlのAPIだけでは相互運用できず不便です。

こうした主に2つの問題を解決するために新しいsql.OpenDBというインターフェースが導入されました。

ドライバーごとの接続情報の事情

ここでは主にgo-sql-driver/mysqljackc/pgxの話をします。

まずこれらドライバーの接続設定を表す構造体は以下の通りです:

mysqlのほうは比較的単純ですがpgxはOnNoticeやOnPgErrorなど関数として持つフィールドが散見されます。

関数や構造体 (のポインタ) をとるフィールドを見ているとDSNをパースして得られる値と同等の値をアプリケーション内で組み立てられるのかが気になってきます。 つまりデフォルト値が公開されているのか、それともコピペしなければいけないのかということです。

pgxのDSNをパースする処理を見ると、BuildFrontendやBuildContextWatcherHandlerなどの関数のデフォルト実装そのものは公開されていませんが、公開メンバのみを使っているのでコピペすることは可能そうです。

気になるのがTLS接続の設定を決めるconfigTLSという実装です。

これはパラメータに加えてファイルシステムに配置された証明書の状況に応じてTLS接続に用いるオプションを決めるもので、最終的にtls.Configを返すのですが、かなり込み入っていることが一目でわかります。

この複雑なロジックはlibpqのsslmodeなどのパラメータ定義に倣ったもので、利用者にとっても明確な定義が外部にあることは嬉しいことが多いのですが、このconfigTLS関数は現時点で公開されておらずコピペするほかありません。

アプリケーションにおいてTLS接続設定の決定は、これほど柔軟である必要はないでしょう。せいぜい商用環境でTLS接続を厳格な設定で用い、ローカルでは無効にする程度の設定で十分です。

しかし先に述べた関数を値にとるフィールドが少なからずあることなどを踏まえると、ライブラリのデフォルト値にせいぜいホスト名やポート番号程度を加工した程度のpgconn.Configを得るためにpgconn.ParseConfigを用いずスクラッチから組み立てる実装を作るのはかなり不合理に思えます。

まとめ

database/sqlの簡単な歴史について触れ、MySQLとPostgreSQLのドライバー実装を読み解き、「ライブラリが提供するパース関数を使うか、それとも構造体をスクラッチから作るか」の実用性について検討しました。

変換処理が明らかにボトルネックでない場合、つまり文字列ではなく構造体として設定を持ち回ることで見通しをよくすることだけが目的であれば、go-sql-driver/mysqlはsql.OpenDBに移行してよい・jackc/pgxは割に合わないので従来通りsql.Openでも構わないという評価に落ち着きました。

MySQLとPostgreSQL両方に繋ぐアプリケーションで統一感を図ろうと考えたことがきっかけですが、結果的にこれまできちんと読んでこなかったドライバーの実装を読み進められました。

go-sql-driver/mysqlはフラットなパッケージ構成で極めてシンプルな実装でそれが設定構造体にも表れており、対してjackc/pgxはもともとlib/pqに欠けていた機能を補完するという目的があったためか現時点でも十分にリッチでかつ拡張性も見据えた実装になっています。

最後に、どちらも活発に開発されているライブラリなので記事の記述と実装に乖離がある場合、最新の実装が常に正しい点をご留意ください。

Kaigi on Rails 2024 に「ActiveRecord SQLインジェクションクイズ (Rails 7.1.3.4)」というタイトルで登壇します

こんにちは、ソフトウェアエンジニアの中村 ( id:kozy4324 ) です。

2024年10月25日 (金) から 26日 (土) に、東京の有明セントラルタワーホール & カンファレンスで開催予定の Kaigi on Rails 2024 にて、「ActiveRecord SQLインジェクションクイズ (Rails 7.1.3.4)」というタイトルで登壇させていただくことになりました。

今回は、このテーマを選んだ背景や、登壇を通じて達成したいことについて、事前にお伝えしようと思います。

Rails アプリケーション開発におけるセキュアコーディング、できていますか?

これは自戒の念を込めた問いかけでもあります。

昨年末、私は Ruby on Rails を使った新規サービス開発プロジェクトにジョインしました。それを機に、Rails におけるセキュアコーディングを再確認したいと思い、情報収集や学習を続けてきました。

Web アプリケーションのセキュリティに関する体系的な知識・情報としては、IPA が公開している「安全なウェブサイトの作り方」や、書籍『体系的に学ぶ 安全なWebアプリケーションの作り方』が非常に参考になりました。

Rails にフォーカスした内容でいえば、Railsガイドの「Rails セキュリティガイド」は必読です。

しかし、こうした学習を進める中で、Rails のセキュアコーディングに関する体系的な情報は、まだ多く存在していないと感じました。世の中の Rails エンジニアはどのようにしてセキュアコーディングを身につけているのでしょうか?これから Rails を学ぶ人々は、どのようにしてこの重要なスキルを習得していくべきなのでしょうか?

さらに、過去の Kaigi on Rails の登壇タイトルを見ても、セキュアコーディングを題材にした発表は少ないように感じました。

そうした背景から、私が学んできた内容をまとめて発表することが、同じように興味を持って学んでいる方々にとって有益になるのではないかと考えました。また、それが自分自身の学習機会にもつながると感じ、プロポーザルを提出し、この発表の機会をいただけることになりました。

SQLインジェクションというテーマについて

SQLインジェクションは、IPAの「安全なウェブサイトの作り方」においても最初に取り上げられるような、セキュアコーディングにおいて基本的かつ重要な項目です。Rails ではいくつかのポイントを押さえることで対策できますが、その内部でどのように対策が施されているのかを掘り下げることで、興味深いトピックになると考えました。ターゲット層は Rails 初学者から中級者までを想定し、このテーマを選びました。

登壇を通じて達成したいこと

私はセキュリティのエキスパートではなく、Web セキュリティをこれからさらに学んでいきたいと考えているエンジニアの一人です。この登壇を通じて、Rails におけるセキュアコーディングについて、私自身も含めて皆さんと一緒に学んでいけたら嬉しいです。また、Rails 開発における SQLインジェクションに対する不安を少しでも払拭することができれば、幸いです。

おわりに

タイムテーブルも発表され、どちらのセッションを聴講しようか迷ってしまうものばかりで非常に楽しみですね。当日、お会いできることを楽しみにしております!

チームでやろう!「ルールズ・オブ・プログラミング」読書会

開発グループ2(通称:kobitoチーム)のチームリーダーをしている前川です。今回は、チームメンバーを中心に『ルールズ・オブ・プログラミング』という本の読書会を行いました。

『ルールズ・オブ・プログラミング』

オライリーの『ルールズ・オブ・プログラミング』紹介ページ

全世界で1,000万本に迫る実売数を誇り、日本でも累計実売数100万本を突破(2023年5月時点)した大ヒットゲーム『Ghost of Tsushima(ゴースト・オブ・ツシマ)』をはじめ、『怪盗スライ・クーパー』などで著名なゲーム制作スタジオ、Sucker Punch Productions(サッカーパンチプロダクションズ)の共同創設者であるChris Zimmermanによる、プログラミングのベストプラクティス集。

きっかけは「リファラジ」

読書会を開くきっかけは、チームメンバーlacolacoが行っているPodcast「リファクタリングとともに生きるラジオ」の#19 雑談回 『ルールズ・オブ・プログラミング』を紹介したい!を聴いたことです。自分たちのチームのルールを考えるたたき台として機能するということや、そこで解説されている「ルール1 できるだけ単純であるべきだが、単純化してはいけない」の話など、とても興味深い紹介だったので、読書会を提案し実施しました。

読書会の進め方

全体的な方針

  • 最初から最後まで全部読む必要はない
  • 各回に1つのルールを取り上げ、担当者がざっくりとまとめて紹介し、その後ディスカッションを行う
  • 参加人数に応じて開催回数を決める(最大21回だが、全てのルールを扱う必要はない)
    • 最終的に12回開催しました

会の進行

各回60分で進行しました。1つのルールにフォーカスし、担当者が15〜30分でそのルールの要約を発表し(本を読んでいなくても内容が伝わる程度)、その後全員でディスカッションを行います。ポイントは、ルールを単に理解するだけでなく、実務にどう適用するかを話し合うことです。

例えば、 「一般化には3つの例が必要」 というルールでは、一般化を急ぐことでコードが複雑化するリスクを議論し、シンプルで読みやすいコードの重要性について深く考えました。

印象的だったルールと成果

読書会を通じて、いくつかのルールがチームに大きな影響を与えました。振り返り会で特に印象に残ったルールを一部抜粋して紹介します。

ルール4「一般化には3つの例が必要」

読書会で最も名前が挙がったルールの1つです。チーム全体で、過剰な一般化を避け、シンプルで用途を限定したコードを書く方針が確立され、実際のプロジェクトでもその考え方が浸透しました。

ルール8「実行されていないコードは動作しない」

デッドコードに対する意識が向上し、不要なコードを見つけ次第削除するという行動が定着しました。

ルール15「雑草は抜け」

小さなリファクタリングや不要なコードの削除に対する心理的ハードルが下がり、スムーズにコードを整理する動機が共有されました。

チームでの読書会のメリット

読書会の最大のメリットは、1人で読むよりもチーム全員で議論することで、さまざまな視点が共有されることです。この読書会を通じて得た知識が、チームのコーディングスタイルに良い影響を与え、日常の開発に活かされつつあると感じています。例えば、不要な一般化を避けるという考え方は、チームの議論のなかで頻繁に出るようになりました。

振り返り会での感想(抜粋)

メンバーの感想を一部紹介します。

  • 1人で読むより、チームで読書会を行う価値が大きかったと感じる
  • 議論が中心で進められた読書会だったので楽しかったし、Classiの文脈で議論できたことにも大きな価値があった
  • 皆さんの議論を聞くのが非常に楽しい読書会でした。今後は経験を積んで、聞く側から議論する側になっていきたいです

今後の展望

今回の読書会では、全てのルールを取り上げたわけではありませんが、最初から全てのルールを扱う予定はなかったので、残りを取り上げる回は予定していません。むしろ、チームでこの本を読書会として取り上げることの有効性が実感できたので、今後は社内外を問わず、同様の読書会が広がってほしいと願っています。このエントリーがそのきっかけになれば嬉しいです。

まとめ

『ルールズ・オブ・プログラミング』 を題材にした読書会は、チーム全体でコーディングのベストプラクティスを共有し、実務に活かすための素晴らしい機会です。興味があれば、ぜひ他のチームやプロジェクトでも試してみてください。チーム全体のコーディング文化の向上に役立つこと間違いなしです!

社内の9個のリポジトリの CI/CD を CircleCI から GitHub Actions に移行した際に考えたこと

はじめに

こんにちは、エンジニアの id:kiryuanzu です!今回はチームで管理するRailsリポジトリ9個の CI/CD を CircleCI から GitHub Actions に移行した際の話を共有します。

概要

Classi では全社的な方針により、メインで使う CI/CDプラットフォームを CircleCI から GitHub Actions に移行することにしました。
主な理由としては、複数の CI/CD サービスを並行して利用し続けるのは運用管理・コスト管理で負担があったこと、社内の知見交換で片方に寄せた方が良いと判断したためです。

筆者が所属するチームでは当時9個のリポジトリの運用を行っていました。それらのリポジトリの CI/CD を全て置き換えることになったため、期日までに全て移行できるように中期的な計画を立てて進めました。
今回の記事では、移行の際にまず取り組んだこと、取り組みを通して得た知見について紹介したいと思います。

取り組んだこと

複数のリポジトリの CI/CD の移行を進める上で、まずは1つのリポジトリを選定し叩き台にし、チーム共通となるデプロイフローを作りました。
最初に着手するリポジトリの選定基準は、チーム内でもっともデプロイ頻度が高かったものにしました。デプロイの試行回数が多ければ、考案したデプロイフローの改善点を見つけやすいと判断したためです。

そのようにして、作成したデプロイフローについてチームからフィードバックをもらい、より効率的なデプロイフローに作り替えた上で他リポジトリの移行作業に着手するようにしました。

チーム共通で使うデプロイフローを考案する

CircleCI から GitHub Actions に移行する上で、まずはどのようなデプロイフローに作り替えるとより良い運用方法になるかを考えました。
各リポジトリの環境に多少の差異があっても先にチームで共通で使うデプロイフローを決めておけば、用意するワークフローの大部分は同様の設計で作ればよいため、より効率的に移行できます。

GitHub Actions は GitHub 関連のイベントをトリガーとして使えるため、CIrcleCI時代に比べてより柔軟にデプロイフローの設計を考えることができました。

docs.github.com

最初はチームメンバーと相談して以下のように CI(lint, テストの実行)とステージング環境のデプロイはPR作成後にコミットがプッシュされる度に、プロダクション環境のデプロイは PR がレビュアーから approve された時に実行するようにしました。

最初に考案したデプロイフロー図

しかし、チームで運用する中で、以下の問題点が見えてきました。

  • stg デプロイを行うために CI の完了を毎回待たないといけない
  • 最新のmain に追従していない古いブランチのワークフローからデプロイを行うと、デグレが発生して障害に繋がるおそれがある
  • PR approve 後に最新の main へリベースを行った際、再度チームメンバーに approve を依頼し、もう一度最新の状態のデプロイ用ワークフローを作成する必要がある

その問題点を踏まえて、以下のデプロイフローに作り替えました。

フィードバックを受けて修正したデプロイフロー図

主な変更点

  • CI と staging デプロイのワークフローは分離し、CI を待たずに staging デプロイを実行できるようにする
  • production デプロイは PR のマージ後に main ブランチから実行することで常に最新の状態からデプロイできるようにする

デプロイフローの変更により不便だった点が解消され、チームメンバーからはデプロイがスムーズになって楽になったというフィードバックをもらいました。

このように、一度デプロイフローを決めた後も、不便な部分が分かってきたら柔軟に変えていくようにしました。
チームの人数やリポジトリの運用の仕方によって適したデプロイフローは変わるため、柔軟に別の種類のワークフローのトリガーやアクションを試していくのが良いでしょう。

例えば、Classi 内の他チームでも Release Please という GitHub Actions のアクションライブラリを使ってリリース自動化した例が開発者ブログの記事で紹介されています。

tech.classi.jp

タスクのチェックリスト作成・見積もりを立てる

チーム共通のデプロイフローを決定後、実際に対象リポジトリの移行作業を1つずつ対応する作業に移りました。
やり方としては、タスクのチェックリストを作成し1PRごとにどの順番でワークフロー・ジョブを追加する計画を立てて実際にPRを作るようにしました。

最初のリポジトリの作業では完了まで2週間前後かかりましたが、最終的に1リポジトリごとで3〜5日で移行できるようになりました。

実際に作成したタスクのチェックリストの一部

早く終えられるようになった理由としては、各リポジトリの環境の差異もありデプロイに必要なビルドの手順が違う部分がありつつも、大部分のワークフローの実装を共通で利用することで見積もりが立てやすくなったためだと捉えています。
CI/CD の構築が未経験のチームメンバーにも1つのリポジトリの作業にチャレンジしてもらい、前述したワークフローを通して作業を進めてもらいました。CI/CD の作り方を学ぶ良い機会になりました。

これは筆者の個人的な感覚かもしれませんが、CI/CD の移行作業は追加するワークフローやジョブごとでタスクを分割できるため、見積もりに関しては他のアプリケーション開発のタスクよりもイメージしやすい作業だと感じました。

また、CI/CD を考える上では対象のリポジトリのインフラ・アプリケーション構成を再確認してデプロイフローを組み立てていくことになるため、リポジトリに対する最低限の技術理解も求められます。
上記の点により、チームのオンボーディング向けのタスクとして CI/CD に触れてもらうのはとても良いと考えています。

筆者自身、この CI/CD 移行作業は半年間の休職から明けて初めて取り組んだ開発でした。
復帰後にまずこのタスクから取り組めたことは良いリハビリになったと感じています。

まとめ

このような流れで進めていき、9個あったリポジトリの CI/CD をCircleCI からGitHub Actions の移行作業を予定していた期間までに完了できました。

また、本記事の中では紹介しませんでしたが、GitHub Actions を触る中で細かい詰まり所がいくつかありそれらの知見を社内の esa で共有する活動もしました。

kiryuanzu.hatenablog.com

これらの経験を通して GitHub Actions や CI/CD周辺の技術に対して少し自信を持てるようになりました。チーム内で CI/CD周りの困り事があれば率先して動くようにし、チームに貢献する範囲を広げることができました。

移行自体は完了しましたが、より効率的なデプロイフロー・自動化の設計やCIのコスト削減などまだまだ改良できるポイントが多い領域です。効率化を進めることが顧客へ届けるリードタイムの短縮・有事の際のMTTR短縮にも繋がります。

今後も CI/CD 周辺の知見を増やし実践することで、社内の開発サイクルの効率化に繋げていきたいです。

tetoruのチームビルディング施策 -Wevox Values Cardの事例を添えて-

こんにちは。tetoruでUXデザイナーをしている原田です。 tetoruチームでは年数回、チームビルディングとして関係者全員参加のワークショップを開催しています。今回はその取り組みを具体例を交えてご紹介します。

tetoruのチームビルディングとは

このチームビルディングは2時間のワーク/3ヶ月程度に1回開催/メンバー全員参加(ただし商談などは優先)/普段の業務から離れ、異なる視点でtetoruを捉えるための対話の時間として開催されています。tetoruのチームメンバーはセールス/マーケ/CS/デザイナー/エンジニア/QAなどがひとつのチーム(※)としてまとまっており、業務内容によってはよく喋る人と全然関わりのない人が発生してしまい、ひとつの事業を一緒に行なっていても分断が発生してしまうことを解消するために行っています。チームビルディングを通して交流する時間を意識的に作ることによってメンバーの人となりを知ることや、他職種向けの業務相談や質問しやすい空気作りを醸成しています。ワーク内容はtetoruに関連することをその時に合わせオーダーメイドで組み立て、原田がファシリテーションをしています。

(※チームメンバーはこちらをご確認ください)

これまでのチームビルディング

過去2年半のワーク内容はこちらです。内容としては事業戦略に対するバリューの言語化/共通化のような真面目な内容もあれば、この1年の年表をみんなで作るといった少しくだけた内容もあったりします。

過去の施策一覧

具体例「大事な価値観ワークショップ」の紹介

直近では8月に「Wevox Values Cardで仕事における大事にしたい価値観を整理しよう」というテーマでチームビルディングを行いました。 これはtetoruチームでは定期的に取り上げている題材であり、今回で4回目です。

get.wevox.io

Wevox Values Cardとはテーマを決め、カードが5枚配られた状態からスタート。トランプのように山札から1枚カードを選び、テーマとは一番遠いカードを1枚ずつ捨てていき、最終的に手元に残った5枚のカードが自分を表す価値観になるというものになります。Wevox Values Cardは実物のカードもありますがオンライン版もあります。tetoruチームは地方在住のメンバーもいるので毎回オンラインで行っています。

ここからは事前準備と本番当日、実施後にわけてトピックごとにご紹介します。

事前準備

①偏りがないチーム分け

メンバーは20名ほどいるので、ファシリテーターが事前に4チーム分のチーム分けを行います。チーム分けは入社年月や職能(セールス/マーケ/CS/デザイナー/エンジニア/QAなど)を考慮した上で、偏りがなくフラットな状態になるように割り振りをしています。またチーム分けの際にチームリーダーも設定し、チーム内でうまく時間内にワークができるような配慮もしています。

②当日調整もできるようなタイムスケジュール

毎回のワークは2時間と決まっているので、事前に大体のタイムスケジュールを作成しています。目安の時間を事前に整理しておくことで当日は時間調整なども行うことができ、想定時間をイメージしながら当日の盛り上がりによって流れを調整しています。

③トラブルも想定した連絡用Slackの準備

当日は回線がつながらないといったトラブルに見舞われる可能性もあります。そういった事態に備えてSlackに専用の連絡用スレッドを作成しておきます。スレッド内にはチームメンバーをメンションしておき、ワーク中のオンラインの拠り所を誘導しています。

チームごとの連絡用スレッド

本番当日

④キャプチャを積極的に活用する

Wevox Values Cardのゲームの特徴として「捨てる」行為があります。 ワークは手元にある価値観カードから一番遠い価値観カードを毎回捨てていきます。時々「それ捨てちゃうの?!」というものに遭遇することもあります。もしそういうクスッと笑えるものに出会えたらSlackに投稿するようお願いしていました。

これは弊社の取締役が「責任感」を捨てた時のもの

⑤自分の言葉で大事な価値観を話してもらう

価値観ワークを終えると個人ごとに5つの価値観のキャプチャを生成することができるので、そのキャプチャを共有してもらいます。

5つの価値観をみんなで共有

自分の言葉でなぜその5つの価値観を選んだのかを1-2分で話してもらいます。それをまわりのメンバーは聞き、リアクションやコメントを残してもらいます。同じ価値観を選んだとしてもその理由はひとりひとり異なりますし、別チームで同じ価値観を選んでいると親近感も生まれやすいです。カードを捨てた際のキャプチャがある場合は、なぜその価値観を捨てたのかより説明しやすい状況を生み出すことも可能です。

実施後

⑥2回目以降は自分の価値観の変化を楽しめる

このワークは同じ内容を定期的に実施しているため、前回の価値観を比較することも可能なメンバーもいます。実際に私も計4回行っており、自分の価値観の変化を楽しむこともできます。こうやって並べてみると自分がどういうことを大事にしているのか、変わらないものと変わっているものが整理でき、自己分析に役立てることもできます。

過去4回分の価値観の変化

⑦メンバーの人となりが見える

5つの価値観は自分の言葉で話してもらうので、とてもダイレクトにその人のことを知れる機会につながります。実際に過去の例ではCSメンバーがユーザーからの問い合わせでエンジニアメンバーに声をかけやすくなったということもありました。

感想コメント

おわりに

こういった自分が大事にしたい価値観を周りの人にしゃべる機会はなかなかありません。 そのため、tetoruチームではチームビルディングという施策を通して自分の考えとメンバーの考えを共有しあう場を意図的に生み出し、今後の業務に役立てられるようにしています。 もし、通常の業務以外の場ですこしカジュアルな時間を作ってメンバーと喋る機会を設けたい人がいらっしゃいましたら、ぜひ活用いただければと思います。

© 2020 Classi Corp.