株式会社ヘンリー エンジニアブログ

株式会社ヘンリーのエンジニアが技術情報を発信します

ヘンリーのオブザーバビリティ成熟度を考える

sumirenです。

ヘンリーではオブザーバビリティに投資をし、開発生産性と品質を高める取り組みをしています。 この記事では、ヘンリーが考えるオブザーバビリティ成熟度を解説し、最後にヘンリーの現状と今後について解説します。

オブザーバビリティ成熟度

全体像

筆者は、オブザーバビリティの成熟度について、以下のように考えています。 これはあくまで一般的な概念ではなく、筆者が説明のために考えた便宜上のモデルになります。

  1. なにもない
  2. インフラメトリック
  3. アプリケーションログ
    1. 非構造化ログ
    2. 構造化ログ
    3. リクエストに紐づくログ
  4. アプリケーションメトリック(ログベース)
  5. トレース
    1. トレース単体
    2. システム固有の共通的な計装
    3. ドメイン/機能カットの計装
    4. トレースの分析と集計
    5. トレースの相関分析

オブザーバビリティ成熟度が低い状態〜中程度の状態

1. なにもない〜 2. インフラメトリック

なにもない状態は、オブザーバビリティがない状態です。この状態では、システムで問題が起きてもトラブルシュートはできません。

そこから1つ進んだ段階に、インフラメトリックのみ存在する状態があります。この状態では、アプリケーションやDBのCPUやメモリ利用率などが確認できます。大半の組織では、インフラメトリックは取れているのではないでしょうか。一方で、アジリティの高いエンジニアリング組織では障害の大半はアプリケーションレイヤで発生するため、依然としてオブザーバビリティが低い状態と言えます。

3. アプリケーションログ

アプリケーションレイヤの障害に備えてログが取れている状態です。3.2.の構造化ログまで進むと、JSON等の形式に対するクエリや集計が可能になり、生産性が高まります。

Webサービスの場合、並行して複数のサービスでリクエストが処理されるため、ログが混在して問題のリクエストを追うのが難しくなります。マイクロサービスをまたいでリクエストにIDを割り振り、構造化ログに出力することで、システム全体でリクエストが何を行ったかを把握できるようになります(3.3.)。

4. アプリケーションメトリック

例えば「過去2時間に最も実行されたAPIはなんでしょうか」という質問に答えることはできるでしょうか。これはアプリケーションのメトリックと言えます。

オブザーバビリティ成熟度が3.2.の構造化ログの段階を超え、ロギングサービスが高速なクエリや集計に対応していれば、これに答えることができます。しかし、そうでなければ難しいでしょう。

代替アプローチとして、ログからメトリックを非同期で生成して保存しておくというものがあります。この方法では、サマリ情報を保存するため、データ量やクエリ速度の観点で効果的です。ただし、事前に決めた形式でメトリックを保存するため、生データがないため、新たな問いにすぐに答えられないというデメリットもあります。

オブザーバビリティ成熟度が高い状態

分散トレーシングが活用できている組織はオブザーバビリティ成熟度が高いと考えられます。トレースの可視化により、システムで何が発生したかを視覚的に表示でき、トラブルシュートが容易になります。

一方で、トレースの活用段階にもいくつかの成熟度があると筆者は考えています。

5.1. トレース単体

まずはトレース単体が利用されている状態です。例えばシステムで例外が発生したらSentry経由で通知され、トレースIDでトレーシングサービスを検索するといったオペレーションができている状態です。

パフォーマンスを定期的に分析している組織であれば、遅い懸念のあるエンドポイントの名前でトレーシングサービスを検索し、トレース単体を見てN+1やスロークエリの問題を判断することもできます。アプリケーションメトリックが運用されていれば、遅いエンドポイントを特定してトレーシングサービスを検索することも可能です。

5.2. システム固有の共通的な計装

上記のアプローチでN+1など技術的なトラブルを解決することはできますが、アプリケーションのトラブルの大半はロジックの問題です。例えば、もしPOST /user エンドポイントのエラーレートが10%で、そのエラーがフィーチャーフラグに依るとしたらどうでしょう。

もちろん、トレースからシステムの振る舞いの全体像を掴むことは可能で、それ自体に十分価値があります。しかし、例えばBFFや個別マイクロサービスでリクエストのペイロードやフィーチャーフラグなどの情報が(個人情報の取り扱いに注意しつつ)スパンに記録されていれていたらどうでしょうか。ソースコードを読むまでもなくトレースだけでトラブルシュートが完結する場合さえあるはずです。

5.3. ドメイン/機能カットの計装

アプリケーションレイヤの障害で最も厄介なのはドメインロジックの問題です。例えばPUT /userで、DBに保存されているユーザーとフィーチャーフラグの組み合わせでエラーが発生するとします。ドメインレイヤでifに入ったかelseに入ったかで後々エラーが発生するとしたら、共通的な計装だけでトラブルシュートを完結することは難しいでしょう。

こうしたトラブルに対処するためには、個別機能で重要な情報をトレースのスパンに記録する文化が必要です。

多くの組織では、オブザーバビリティ成熟度の5.1.〜5.3.の段階を理想として目指しているか、その段階にあるのではないでしょうか。

5.4. トレースの分析と集計

筆者は、トレースの活用において、より進んだ段階として「トレースの分析と集計」があると考えています。

例えば5.2.〜5.3.の成果で、PUT /userはDBに保存されているユーザーとフィーチャーフラグの組み合わせでエラーを起こす可能性があると分かったとします。しかし、これは「可能性がある」だけです。なぜ断定できないのでしょうか。それは、見ているのがトレース単体であり他のトレースも同じ問題を持っているかの確証がないからです。

例えば、トレースをデータベースのテーブルのように扱い、当該フィーチャーフラグとユーザーの属性で全てのトレースをGROUP BYしてグループごとのエラーレートを可視化したらどうでしょうか。特定のグループが高いエラーレートを示せば、仮説に確証が持てます。

トレースに対する分析や集計のイメージ

実のところ、5.2.や5.3.はログに情報を記録するという手もありました。しかし、筆者はトレースのスパンへの記録が望ましいと考えています。それは、スパンに記録したものはトレース単体の確認とトレースの分析集計の両方で活用できるからです。

5.5. トレースの相関分析

筆者が現時点で最も先進的だと考えているのは、トレースの相関分析が利用できている状態です。

5.4.では、トレースの分析集計により、立てた仮説の検証をトレーシングサービスで完結できる可能性を説明しました。しかし、そもそも最も難しいのは良い仮説を立てることです。なぜ数あるスパンの属性の中から、特定のフィーチャーフラグやDB上の項目がエラーレートと相関している可能性が高いと思いついたのでしょうか。その仮説は、その機能を開発した人でなくても立てられる仮説でしょうか。

結局のところ、やりたいことは「PUT /userのトレース/スパンの全ての属性全てから、エラーレートと相関性の高いものをピックアップする」ということです。これは、トレーシングサービス側で全属性を突合してくれれば自動化できます。こうしたトレース属性の相関分析を活用し、誰でも良質な仮説を立てられる世界観を、筆者は目指しています。

ヘンリーのオブザーバビリティ成熟度の過程とこれから

2022年

2022年時点では、ヘンリーのオブザーバビリティ成熟度は3.3.の段階にありました。インフラメトリクスやアプリケーションログについては十分に活用できていましたが、分散トレーシングやアプリケーションメトリックはまだ導入されていませんでした。この段階では、インフラの監視とアプリケーション構造化ログの収集が中心でした。

2023年

2023年から、ヘンリーでは本格的にオブザーバビリティに投資を始めました。4のアプリケーションメトリックの成熟度が大きく高まり、5.1のトレース単体の活用にも着手しました。この年は多くの取り組みが行われ、大きな転機となりました。

OpenTelemetryの導入

OpenTelemetryを導入し、OpenTelemetry Collectorをデプロイしました。これにより、5.1.のトレース単体について技術的な整備が進み、一部のエンジニアがCloud Traceを使い始めるようになりました。

インフラに強いSREが入社

インフラに強いSREが入社しました。様々な成果を上げられていますが、オブザーバビリティに関しては、特に4. のアプリケーションメトリックの生成や運用が進みました。これにより、システムの全体感に対する可観測性が大きく向上しました。

2024年〜現在(7月)

2024年開始時点の課題は、5.1のトレース単体の技術的な成熟度が十分でなく、一部のエンジニアしか活用できていなかったことです。また、5.2〜5.3についても手つかずで、N+1などの技術的障害の解決と、システムの処理の全体感を掴むことのみが可能でした。

2024年は、上記の課題に取り組んできました。それに加え、あるべき姿を見据え、5.4.と5.5.のトレースの分析集計・相関分析にも取り組みました。その結果、成熟度は以下のような状態にあります。

  • 5.1 トレース単体の成熟度:完全
  • 5.2. システム固有の共通的な計装:高い
  • 5.3. ドメイン/機能カットの計装:着手済み
  • 5.4. トレースの分析と集計:完全
  • 5.5. トレースの相関分析:着手済み

5.1. Context Propagatorの自作

以前記事で紹介したとおり、ヘンリーではCloud Runの不具合でCloud Run間の通信でトレースが切れるという課題がありました。dev.henry.jp

この問題に対処するために、JVMとNode.jsそれぞれでContext Propagatorを自作し、Cloud Runが知る由もないHTTPヘッダでトレースコンテキストをやりとりするように改善しました。これにより、5.1.のトレース単体の技術的な整備が完全となりました。

5.2.〜5.3. トレース情報の充実化とEmbedded SREing

5.2.の共通的な計装でトレースの情報を充実させ、フィーチャーフラグ・認証情報・バージョン・エンドポイントのメタデータなど、多くの情報を共通的にトレースに含めるようにしました。また、新しいフィーチャー開発においてはEmbedded SREとして支援に入り、5.3.の個別機能の計装のイネーブルメントを進めています。

特に個別機能の計装は1つ1つの取組みの範囲こそ狭いですが、その機能でトラブルが発生したときのインパクトは絶大だろうと考えており、強く期待をしています。

5.4.〜5.5. Honeycombの導入

5.4.と5.5.の達成に向けて、トレースの分析と集計が強力なHoneycombを試験的に導入しました。Honeycombではスパンに対して柔軟に集計や可視化を行うことができ、導入しイネーブルメントすることで5.4.の成熟度が完全なものとなりました。

また、HoneycombにはBubbleUpというスパン間の相関分析もあり、5.5.についても技術的なケイパビリティがあります。ただし、これは使いこなすのが難しく、実運用で再現性が得られないと成熟度が高いとは言えないとも考えています。

今後の展望

今後は、現在低い成熟度の部分を高めていくことを目指します。当然ながら難しいテーマや時間のかかるテーマが残っている認識ですので、腰を据えて取り組んでいきたいです。加えて、上記成熟度の整理に含んでいない技術的テーマや、文化のイネーブルメントにも取り組んでいく必要があります。

5.3.の個別機能の計装、5.5.の相関分析

先述のとおり、5.3.の個別機能の計装は非常に期待の大きいテーマです。まだ始めたばかりなので、プロダクトエンジニアと密に関わりながら、腰を据えて進めていきたいと考えています。また、5.5.の相関分析についても、実運用で再現性を確立できれば、銀の弾丸といっても過言ではないほど強力な武器になりうると考えています。HoneycombのBubbleUpを実運用で利用し、ナレッジを蓄積して勝ちパターンを増やすことで、トラブルシュートにおいて誰でも良質な仮説を立てられる世界観を実現したいです。

フロントエンドオブザーバビリティ

これはOpenTelemetryにベットしていることの反動でもあるのですが、フロントエンドオブザーバビリティについては手つかずです。Sentryの導入によるトレース取得はできていますが、パフォーマンス改善のPDCAが運用されていません。この分野の整理と強化も進めていきたいです。

オブザーバビリティ文化の醸成

最後に、オブザーバビリティで最も重要な目標は、全エンジニアがオブザーバビリティを活用できることであり、イネーブルメントが肝要と考えています。ツール活用や計装のイネーブルメントの他にも、例えばオブザーバビリティ成熟度が高まったことで、既存機能でパフォーマンスの問題が多数見つかっています。そうして見つけた既存機能の問題を改善するサイクルを根付かせていくことなどにも取り組んでいきたいです。

最後に

この記事では、ヘンリーが考えるオブザーバビリティ成熟度と、現状および展望について解説しました。この記事が皆さまの組織においてオブザーバビリティの議論の役に立ったり、トレース単体活用の先にあるオブザーバビリティの世界観を知るきっかけになれば幸いです。

また、Honeycombについては、国内事例が少ないかもしれません。Honeycombを使った5.4.や5.5.の達成方法や活用事例についても、今後発信していきたいと考えています。

ヘンリーでは各種エンジニア職を積極的に採用しています。医療ドメインに興味がある方も、オブザーバビリティに興味がある方も、ぜひカジュアル面談でお話させていただければと思います。

jobs.henry-app.jp

はじめての転職で難易度鬼のレセコン開発に挑戦している

はじめまして!
5月にヘンリーにレセコン開発エンジニアとして入社した岡部(id:takamizawa46)です。 早いもので、なんと入社してから約2ヶ月も経っていたので、振り返りをしながら入社エントリーを書いてみたいと思います。

ヘンリーにやってくる前

前職は名古屋のベンチャー企業で、機械学習やチャットサービスなどの受託開発や、採用管理ツールの自社開発を経験しました。 主にサーバーサイドを担当しつつ、なんちゃって*1EMなどもやっていましたが、今思うとチームメンバーの話を聞くだけのお粗末なものだったでしょう。当時のチームメンバーには迷惑をかけてしまったと思います。

入社したばかりの頃は、とにかくコードを書くのが楽しくて「コードが書ければ何でも良い」という乱暴な開発者でした。 しかし、不思議なもので開発に慣れてくると次第に「リリースしたシステムは使われているのだろうか...?」と考え始めるようになってきました。 前職では組織の体制上、どうしてもシステムの利用ユーザーとの距離が遠くなることが多く、開発したシステムや機能が本当に使われているのかを知るには難しい状況にありました。

転職を考えはじめる

自分のモチベーションが「良いものをつくっているか」に変化した事に気づいた頃に、何となく転職を考え始めました。

ただ、初めての転職だったので、何から始めれば良いのか全く分かりませんでした。
とりあえずカジュアル面談を受けつつ改めて転職理由を考えていたのですが、言語化するのは想像以上に難しく時間がかかりました。
何度も「転職理由は何ですか?」と聞かれ考えている内に、最終的には「良いものをつくりたい」という思いと「エンジニアとして、もっと成長したい」という理由に落ち着きました。

ヘンリーを選んだ理由

ヘンリーはXでフォローしていたSongmuさんのツイートを通して知りました。
当時は*2レセコンが何なのか全く知らずの状態で、軽い気持ちでカジュアル面談を受けたのですが、担当の縣さんから「複雑な診療報酬制度をシステムに落とし込んでいく難しさ、機能・改善をリリースしまくることが、そのままユーザーへの価値提供になる」という話がとても響きました。

また、国内でクラウド型のレセコン一体型電子カルテを開発しているプレイヤーはほとんどいない、というか診療報酬制度が複雑すぎて参入するのが非常に難しい状況があります。 逆を言えば「これだけ複雑なものを作れたらカッコよくないか...?」とワクワクさせられたのと、ヘンリーには強いメンバーが集まっており、このメンバーの中で難しい課題にチャレンジすれば間違いなく一段階上のエンジニアになれるんじゃないかという期待感がありました。

その後、選考を通した体験・丁寧なフィードバックが、最終的な決め手となりヘンリーを選びました。 ここまで丁寧にフィードバックをもらったことがなかったので、例え不採用だったとしても良い経験になったなと思えるような選考でした。

入社後のあれこれ

オンボーディング

入社後の1ヶ月間はオンボーディング期間として設定されています。
初日から医療ドメインのキャッチアップや現在のチーム・業務について学べるように自分専用のTODOリストが用意されており、会社として受け入れをしっかりやろうという体制が整っているなと感じました。

オンボーディングについてはnabeo(id:nabeop)さんの記事でも触れられています。

dev.henry.jp

全体的なオンボーディングに関してはとても整備されていると感じましたが、その一方で所属するチームのオンボーディングに関しては、まだまだ未整備の部分がありました。

環境構築の手順だったり、もろもろ関連サービスへ招待・権限付与をしてもらう必要があったり...と暗黙知になっている箇所がいくつかあり、自分がジョインしたことで明瞭化されたのは良かった点だと思います。 今後、新しくジョインするメンバーが困らないように詰まった箇所はドキュメント化することで、僭越ながら整備を行いました。

ドメインのキャッチアップ

いまだに頭を抱えることが多いです。
入社して1ヶ月は単語や概念のキャッチアップで精一杯でした。
ようやく何の会話をしているかは分かる状態になりましたが、詳細の議論になってくると、理解が追いつかないことがあります。 とにかく領域が広くて深いので、根気強くキャッチアップしていくしかありませんが、皆さんが丁寧に教えて下さるので大変、助かっています。

例えば最近、関わりがあった感染対策向上加算3はこんな感じです。

感染対策向上加算3は入院初日及び入院期間が90日を超えるごとに1回算定する。
90日を超えるごとの計算は、入院日から起算して91日目、181日目等と計算する。
なお、ここでいう入院とは、第2部通則5に規定する入院期間中の入院のことをいい、感染対策向上加算1及び2については入院期間が通算される再入院の場合は算定できず、感染対策向上加算3については通算した入院期間から算出し算定する。

引用元: A234-2 感染対策向上加算(入院初日)

どうでもいい話ではありますが、ドメイン知識がついたおかげで病院でもらえる明細書が楽しく読めるようになりました。 珍しい加算がされていたりすると勝手に盛り上がっています。

業務に関して

まだまだ思ったように手は動かないなと感じています。
ドメイン知識とコードベースへの理解が不足しているので、コードを読むのに時間がかかりますし、複雑な診療報酬制度をコードに落とし込むのは本当に難しいです。 少しずつできることが増えているはず...なので、早くチームの力になりたいです。

何度か質問されたことがあったので、技術スタックについても触れておきます。
自分は元々、動的型付け言語のRubyをメインに書いていたのですが、ヘンリーではバックエンドに静的型付け言語のKotlinを採用しており、技術スタックには差異がありました。 最初は苦戦するかな?と思っていましたが、そんなことはなくKotlinは非常に書きやすい言語だなと感じています。

特に障壁はなかったですが、一例として依存性の注入は、RubyというかRailsを書いていた自分にはあまり一般的なものではなく、書籍で読んだことはあれどプロダクションコードで扱ったことがありませんでした。ヘンリーではKoinというフレームワークで依存性の注入をしており、最初はどこでインスタンスを作っているのかと不思議に思ったのを覚えています。

とはいえ、今まで触れたことがない技術に触れられるというのは、やはり転職のメリットですね。

一緒に仕事をする仲間を募集しています

ここまで読んでいただき、ありがとうございます。
ヘンリーでは複雑なドメインの開発に共にチャレンジする仲間を募集しています。
これからも成長が見込める弊社で一緒に働いてみませんか?

興味のある方は以下の採用サイトから、ぜひコンタクトしてみてください。

*1:エンジニアリングマネージャーの略称

*2:レセプト(診療報酬明細書)を作成するときに使用するレセプトコンピューターのこと

Kotlin Fest 2024にひよこスポンサーとして参加しました!

ヘンリーは先日開催されたKotlin Fest 2024に、ひよこスポンサーとして参加しました!
イベントも大盛況で、ヘンリーからも多くの開発者が参加し、活気と熱量に溢れた一日となりました。
今回はスポンサーとして出展したブースなど、参加した様子をご紹介していきます。

Kotlin Festとは

Kotlin Festは、日本Kotlinユーザーグループの主催する、Kotlinに関する国内最大規模のカンファレンスです。

www.kotlinfest.dev

2018年から始まり、今年は2019年以来5年ぶりのオフライン開催でした(前回は2022年にオンライン開催)。

続きを読む

Server-side Kotlinプロジェクトのモノレポ化について発表しました

株式会社ヘンリーでSREなどをしている戸田(id:eller)です。先日「Hack@DELTA v24.06 モノレポは、令和のソフトウェア開発における銀の弾丸か?」にお招きいただき、弊社のモノレポ化事例について発表させていただきました。当日利用した資料とQ&AをSpeaker Deckで公開しております:

speakerdeck.com

当技術ブログではこのモノレポ化について、2回に分けて解説しております。合わせて読んでいただけると理解が深まるかと思います。

dev.henry.jp

dev.henry.jp

弊社ではサービスやサポートを通じて、お客様である医療機関様ならびに国家が抱える社会課題の解決に邁進したいと考えております。そのためにはモノレポをはじめとした生産性改善施策を継続的に行い、サービスの変化を加速していくことが大切です。

社会課題解決というスタートアップらしい働きに関心をお持ちの方、チームでの課題解決が大好きな方など、様々なタレントにジョインいただきたいと考えておりますので、よろしければ採用サイトもご確認いただけると幸いです。

jobs.henry-app.jp

Test Talkで紹介されていた探索的テストの会をヘンリーでもやってみたら、早速、品質向上の効果が出た!

LEADING QUALITYの輪読会後の雑談中様子

株式会社ヘンリーCEOの逆瀬川です。

4月から製品企画の責任者として開発ロードマップや要件開発を行っています。

特に今後、電子カルテを開発するチームでは人数も増え、これから大きな機能開発も増えていくため、これまで以上に品質向上への取り組みを強化しています。

具体的には、品質と信頼性を上げるための施策会議(品質と信頼性の会)や、不具合分析の定例会を開始しました。

本日は、品質と信頼性の会で出てきた施策のひとつである探索的テストを学び、早速実行したところ、大きな効果が出たので共有します。

弊社の品質の守護神 Aさん依存体質からの脱却!!

探索的テストに取り組むことになった背景としては、QAのAさんへの依存です。

元々医療事務出身のAさんがQAやテストを学び、チームの全てのテストを担っていました。ドメインエキスパートでもある彼女はスクリプトテストの他に、暗黙的に探索的テストを行っている状態です。

現状でもAさんのおかげで一定の品質は担保されていますが、以下のような懸念がありました。

  • 開発とQAが分離しており、品質への投資がチームとして行えず、品質が上がる速度が遅いのではないか?
  • テストの視点が単一であり、複数の視点で行われておらず、特に開発者やデザイナー視点で気づく内容が漏れているのではないか?
  • 品質担保が属人的になっており、彼女が体調不良になってしまった場合やチームから離れたりした時のダメージが大きいではないか?

チームとしても品質への関心が高かったため、何かチームで取り組めるきっかけがあれば改善速度が上がると考え、気軽に始められる探索的テストを取り組むことにしました。

LayerX様のET会良い!Henryでも始められそう!

奇跡的に探索的テストを取組むことを決めた翌週に、Nihonbashi Test Talk #2があり発表の一つが探索的テストだったので、早速申込みます。

LayerX様のmatsuさん発表で早速探索的テストを学び、ヘンリーでも試せそうな感覚を持ったので早速企画することにしました。

学んだことの一部を下記に紹介します。

探索的テストの定義

  • アプリケーションから得た情報を元に仮説を構築し、その検証を反復する、柔軟かつ自由度の高いソフトウェアテストの方法
  • 上手な探索的テストの言語化
    • バグを多く見つけることが出来る人ではなく、より多くの良質な仮説を立てられる人
      • 広範囲のドメイン知識を使って、より多くの仮説を立てられる
      • 有限な時間の中でより優先度の高い仮説から検証出来ること
      • テスト対象の性質によって様々な視点から有効なアプローチの方法が取れる
  • 良質的な仮説を立てられる探索的テストの条件
    • 適切な自由度を設定したチャーター
    • テスト対象に効果的なツアーが設定されている

詳細は、matsuさんの登壇資料「上手な探索的テストとその上達方法について」をご参照ください。

探索的テストの準備

準備としてやったことは下記の通りです。

  1. 探索的テストの説明をNotionにまとめる
    • matsuさんのスライドとAgile Testingの2章を参考にしました
  2. 要求仕様の説明をNotionにまとめる
  3. 今回テストで用いるチャーターとツアーを準備する
  4. 当日の探索的テスト会のファシリテーションプランを作成する

チャーターの準備として、テスト範囲・テスト方法・テストの狙いを定めました。

初回ということもあり、完璧を求めずに自分で理解できるわかりやすいフォーマットを選んで作成しています。

参考 : 「やってみよう!探索的テスト」

今回使った「血液検査などの検査結果を確認する画面のUI改善」に対する探索的テストのチャーター例

テスト当日

当日のテストの様子

当日、オンラインでテストを実施しました。

休みだったメンバー以外は全員参加し、10名でワイワイとテストを実施しました。

実際参加したメンバーからの声

Aさん(開発者)「結論楽しい!」

Bさん(UX リサーチャー)「(機能が)良く出来てるなぁ以上出てこない」

Cさん(PdM)「成果もシェアできるしよさそうですね!!毎週のリリースごとに1時間くらいとってやってもよさそう!!」

他にも、昨日のEpic Ownerやフロント・バックエンドの開発者などが参加し、多様なメンバーが各々の観点で仮説出しとテスト実施を行いました。
結構盛り上がり、最終的に30分予定だったところを1時間に延長しています。 実際20以上の仮説が出てきて、初回にしては大成功でした。

その後、その場で出てきた不具合に対してもemojiを付けて、どれを直すかをEpic Ownerが決めていきます。今回は仕様の考慮漏れで修正したほうが良いものが多く、仕様段階での要求開発やレビューに改善の余地がありそうです。 そして、最終的には、なんと、17個の🐲がリリース前に倒されました。

探索的テスト出でてきた不具合の分類

リリース当日!

今回修正した画面は、「血液検査などの検査結果を確認する画面のUI改善」。

機能を公開した当初から検査結果を瞬時に把握しづらいとフィードバックをもらっていた画面で、ユーザーインタビューを重ねてリニューアルした画面になります。UIの完成度がとても重要な画面であり、探索的テストを経てブラッシュアップした結果を祈るのみ。。。


そして、社内外からポジティブな反響が!!!!

めちゃくちゃいい感じを伝えるPdMと、品質の高さにビビる私

お客様からのお喜びの声

学びとこれから

良かったこと

  • リリース前にチームで同期的に機能を触ることで、チームで新しい機能を出すという一体感が生まれた
  • リリース前に様々な観点より仮説が出たため、スクリプトテストでは見つけられない不具合が発見された。(特にエンジニアの技術観点が追加されたことが良かった)
  • 思ったより、改善出来る内容が多くリリース前にだいたいの不具合を潰すことが出来た
  • 通常ならリリース後にフィードバックを受けて潰していたことが内部で潰すことが出来、効率的な開発となった!
  • 探索的テスト、とても楽しい!!

改善していきたいこと

  • 仕様理解に時間がかかるため、想定より時間がかかった!ので時間が足りない
  • ツアーをうまく活用できなかった
    • ランドマークツアーやガイドブックツアー、FedEXツアーを活用していきたい
    • 専門性を生かしたツアーを提案できればよかったかもしれない
  • 開発内に閉じず、実際運用提案をしている導入 / CSのメンバーにも参加してもらいたい
  • 仮説に対して、不具合を見つける。仮説と不具合が紐づくレポートフォーマットに変えたい
  • 当日の仕切りの改善
    • 今回は各々もくもくとテストをする感じでしたが、モブテストやチャーターごとに時間を仕切るなど、ファシリテーションを改善していきたい

探索的テストはとても楽しいし、効果テキメン!!!なので、今後も継続的に行っていきたいと思います!(次回は今週実施予定!)

一方で、品質担保が属人手的である点については、今回の取組では解消されません。ただQA以外のメンバーがテストに参加したことで、品質への取り組みに実際貢献できるという実感を感じたことはとてもポジティブでした。チーム全体での品質への優先度が上がり、開発者のE2Eテストの実装や、リグレッションテストのシナリオ作成等の取組みが行われてきております。今後もこのような活動を継続的に取り組むことで属人化から解放されることが期待できるではないかと考えております。

最後に、採用中です!

ヘンリーではQA含め、各種エンジニア職を積極的に採用しています。Henry が扱っている医療ドメインは複雑ですが、社会的にもやりがいがある領域だと思っています。また、基幹システムであることと医療の業務の特異性から、求められるQAの基準は大変高く、プロダクトだけではなくプロダクトを利用したワークフローやオペレーションレベルで高い品質の実現が求められます。ぜひ、品質の高いプロダクトを提供して、社会的に価値を出していきたいという方、カジュアル面談で弊社のVPoEとお話しましょう!!

henry.jp

開発者イベントやコミュニティ参加についての期待や効果

VP of Engineeringの id:Songmu です。冒頭に、大事なお知らせですが、今週土曜日(6/22)に開催される、Kotlin Fest 2024にヘンリーはスポンサーをしています。スポンサーブースも出展しますので、是非お立ち寄りください。私もいます。

また、Henryの開発者の一人でもあり「Kotlin サーバーサイドプラグラミング実践開発」の著者でもある、 @n_takehata が、2024年版 Kotlin サーバーサイドプログラミング実践開発というタイトルで登壇します。是非こちらも聞きに来てください。

ヘンリーも社員数が増えてきたこともあり、このスポンサーを機に、イベントやコミュニティ参加に関する制度づくりを始めました。また、それらに参加する社員も増えて欲しいと思っています。そのために、改めて、社員がイベントやコミュニティに参加する意義を考え直して整理した内容が本エントリです。

前提として、頻繁に技術勉強会に参加していたり、技術コミュニティの運営に関わっているような、社交的でトレンドに敏感な開発者が社内に一定割合必要だと考えます。そういう開発者ばかりになるのが良いとも思いませんし、そういう活動に興味がない人もいても構いません。とは言え、開発者コミュニティとつながることは、組織と個人、両面にメリットがあるため、促進したいと考えます。

イベント参加に対する期待

一般的に、社員の開発者イベント参加に対する、わかりやすい期待は以下の3点です。

  • 開発者の技術力向上
    • 専門領域の技術力を高め、社の生産性の向上に繋げる
  • 技術進化と開発戦略の一致
    • トレンドのキャッチアップとエコシステムとの協調
  • ファンを増やす
    • 会社の認知率や好感度を向上させ、人材採用や自社プロダクト購買に繋げる

それぞれの観点で、まずは受動的に関わるところからで構いませんが、能動的に関わることでより効果を高められます。

開発者の技術力向上への期待

開発者の技術力向上への期待は一番わかりやすい観点でしょう。

まずは、情報や技術を学んで社内に持ち帰るというところから始め、ゆくゆくは登壇等で自ら情報発信をして広くフィードバックをもらって成長や改善につなげられると良いでしょう。

技術進化と開発戦略の一致への期待

1点目と似ていますが、技術トレンドのキャッチアップとエコシステムとの協調という観点も重要です。

すべてのソフトウェアを自前で作ることは実質的に不可能であり、自前主義で頑張りすぎるとスピードで負けてしまいます。また、技術の流行り廃りも激しくなりました。正しい(と自分たちが思う)技術が生き残るとは限らず、使われている技術が生き残るという現実もあります。利用技術が自分たちにとって好ましい方向に進化するとも限りません。

そのため、トレンドのキャッチアップやエコシステムとの協調は重要です。開発者イベントやコミュニティは、それらの雰囲気を感じ取るのに最適な場所であり、影響を与えるチャンスでもあります。

まずは、技術動向やトレンドをキャッチアップすること。それにより、技術選定の精度が高まり、ガラパゴスな技術スタックで自社開発が先細ってしまうリスクを低減できます。

そして、ゆくゆくはトレンドやエコシステムに影響を与えられるようになると良いでしょう。自分たちの開発が困らないよう、コミュニティと協調して技術進化の方向性をマネージできることが理想です。

それは、OSSや技術トレンドを作ったりコントリビュートしたり、ディスカッションしたりすること。それらについて、発信することです。実際、開発者イベントの「廊下」でOSS開発者が議論して開発方針が決まることもよくあります。

開発者コミュニティに自分たちの技術を還元することは、コミュニティからも好意的に受け止められます。これは次の「ファン」の項目にも関わってきます。

ファンを増やすことへの期待

開発者イベントでは、登壇やスポンサーをうまくやることで社の認知や好感度を大きく高められます。それらをやらずとも、参加している自社の社員が他社の社員と交流するだけでもそれらを高める効果が期待できます。近い職種同士だと話も弾みやすく、リアルな情報交換も行われやすい。そこで、会社の雰囲気や働いている人の人となりを知ってもらい、良い会社だと感じてもらうことは、非常に効果的です。

言ってしまえば、イベントの参加者は、潜在顧客だったり潜在的な採用候補者になりうる人たちです。その人達の好感度を上げることは、製品の購買や、将来的な人材採用に思ってる以上に効いてきます。潜在顧客であるかどうかは製品の特性にも寄りますが、特に、採用市場としては参加者は近い位置にいることは間違いないです。

実際、開発者はコミュニティづてで転職先を決める人も多いです。社員と直接のつながりが無くとも「コミュニティで良く名前を聞く評判の良い会社だ」と認知してもらえるだけで、その人が転職活動を始めた時に、転職先候補に挙げてもらえる確率が高まります。

閉ざされている会社より、開かれている会社の方が魅力を感じてもらいやすいのは当然のことです。

社外のコミュニティとつながること自体のメリット

前項で、わかりやすい期待を3点を挙げましたが、実は、社員が社外のコミュニティとつながる事自体、越境学習の観点から、本人と会社双方にメリットがあります。コミュニティとの触れ合いそのものは楽しいですが、それだけではない価値があります。詳しくは、以下のブログ記事が参考になります。

社員各々が、多様なコミュニティに接続されていることが大事です。それぞれの興味範囲のコミュニティに属し、知的好奇心を満たすことを会社が認めることが、モチベーション高く仕事をしてもらうことにも繋がります。

開発技術領域が細分化と同時に、コミュニティの細分化が進んでいる現状において、それぞれの社員が各方面に多様なコネクションを持つことは重要です。いわゆる「弱い紐帯の強み」における紐帯が各所に張り巡らされているイメージです。

ですので、自社の社員が、現在の社内の技術スタックとは直接つながりがない技術コミュニティに属することにも意味があります。その技術やコミュニティが長期的に役立つかもしれないし、役に立たなくても良い。実際に、コミュニティで仲良くなった人がリファラル採用につながるケースは頻繁にあります。

発信力を上げるメリット

イベントに参加する場合、能動的な発信、つまり、登壇ができると尚良いです。これは、会社と個人双方に大きなメリットがあります。個人側のメリットについては、以前私が書いた以下のエントリーに説明を譲ります。

会社側のメリットについて補足すると、社員の登壇は社の認知や好感度向上に何より効果的です。スポンサーと違って少ないお金で済みますし、参加者からの第一印象もスポンサーに比べて良い傾向にあります。登壇発表がイベントのメインコンテンツだからです。

登壇発表内容には「現場の生の声感」や「技術の面白さや楽しさ」などが盛り込まれていると魅力的になります。参加者もそれが一番有益だと感じているからです。オープンに率直にノウハウを出すことはコミュニティからポジティブに受け取られますし、その人や会社のスタイルや音楽性の共有にもなります。参加者に、そういう生の声に触れてもらい「自分とマッチしていそうだな」「この人と働きたいな」などの共感を得られば、転職先候補としても見てくれるようになるでしょう。

発表資料は、オンラインで公開すると良いでしょう。発表だけではせいぜい数百人の聴衆にしかリーチしませんが、オンラインに公開すれば、聴衆が拡散を促してくれて1万人以上にリーチすることもあります。会社説明や採用情報にも軽く触れられると効果的な広報活動になります。

ただ、企業色が出過ぎることは、特にコミュニティベースの開発者イベントでは、ポジティブに受け取られません。宣伝色が強すぎたり、メリットばかり強調するような過度なポジショントークをしてしまうのは逆効果で、折角の登壇機会を台無しにします。

イベントスポンサーを上手くやる難しさもこのあたりに起因します。以前ほど潔癖な雰囲気はなくなり、コミュニティに企業からスポンサーしてもらうことの重要性を多くの開発者が理解するようになりました。それでも、スポンサー登壇枠やブース出展では、商業色を出しすぎず、その場のコンテキストにあった発信をすることが好まれます。

この開発者コミュニティの雰囲気やコンテキストを理解することは、他職種からするとかなり難しいのではないかと感じています。これは、参加してもらって実際に体験してもらうのが効果的です。開発者に限らず、経営者、人事や広報、マーケの人などにも参加してもらえると良いでしょう。

コミュニティ作りや運営に関わる

余力があれば、会社としてコミュニティを作りを支援したり、運営に関わったりする事も考えたいところです。CSR的な側面もありますが、これもまた、受動的にコミュニティに参加するだけではなく、能動的にコミュニティづくりに関わるほうが効果を大きくできる、という打算的な考えも裏側にあります。

ただ、このあたりを真面目にやろうとすると、金銭だけじゃなくて人的コストも結構掛かるので、小さい会社がそこにどれくらい踏み込むかは悩ましいのが実情です。ただ、コミュニティや場を作るのが好きな人・得意な人がいて、そういう人が社内にいる場合、動きを妨げないことは大事です。

私個人としては、コミュニティやイベントに参加して、発表させてもらうことが好きだし得意領域です。コミュニティ作りや勉強会運営などもやったことがありますが、個人的な志向としては、そこまで情熱や優先度が高いわけではありません。

なので、コミュニティ作りや運営をして場を提供してくださっている方々には本当に感謝しかありません。このあたりはお互いの得意領域を活かして持ちつ持たれつ、という話でもあるとも思っていますが。

両刃の剣になりうるコミュニティ戦略

ここまで書いたコミュニティ戦略は、コミュニティでうまく立ち回っている他社も同様に考えており、対称性があります。つまり、他社の参加者を我々が潜在採用候補だと見ているのと同様に、自社の参加社員が潜在採用候補だと見られているということです。

とはいえ、これはそういうオープンな場での競争なので、場に出ていかないことには、会社の魅力付けで差をつけられ、ジリ貧になってしまいます。それに、コミュニティ戦略をうまくやっている会社はごく一部ですし、そこをそれほど重要視していない会社もあるでしょう。なのでちょっと丁寧に立ち回るくらいで悪くないポジションが取れます。

ここの競争に負けないために、まずは、社員の自社へのエンゲージメントが高いことが前提になります。各人が自然と、適切な自社をアピールできるようになってもらうのが理想です。

なので、そういう場に快く送り出すことが大事です。例えば「休みを取って勉強会に参加しています」などの会話がなされると「えっ、あの会社ってそんなイケてない会社なのか」と受け取られてしまうリスクもあります。愚痴や不平不満を撒き散らされるともっと困ります。

会社と社員双方のメリット

社員のコミュニティ参加に対する会社からの支援においては期待値調整が大事です。

会社としては、個人が期待する「当たり前水準」を認識しつつ、スタンスを示すことが必要です。例えば、前項で書いたように、業務扱いでコミュニティの勉強会に参加できることが「当たり前水準」になってきていることを会社側が受け止め、どういうスタンスを取って説明するか、という話などです。「海外カンファレンスの渡航費を支援して欲しい」という期待値がある時に「今はまだそこに投資はできない」という説明をするといったことです。

社員個人側も、会社と社員がWin-Winであるかどうかを意識できると良いでしょう。業務に関係ない技術領域のコミュニティに出ていくことも無駄ではない、という話はしました。ただ、それを濫用し、業務に直接関係ない領域のコミュニティに、業務に支障が出るレベルで頻繁に参加するのは困りものです。

社員が、与えられた状況を享受するだけではなく会社側のメリットも理解できるようになれば、例えば「会社にこういうメリットがあるからこのカンファレンス渡航費を支援して欲しい」といった交渉もできるようになります。

コミュニティ参加は自発的にされてほしいし、過度に縛りは設けたくありません。縛りを設ければ設けるほど、ここまで説明してきたような効果が薄れてしまうからです。性善説前提で、それぞれが節度を持って自治することが望ましいです。もちろん何が濫用なのかは、それぞれ考えがあるので、期待値調整が必要です。

何にせよ、お互いのメリットになるように期待値調整していかないと、持続性に欠けます。ただ、短期的な結果を求めない自発的な活動であることが前提なので、コミュニティ活動を通して、短期的に会社へのリターンを持ち帰られないことに対して重く受け止める必要はありません。

とはいえ、その辺のしがらみなく、気楽に参加したいこともあるでしょう。なので、敢えて個人の予定としてコミュニティに参加することもあっても良いと思いますし、私もたまにやります。ただ、それらもあまり変に考えすぎずに、業務の一環として参加して良いとは思っています。

まとめ

開発者イベントやコミュニティ参加に関する意義を私なりにまとめてみました。これらの前提を踏まえ、社内で制度設計をしているところです。この内容自体は、汎用的なものなので他の方の参考に慣れば幸いです。

また、ヘンリーは、開発者やその他職種を絶賛募集中です。カジュアル面談も実施していますので、興味のある方は是非連絡してください。お待ちしています。

Server-Side Kotlinで書かれたCloud Runサービスのコールドスタートレイテンシを短縮する

株式会社ヘンリーでSREなどをしている戸田(id:eller)です。先日弊社のエンジニアが登壇したサーバーサイドKotlin LT大会 vol.11でSansan社の柳浦様がServer-Side KotlinアプリのCloud Run コールドスタートレイテンシを改善した話をされていました。

Server-Side Kotlin アプリのCloud Run コールドスタート レイテンシを改善した話 - Speaker Deck

本件はCloud Runを使ってServer-side Kotlinを運用している弊社でも関心が高い内容です。そこで弊社の事例も紹介させていただければと思います。

動機:コールドスタートレイテンシを改善すると何が嬉しいか

Cloud Runはスケールアウト時に10秒までリクエストを滞留させます。そして10秒でインスタンスが用意できなかった場合は429エラーが発生する可能性があります。エラーの発生はユーザ体験を悪化させるため、常に多めにインスタンスを起動しておくか、コンテナを10秒以内に起動させる必要があるわけです。

常に多めにインスタンスを起動させる方法は容易ではありますが、ランニングコストを増大させます。コールドスタートレイテンシを改善できればスケールアウトに頼った運用も採用しやすくなり、ランニングコスト圧縮効果を期待できます。

採用した施策

DB接続確立の遅延

弊社ではJDBC接続のプールにHikariCPを採用しています。HikariCPはデフォルトの設定では、DataSource作成時に接続の確立まで処理をブロックします。

設定を変更することでDataSource作成をすぐに終了して後続処理を進めることができますが、この場合はDataSourceからConnectionを取得する際に例外が出る可能性があります。このためstartup probeが呼び出されたときにデータベースに接続できることを確認する処理を入れておくことが良いでしょう。

logbackの設定を動的に組み立てるのをやめる

弊社ではそこまでlogbackの設定を複雑にしていませんが、それでも起動時に ch.qos.logback.classic.util.DefaultJoranConfigurator が1秒弱の処理時間を持っていっていました。これはXMLの解析や各種インスタンスの作成・設定に時間がかかっているからと思われます。

この問題を解決するためのパッケージが logback-tyler として公開されています。まだ安定バージョンに到達していませんが、Javaコード生成ツールなので生成されたコードに自分で責任を持てば良いだけです。

また生成されたコード自身は logback-tyler に依存しないため、ランタイムの依存は追加する必要ありません。むしろjaninoへの依存を削れるので、1MiB程度ですがコンテナも小さくできます。設定時間は弊社事例では100msと、10倍高速化まで持っていくことができました。

利用しているConfiguratorは以下のようなものです。生成されたコードは変数名などに課題が多く、またLogstashEncoderなどに対応していなかったため、けっこう手を加えています。

import ch.qos.logback.classic.AsyncAppender
import ch.qos.logback.classic.LoggerContext
import ch.qos.logback.classic.spi.Configurator
import ch.qos.logback.classic.spi.Configurator.ExecutionStatus
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.classic.tyler.TylerConfiguratorBase
import ch.qos.logback.core.Appender
import ch.qos.logback.core.ConsoleAppender
import net.logstash.logback.encoder.LogstashEncoder
import net.logstash.logback.stacktrace.ShortenedThrowableConverter
import kotlin.system.measureTimeMillis

class TylerConfigurator : TylerConfiguratorBase(), Configurator {
    override fun configure(loggerContext: LoggerContext): ExecutionStatus {
        context = loggerContext
        val elapsed = measureTimeMillis {
            val asyncAppender = setupAsyncAppender()
            val loggerRoot = setupLogger("ROOT", "INFO", null)
            loggerRoot.addAppender(asyncAppender)
        }
        println("TylerConfigurator.configure() call lasted $elapsed milliseconds.")
        return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY
    }

    private fun setupAsyncAppender() = AsyncAppender().apply {
        context = this@TylerConfigurator.context
        name = "ASYNC"
        queueSize = 1024

        addAppender(setupStdoutAppender())
    }.also {
        it.start()
    }

    private fun setupStdoutAppender() = ConsoleAppender<ILoggingEvent>().apply {
        context = this@TylerConfigurator.context
        name = "STDOUT"
        encoder = setupLogstashEncoder()

        addFilter(HealthCheckLogFilter())
    }.also {
        it.start()
    }

    private fun setupLogstashEncoder() = LogstashEncoder().apply {
        context = this@TylerConfigurator.context
        throwableConverter = ShortenedThrowableConverter().apply {
            context = this@TylerConfigurator.context
            maxDepthPerThrowable = 50
            shortenedClassNameLength = 20
        }
        fieldNames.apply {
            level = "severity"
            logger = "loggerName"
            thread = "threadName"
            levelValue = "[ignore]"
        }
    }
}

JITコンパイラ最適化

Sansan様の資料にも記載されているものです。Cloud Runのドキュメントでも紹介されていますので、すでに試された方も多いかもしれません。

k6による負荷テストを行ったところ、弊社サービスではパフォーマンスへの影響も限定的と判断できました。これにより1秒近い高速化効果が得られました。

CPU boost

Sansan様の資料にも記載されているとおり、起動時にCPUを追加するCPU boostがとても強力です。JavaやKotlinのサーバはDB接続の確立やDIコンテナの初期化を行ってはじめて接続を受け付けることになりますが、DIコンテナの初期化すなわち依存関係の算出やインスタンス生成はCPUを多く使う処理です。

とはいえ普通にコードを書いていてはCPUコア数増大のメリットは受けにくいようにも感じます。JITやGCでの活用はできているはずですが…。本当はDIコンテナによるインスタンス生成をcoroutineを使って並列化したいのですが、弊社で使っているKoinにはまだこうした機能がまだありません。 lazyModule を使った遅延初期化がexperimentalな機能として実装中とのことで期待しています。

また弊社事例では該当しませんでしたが、CPUコア数が動的に変化するということは Runtime.availableProcessors() を参照して動的になにかを決定するような実装がある場合には注意が必要かもしれません。

適用を見送った手法

コンテナを小さくする

ベースイメージにalpineを使ったり、jlinkで不要な機能を削ぎ落としたJVMを作ったりしてコンテナを小さくできます。場合によっては大きな高速化効果を得られることもありますが、今回のケースでは0.5秒程度の短縮にとどまったため、ビルド工程をシンプルにするために採用しませんでした。

OpenTelemetryの自動計装を諦める

Sansan様の資料にはSplunkのagentの設定変更で充分に回避できると記載されていましたが、弊社はopentelemetry-javaagentを使っている関係からか、必要な自動計装だけを有効にしても10秒の壁を超えられませんでした。-Dotel.javaagent.debug=true オプションを有効にして調査したところ、bytecode manipulationの準備のために編集対象となるClassを探しているようで、ここがかなり遅そうでした。TypeInstrumentationのJavadocによるとクラス名以外の実装や親クラス・インタフェースによって挙動を変えるinstrumentationの場合にここが遅くなるようです。

公式GitHub Issuesにも似たような指摘と議論が複数存在します。こちらのIssueからリンクされているので、気になる方はご覧ください。昔は40秒とかかかってたんですね、さすがに今はそういったレベルではないですがパフォーマンスに課題があるのは変わらないようです。

一応extensionを用意して特定のクラスを対象から外すことはできるようですが、有効にしたい計装があまり多くないことからjavaagentによる自動計装を諦めることも充分に検討できそうです。

AppCDS

javaagentの利用を諦めるなら、AppCDSも検討できます。今回はAppCDS無しでも目標を達成できたので、ビルド工程をシンプルにするために採用しませんでした。

CRaC

JVMアプリケーションコンテナの起動高速化と言えばCRaC、という雰囲気はありますが、弊社で使っているgRPCサーバなどのフレームワークでは公式の対応を謳っているものがなく、検証工数が大きくなると判断して採用していません。Cloud RunでCRaCを使うこと自体は可能なようなので、こちらの記事などを参考に挑戦しても良いかもしれません。

まとめ

Cloud Runのコールドスタートレイテンシを改善すると何が嬉しいかについて述べ、弊社で採用した施策をいくつか紹介いたしました。よく紹介されている手法でもあまり効果がなかったり、新しく開発された技法が有効だったりと、検証のしがいがある技術領域だと感じます。特に logback-tyler はまだ情報が少ないので、この記事が皆さんの参考になれば幸いです。

弊社ではこれらの施策により、18秒近くかかっていたコールドスタートレイテンシが10秒程度に短縮されています。もっと短縮したいとは思っていますが、OpenTelemetry agentによる自動計装、CRaCなどの基盤技術に手を入れないと難しそうです。JVMアプリケーションやServer-side Kotlinの高速化なら任せろという方、ぜひ弊社の採用サイトにも足を伸ばしていただけると幸いです。

jobs.henry-app.jp