有名テック企業の技術ブログを、ひとつのフィードで。
フィード
34件
中部支店に勤務しているData Intelligence Unit Master Data Groupのウチウゾウです。 前回、前々回とご好評をいただいた「Nagoya Tech Talk」の第3弾、「Nagoya Tech Talk #3 〜CloudNative × Platform〜」を開催しました!sansan.connpass.comこれまでは「AI」をメインテーマに扱ってきましたが、第3回となる今回は「オブザーバビリティとプラットフォームエンジニアリング」に特化しました。おかげさまで、当日は多くのエンジニアの方にお集まりいただき、熱気あふれるイベントとなりました。 CTO笹川によるオープニング本記事では、当日の発表内容やイベントの熱量をコンパクトに振り返ります。 1:研究開発部の監視基盤 - DatadogからNew Relicに移行 speakerdeck.com弊社の上田より、研究開発部のEKS基盤(Circuit)の監視基盤をDatadogからNew Relicへ、わずか1カ月で移行した事例が紹介されました。EKS基盤(Circuit)の使われ方とDatadogのコスト体系の相性があまりよくなかったため、移行に踏み切ったそうです。この爆速移行の裏側には、AWSが提供するAI統合開発環境Kiroの活躍がありました。具体的には、Datadog上にあった4つのダッシュボードと160個のウィジェットをJSONにエクスポートして、KiroとNew Relic MCPを組み合わせ、半自動移行を成功させました。これまで大きなコストがかかっていた移行作業をAIによって比較的容易にできた点に驚かされました。 上田の発表 2:作るより難しい、使い続けてもらうこと - Platform as a Productの実践 speakerdeck.comSansanには、研究開発部のCircuitのほかに、Orbitという全社横断Platformも存在します。弊社の辻田より、開発者体験を向上させ、使いたいと思われるPlatformにするため、Orbitという社内開発基盤をProductとして捉えた実践についての紹介がありました。まず、フィードバックループによる改善を目指して、1対1のユーザーインタビューを通し、社内開発基盤のユーザーの解像度を地道に上げていったそうです。その後、RICEスコア(Reach/Impact/Confidence/Effort)により施策の優先順位を決め、初期構築時の複雑さを解消する施策を実施しました。その結果、導入時のオンボーディング時間は半減しました。AIでなんでもすぐに作れてしまうものの、作り物が増えてしまうと、どうしても運用負荷もじわじわと膨れていきます。開発者の生産性を高く保つためにも、開発者に選ばれ続けるためのPlatform as a Productの実践の大切さを学べるお話でした。 辻田の発表 3:アラート削減でチームの開発生産性を向上 speakerdeck.com研究開発部のEKS基盤(Circuit)の上で動いているサービスのSREである野首より、システム運用負荷を大幅に改善した取り組みに関する発表がありました。アサイン初期は、アラート発報のたびにSREがまず調査する対応を繰り返していたそうです。この運用負荷を下げるべく、研究員とSREの責任境界の明確化に取り組みました。この明確になった責任境界を運用に乗せるため、SREに属人化していたナレッジをRunbookに整理しました。また、調査方法については、Agent Skillsに落とし込むことで、運用の属人化を解消していったそうです。これらの取り組みに伴い、不要なアラートを削減していった結果、アラートの9割削減に成功したとのことでした。SREと開発の責任境界を明確にしつつも、それを運用に乗せるには協働するという姿勢と、知見の共有が大事であることを改めて感じさせられました。 野首の発表 終わりに 改めて、会場にお越しいただいた参加者の皆さま、本当にありがとうございました。名古屋のエンジニアコミュニティーをさらに盛り上げ、最先端の知見をシェアし合える場として、「Nagoya Tech Talk」は今後も継続して開催していきます。「名古屋のエンジニアコミュニティーに関心がある」という方は、ぜひconnpassグループのフォローをお願いします!また、Sansan中部支店では、このような刺激的な環境で共に挑戦するメンバーを募集しています。イベントのオープニングでCTOの笹川が登壇した際の「Sansan中部支店の紹介資料」も公開しておりますので、私たちの組織や開発環境に少しでも興味を持っていただけた方は、ぜひチェックしてみてください。 speakerdeck.comそれでは、次回のNagoya Tech Talkでお会いしましょう! Sansan技術本部ではカジュアル面談を実施しています Sansan技術本部では中途の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。
「プロダクトで成果を出す。そのために必要なら職種の枠にはこだわらない」 そう語るのは、取引管理サービス「Contract One」のプロダクトマネジャー(PdM)を務める午菴と北野です。二人がいま向き合っているのは、AIによって大きく変わり始めたプロダクト開発の現場です。PdM自らがUI改善を実装し、エンジニアは事業視点で提案し、法務メンバーも企画や商談に入り込む。役割や肩書きにとらわれず、それぞれがプロダクトの成果に向けて動いています。役割や職種の枠を越えて挑戦する。そんなContract One事業部では、どのようなプロダクトづくりが行われているのでしょうか。 異なるキャリアを歩んできた二人に、Contract Oneならではのプロダクトづくりと、そこでPdMが果たす役割について聞きました。 午菴 夏希 / Natsuki GoanContract One事業部 プロダクト室新卒で広告代理店にて営業・販売促進・新規事業に従事。2017年よりデザイナーに転身し、大手人材企業の転職サービスの企画・開発を担当。 2020年12月にSansan株式会社へ入社し、ビジネスデータベース「Sansan」のプロダクトデザイナーを経て、2024年からContract Oneのプロダクトマネジャー兼プロダクトデザイナーを務める。 北野 浩司 / Koji KitanoContract One事業部 プロダクト室大学院卒業後、外資系計測機器メーカーにエンジニアとして入社し、研究開発者向けSW/HWのテクニカルサポートに従事。その後、製造業調達部門に特化したSaaSスタートアップに転職。マーケティングや導入活用支援を兼務したのちプロダクトマネジャーとして新規事業の立ち上げに携わった。 2025年8月にSansan株式会社に入社し、プロダクトマネジャーとしてContract Oneの製品企画を推進している。 「事業のためなら何でもやる」──異なるキャリアからContract OneのPdMへ ──これまでのキャリアについて教えてください。 午菴:はじめは広告代理店で営業をしていたのですが、お客様と深く向き合う中で「作って届けるところまで関わりたい」という気持ちが強くなり、デザイナーに転身しました。 そこでさまざまなクライアントの案件に携わるうちに、今度は一つのプロダクトに長期的に向き合いたいという思いが生まれ、事業会社への転職を考えていたタイミングでSansanを知りました。個人的にも名刺アプリ「Eight」を使っていて、そこで出会った人との縁が仕事につながった経験もあったので、以前からSansanのミッションには共感していました。Sansanへ入社後はデザイナーとしていくつかのプロダクトに携わり、約2年前からContract OneのPdMを務めています。異動のきっかけは、事業責任者の尾花から「PdMをやってみないか」と声をかけてもらったことでした。 ──PdMをやってみることに抵抗はなかったのですか。 午菴:全くありませんでした。私の働くモチベーションは、事業への貢献を実感できることにあります。ユーザーに良い体験を届けたり、身近なチームメンバーの助けになったり、形はさまざまですが、自分の働きが誰かの役に立っている瞬間が好きなんです。なので、「デザインじゃないといけない」というこだわりは特になかったですね。ちょうどPdMが足りていなかったこともあり、「事業のためなら何でもやります」という気持ちで引き受けました。今振り返ると、尾花が評価してくれていたのも、デザイナーとしての専門性以上に、そのスタンスだったのだと思います。 ──北野さんのこれまでのキャリアについても教えてください。 北野:大学院でシステム工学を専攻したのち、新卒入社した外資系の計測機器メーカーではエンジニアとして技術サポートや研修講師の仕事をしていました。 当時、仕事に一定のやりがいは感じていましたが、「新しい事業やプロダクトを作る挑戦がしたい」という思いが次第に強くなり、ご縁のあった創業期のSaaSスタートアップに転職しました。 そのスタートアップでは最初カスタマーサクセスの立ち上げに関わり、その後マーケティングも兼務するようになりました。そうした折に社内で新規プロダクトを立ち上げる話が持ち上がり、「お客様のことをよく知っているし、技術系のバックグラウンドもあるから」と声をかけてもらってPdMに転身しました。そこからはプロダクト開発の仕事を続けています。 ──Sansanに入社したのはどういった経緯ですか? 北野: 私は、複雑な業務を解きほぐして、そこから得られたマニアックなインサイトをもとに課題を解決する、というプロセスがすごく好きで(笑)前職でも自動車業界特有の複雑な商習慣や帳票構造を読み解いて、得られた知見をお客様への活用提案やコンテンツ作成につなげることや、汎用的な機能としてプロダクトに落とし込むようなことをやっていたんです。そうした経験やスキルが生かせる次のチャレンジを探していたときにContract Oneに出会い、「契約書」という領域が面白いと思いました。 会社ごと、業界ごとに扱う契約書は大きく異なります。そこには各社・各業界固有の商慣習や取引の知恵が埋もれていて、まだ体系的な知見として整理されていない領域がたくさんありそうだと感じました。もう一つは、Sansanという会社自体への興味もありました。 以前からSansan社員の外部発信を見ていたので、PdMとして成長できる環境があるという印象を持っていましたし、Bill Oneの目覚ましい成長を見ていたので、Sansanが持つ組織的な営業の強さは確信していました。一定の顧客基盤と営業組織が整った会社の中で、これから伸ばそうとしている新規事業領域で働けば、自分が作ったものが社会により大きなインパクトで届くはずだという期待もありました。 AIで開発スピードは3倍に。Contract Oneで起きている変化 ──現在携わっているContract Oneとは、どのようなプロダクトなのでしょうか? 午菴:Contract Oneは、契約書・発注書・見積書など、ビジネスで発生するあらゆる取引書類をデータ化・構造化して管理・活用できる取引管理サービスです。紙・電子を問わず高精度でデータ化し、契約書になじみがない方でも取引の条件や変遷を一目で把握してビジネスに生かせる環境を提供しています。 直近ではMCPサーバーの提供を開始するなど、契約データベースと生成AIを接続した新たな価値提供にも積極的に取り組んでいます。 jp.corp-sansan.com ──Contract Oneの企画が生まれるプロセスを教えてください。 午菴:いくつかパターンがありますが、まずは「ユーザーフィードバック」ですね。Sansanには、プロダクトごとにユーザーからのフィードバックを集約したSlackチャンネルがあり、お客様と接するフロントメンバーが、ヒアリングした要望や課題をSlackチャンネルに投稿してくれる仕組みがあります。 私たちPdMも常に目を通しているので、すぐに対応できそうなものは即座に企画として動かしています。次に、新規顧客の提案ベースで生まれるもの。「この機能がないから導入に踏み切れない」という場面で、クイックに意思決定して実装するというケースが結構あります。そして、将来的なプロダクトの方向性から逆算して「これをやるべきだ」という私たち自身の意志として考えるもの。あとは、ビジネス側からだけでなく、エンジニアから提案が上がることもよくあります。具体的には、パフォーマンス改善や将来の発展性を見据えた技術的な提案もありますし、ユーザーフィードバックを見て、「これすぐ対応できますよ」と自主的に拾い、課題や要件を整理してくれることもあります。そうした声を受けて、優先度を柔軟に組み替えることも多いですね。 ──AI活用によってプロセスはどう変わりましたか? 午菴:企画プロセスの大きな流れは変わっていませんが、各ステップのスピードと精度が上がりました。 チームではClaude Codeを活用していて、議事録や社内ドキュメント、Slack、Salesforceなどあらゆる情報を参照しながら企画検討を進められるようになりました。提案資料やプロトタイプをクイックに作ってお客様に提案し、フィードバックをもとに修正してまた見せる——このサイクルが劇的に速くなっています。プロトタイプを作りながら企画を詰めていくので、企画が固まる頃にはある程度の体験も出来上がっている。結果として、新機能を届けるまでのスピードも上がってきていると感じます。実際の開発スピードも体感で2〜3倍くらいになっています。 以前は限られたリソースの中で「どれを優先するか」を必死に議論していましたが、今はAIによって開発スピードが劇的に上がり、「何を諦めるか」ではなく「いかに早く次の企画を生み出すか」という前向きな議論に変わりました。 ──以前別のインタビューで「簡単なUI改善ならPdMが自ら実装してリリースしている」と伺ったのですが、本当ですか? 北野: はい、こちらの記事でも触れていますが、エンジニアだけでなく、PdMやデザイナーも影響範囲の限られた改善であれば自ら実装・リリースできるようにしています。AIを活用しながら、必要なチェックを経ればリリースできるルールを設けています。 PdMやデザイナーが自ら実装・リリースできるようになったことで、それまで開発リソースの都合で優先度を上げきれずにいたユーザビリティの課題をどんどん解消できるようになりました。そうした改善が進んだ結果、以前と比べてお客様からいただくフィードバックの純度が上がってきた感覚があります。表面的な問題が解消され、より本質的な課題にフォーカスが当たるようになってきた。 そうすると「事業を伸ばすために次に何をやるべきか」についての認識が、チーム内で自然と揃ってくるんです。これはAIを活用する前では得られなかった感覚で、興味深い発見でしたね。 エンジニアも法務も。“職種を越えて”プロダクトを作る ──エンジニア組織との距離感はいかがですか? 午菴:エンジニアとの距離はかなり近いです。毎日のように誰かが私たちの席の隣に来て、気軽に話せる環境ですね。企画の早い段階からアーキテクトやエンジニアに入ってもらい、事業の状況や数字を共有しながら方針について議論しています。 だからこそ、技術負債の解消や将来の発展性を見据えた提案はもちろん、ユーザーフィードバックを拾って課題や要件を整理してくれることもあります。あとは、ミーティングの録画や議事録は全員がいつでも参照できる状態になっているので、自然と状況を把握して提案や相談をしてくれたり、ときには「これってどんな状況ですか?」という一言をきっかけに案件が動き出すこともあります。北野:入社して感じたのは、エンジニアがとにかく優秀だということです。技術だけに閉じず、事業がどうすれば伸びるかに強い関心を持っている。内部品質と事業インパクトのトレードオフが生じる場面でも、高いレベルでバランスよく判断している印象があります。新卒メンバーでもそういう視点で話をしているので、最初は驚きました。 ──職種を越えた連携という点では、法務担当との協業もユニークだと伺いました。 午菴:はい。法務担当のメンバーがドメインエキスパートとしてPdMのように企画から入り、さらに自らお客様の商談に同席して、熱量を持って提案することもあります。 AI時代に法務の業務がどう変わっていくべきかを自ら検証し、最前線で動いているんです。こうした職種の垣根を越えた連携は、Contract Oneの大きな強みですね。 「プロダクトで成果を出す」がPdMのミッション ──Contract OneにおけるPdMの役割はどう定義されていますか? 午菴:以前、チームで「ミッションを言語化しよう」と話したときに、「プロダクトで成果を出す」という言葉になったんです。つまり、プロダクトで成果が出ることなら何でもやる、ということだと思っています。 企画をして、デリバリーのプロジェクトマネジメントをするのはもちろんですが、商談への同席も、展示会への参加も、社内で困っていることへの壁打ちも、デザインもやる。それぞれの得意なことを生かしながら、必要なことを必要なときにやっていくイメージです。北野: 実際、商談に同席する機会は多いですね。企画の方向性を考えるときにお客様の反応を直接見たいという目的で参加することもありますし、営業のメンバーから「提案に同席してほしい」と声がかかることもあります。 PdMの役割がきっちり定義されていないからこそ、そういう関係性が自然に生まれているのだと思います。私自身、お客様との接点が多い方が仕事しやすいタイプなので、こういう環境はとてもありがたいなと感じます。 ──AIが普及した時代に、PdMが担うべき価値とは何だと思いますか? 午菴:AIの普及により判断材料が増え、スピードも上がりますが、どこに向かうかの意思決定は人の役割ではないでしょうか。 また、AIを活用することで生まれた時間を使って、一次情報をより取りにいけるようにもなりましたね。商談への同席もそのひとつで、そこでしか得られない
こんにちは。 大学の春休みにPlatform Engineering Unit Identity Platformグループでインターンをしていた河内です。 今回のインターンでは、Sansan株式会社の複数プロダクトに認証・認可を提供している共通認証基盤「Auth One」へRefresh Token Rotationの追加に取り組みました。 背景となるプロダクト側のMCP提供については、次のリリースも参照ください。 Contract One、MCPサーバーの提供を開始(プレスリリース) Refresh Token Rotation自体は、OAuth/OIDCに関わる人にとっては比較的知られた考え方かもしれません。一方で、実際にプロダクトの共通認証基盤へ組み込むには、仕様、既存実装との互換性、セキュリティー、UX、運用時の観測性を同時に考える必要がありました。 本記事では実装の細部ではなく、導入にあたって行った設計判断を中心にまとめます。 Auth Oneとは Auth Oneは、Bill OneやContract Oneなどのプロダクトに対して認証・認可を提供する共通認証基盤です。 OAuth/OIDCプロバイダーとして、パスワード認証、SSO、API認可などを提供しています。もともとはAuth0のコスト課題を契機に、Amazon Cognitoとサーバーレスアーキテクチャを中心に内製化された基盤です。 Amazon Cognitoや利用しているライブラリで要件を満たせない部分については、Auth One側で追加実装しています。 参考:Sansanの認証基盤を支えるアーキテクチャとその振り返り (Speaker Deck) なぜRefresh Token Rotationが必要だったのか 背景はMCP(Model Context Protocol)対応です。 Sansanでも、MCPサーバーを通じてプロダクトの機能やデータをAIアプリケーションやAIエージェントから利用できるようにする取り組みが進んでいます。このときAuth OneはMCPサーバーのOAuth認可基盤として利用されます。 MCPクライアントがMCPサーバーを継続的に利用するには、認可後もAccess Tokenを更新し続ける必要があります。そこでRefresh Tokenを利用しますが、MCPクライアントの形態上、Refresh Tokenを安全に扱ううえでいくつか課題があります。 今回想定したMCPクライアント(ユーザー環境で動作する形態)ではClient Secretを安全に保持できないため、 Public Clientとして扱う必要がある 一般にPublic ClientでRefresh Tokenを使う場合、漏えい時の不正利用リスクを下げるために (1) Sender-constrained(DPoPなど)とする、または (2) Refresh Token Rotationを行う、といった対策が必要。Auth OneはDPoPを実装済みだが、2026/05現在のMCP仕様ではDPoPが採用されておらず、MCPクライアントでは (1) を前提にできない。 Roadmap: https://modelcontextprotocol.io/development/roadmap#on-the-horizon SEP-1932(DPoP): https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1932 特にPublic Clientでは端末内での保護に限界があり、さらにDPoPなどでトークンを特定クライアント(鍵)に束縛(sender-constrain)できない場合、Refresh Tokenが単体で漏えいしただけで第三者による継続的な不正利用につながり得ます。 この条件下でRefresh Token漏えい時の影響範囲を抑えるため、Refresh Token Rotationを導入する必要がありました。 Refresh Token Rotationと、その単純実装で起きる問題 Refresh Token Rotationは、Refresh Tokenを使ってAccess Tokenを更新するたびに、Refresh Token自体も新しいものへ入れ替える仕組みです。 通常のRefresh Tokenでは、漏えいしたトークンが有効期限まで使われ続ける可能性があります。一方、Refresh Token Rotationでは、古いRefresh Tokenを使った時点で新しいRefresh Tokenへ入れ替えるため、漏えいしたRefresh Tokenが使われ続けるリスクを抑えやすくなります。 ただし、単純に旧Refresh Tokenを失効し、新Refresh Tokenを発行するだけだと、クライアントが不必要に再ログインを求められる状況が起こりえます。 代表例は次の2つです。 レスポンス未達によるトークン喪失 サーバー側では旧Refresh Tokenを無効化し、新Refresh Tokenを発行したにもかかわらず、ネットワークエラーなどでレスポンスがクライアントへ届かない場合、クライアントは旧Refresh Tokenしか保持していません。 その結果、次回以降の更新が失敗し、再ログインが必要になることがあります。 同時リフレッシュによる競合 同じRefresh Tokenを使ってほぼ同時にリフレッシュが走ると、一方が成功してローテーションされた後、もう一方は旧Refresh Tokenでのリクエストになり失敗しうるため、再ログインにつながることがあります。 この正しく更新しているのに再ログインとなる問題をどう吸収するかが、設計上の主要な論点でした。 設計判断1: Grace Period方式か、冪等方式か Refresh Token Rotationの実装方針はRFCで細かく規定されているわけではないため、既存の認証基盤・サービスの実装方針を調査しました。 調査した範囲では、レスポンス未達や同時リフレッシュへの対処は大きく2つに分かれました。 方式 概要 特徴 Grace Period方式 ローテーション後も、旧Refresh Tokenを一定時間受け付ける 実装は比較的素直だが、同じ旧Refresh Tokenから複数の新Refresh Tokenが発行されうる 冪等方式 同じ旧Refresh Tokenに対するリクエストには、同じ新Refresh Tokenを返す 有効なRefresh Tokenを常に 1 つに保ちやすいが、新Refresh Tokenを一時保存する必要がある Auth Oneでは冪等方式を選んだ Auth Oneでは最終的に冪等方式を選びました。 なお冪等方式では、同じ旧Refresh Tokenに対して同じ新Refresh Tokenを返すことで再送や同時リフレッシュによる競合を吸収しますが、無期限に同一応答を保証するのではなく、一定期間に限って同一応答を返す設計にすることが多いです。本記事ではこの期間を「冪等Window」と呼びます。 冪等方式を選んだ理由は次の2つです。 トークン管理を単純に保ちやすい Grace Period方式では、同時リフレッシュ時などにRefresh Tokenが分岐します。そのため、有効なRefresh Tokenの系列が木構造のように増えていき、管理が複雑になります。 冪等方式では、同じ旧Refresh Tokenに対して同じ新Refresh Tokenを返すため、分岐を避けやすくなります。 冪等Window内の再リクエストでDB書き込みを避けやすい 再リクエスト時にキャッシュから同じ新Refresh Tokenを返せれば、毎回のDB書き込みを伴わずに処理できます。 設計判断2: 新Refresh Tokenをどう安全に一時保存するか 冪等方式では、同じ旧Refresh Tokenに対して同じ新Refresh Tokenを返すために、新Refresh Tokenを一時的に保存する必要があります。 ここで問題になるのは、Refresh Tokenが長寿命であり、新しいAccess Tokenを発行する起点になる重要な値だという点です。 Auth Oneの既存実装では、Refresh Tokenの値そのものは保存せず、一致確認用のハッシュのみを保存していました。これは、Refresh Tokenの漏えいリスクを下げるためです。 そのため、冪等方式のためだけに新Refresh Tokenを平文でキャッシュする設計は避けたい、という前提がありました。 検討した選択肢は、主に次のようなものです。 選択肢 概要 懸念 平文で保存する 透過的データ暗号化に任せ、Valkey には平文で保存する アプリケーションからはRefresh Tokenを平文で扱える 共通鍵で暗号化する アプリケーション共通の鍵で新Refresh Tokenを暗号化する 鍵漏えい時の影響範囲が大きい。追加で鍵管理する必要がある。 KMSでエンベロープ暗号化する KMS を使ってデータキーを保護する レイテンシやコストが増える 旧Refresh Token由来の鍵で暗号化する 旧Refresh Tokenから鍵を導出し、新Refresh Tokenを暗号化する 鍵導出の安全性(用途の分離や漏えい時の影響)を設計で担保する必要がある HMACなどで導出する 保存ではなく導出で対応する 前方秘匿性などの観点で課題がある この中で採用したのが、旧Refresh Tokenから鍵を導出し、その鍵で新Refresh Tokenを暗号化して保存する方式です。 旧Refresh Tokenから鍵を導出して新Refresh Tokenを暗号化する 採用した方式のポイントは、冪等キャッシュヒット時のリクエストには、必ず旧Refresh Tokenが含まれることです。 同じ旧Refresh Tokenに対して同じ新Refresh Tokenを返すには、クライアントが旧Refresh Tokenを再度送ってくる必要があります。つまり、復号に必要な材料である旧Refresh Tokenは、リクエストから毎回得られます。 そこで、旧Refresh TokenからHKDF-SHA256で256bitの対称鍵を導出し(用途分離のためにinfoを設定)、その鍵で新Refresh TokenをAES-256-GCMにより暗号化します。 Valkeyには、新Refresh Tokenの平文ではなく、暗号文、IV、認証タグを保存します。キャッシュキーは旧Refresh Tokenのハッシュを用いて、旧Refresh Tokenを平文で扱う箇所を増やさないようにしました。 なお、この方式が主に想定しているのは、キャッシュ(Valkey)や周辺コンポーネントから暗号文が漏えいした場合の影響を抑えることです。旧Refresh Token自体が漏えいしているケースは別問題であり、その前提ではRefresh Token Rotationや有効期限など別の対策が重要になります。 初回リフレッシュ時の流れは次の通りです。 クライアントが旧Refresh Tokenでリフレッシュを要求する Auth Oneが旧Refresh Tokenを検証する 新Refresh Tokenを生成する 旧Refresh TokenからHKDF-SHA256で鍵を導出する 新Refresh TokenをAES-256-GCMで暗号化する 暗号文、IV、認証タグをValkeyに短時間保存する クライアントへ新Access Tokenと新Refresh Tokenを返す sequenceDiagram participant C as クライアント participant A as Auth One participant D as DB participant V as Valkey C->>A: 1. Refresh Request (旧RT) A->>D: 2. 旧RT 検証・無効化 A->>D: 3. 新RT保存 A->>V: 4. 暗号化キャッシュ設定 A-->>C: 5. 新RT + 新AT 返却 その後、冪等Window内に同じ旧Refresh Tokenで再リクエストが来た場合は次の通りです。 クライアントが同じ旧Refresh Tokenでリフレッシュを要求する Auth OneがValkeyを参照する キャッシュヒットした暗号文を取得する リクエストに含まれる旧Refresh Tokenから同じ鍵を導出する 暗号文を復号し、新Refresh Tokenを取得する 同じ新Refresh Tokenと、新たに生成したAccess Tokenを返す sequenceDiagram participant C as クライアント participant A as Auth One participant V as Valkey C->>A: 1. Refresh Request (旧RT) A->>V: 2. Valkeyキャッシュ検索 → HIT A->>A: 3. HKDF(旧RT)で復号 <br> → 新RT取得 + 新AT再生成 A-->>C: 4. 同じ新RT + 新AT 返却 この方式では、Valkeyに新Refresh Tokenの平文を保存しません。また、復号用の共通鍵を別途永続管理する必要もありません。 情報セキュリティ部レビューとパラメータ設計 Refresh Tokenは認証基盤において重要な値です。そのため、設計を確定する前に情報セキュリティ部へ相談しました。 相談時には、次のような点を整理しました。 なぜRefresh Token Rotationが必要なのか なぜ冪等方式を選ぶのか 新Refresh Tokenをどこに、どの形式で保存するのか 旧Refresh Token由来の鍵で暗号化する方式にどのようなリスクがあるか 想定する攻撃や漏えい時の影響範囲 各種有効期限やWindowをどう設定するか また、パラメータは最初から最適値を決め打ちするのではなく、観測しながら調整できるようにしました。 これらの値は、セキュリティーとUXの両方に影響します。期間を短くすれば、漏えい時の影響範囲は小さくなります。一方で、短すぎるとクライアントが頻繁に再認証を求められ、UXが悪化します。逆に期間を長くすればUXは改善しますが、漏えい時のリスクは大きくなります。 実装と検証 実装では、主に次の作業をしました。 DB設計とマイグレーション Protocol Buffersでのスキーマ設計 内部向けAPI対応 Refresh Token Rotationのコアロジック 暗号化・復号ロジック E2Eテスト OpenTelemetryメトリクス k6による負荷テスト 実MCPクライアントでの動作確認 検証では、主要サービス3種のMCPクライアントを使い、Product側のMCPサーバーへ接続しました。 MCPクライアントがMCPサーバーへ接続し、Auth Oneを通じてOAuth認可とトークンの更新を行います。MCPサーバー側では取得したJWTを検証してサービス環境へアクセスします。 検証の結果、3種類のクライアントで認証・接続が成功し、定期的なRefresh Token更新を通じて継続利用できることを確認しました。 また、テスト・観測面では次の観点を確認しました。 種別 確認したこと ユニットテスト 暗号化・復号、ローテーション処理 E2Eテスト Refresh Tokenの連続ローテーション、冪等性 負荷テスト 想定されるリフレッシュ頻度での挙動 メトリクス キャッシュヒット、ローテーション結果、Refresh Tokenの経過秒数、セッション全体の経過秒数 実クライアント検証 ChatGPT、Claude Desktop、Copilot Studioでの接続 やりきれなかったことと今後 今回のインターン期間では、Refresh Token Rotationの基本的な設計、実装、テスト、実クライアント検証まで進めることができました。 一方で、今後取り組むべきことも残っています。 Refresh Token Rotationの適用範囲の拡大 管理画面からRefresh Token Rotationを有効化できるようにする対応 実際の利用状況を見ながらの各種パラメータ調整 おわりに 今回の取り組みでは、Refresh Token Rotationという一見よく知られた機能であっても、実際にプロダクトの共通認証基盤へ組み込むには多くの判断が必要だと感じました。 特に、MCPクライアントのようにPublic Clientであり、かつDPoPを前提にできないクライアントでは、仕様上の要件、セキュリティー、UX、既存基盤との互換性を同時に考える必要があります。 また、OAuth/OIDC周りでは、仕様に明確に書かれている部分と、実装側で判断しなければならない部分があります。今回のRefresh Token Rotationもまさにそのような領域でした。「銀の弾丸」はなく、複数の選択肢のPros/Consを比較しながら、その時点の要件に対して最も妥当な方式を選ぶことが重要だと学びました。 最後に
複数のシステムに散在するデータ、表記揺れや重複、更新漏れ。CRMとSFAで異なる情報が登録され、どれが正しいのか分からない。 こうした「データの信頼性」という構造的課題を解決するために生まれたのが、データクオリティマネジメント「Sansan Data Intelligence」です。Sansan Data Intelligenceは、Sansanが培ってきた名寄せ技術とSansan Organization Code(SOC)を活用し、企業が保有する取引先マスターのクオリティー向上を一気通貫で実現するプロダクトです。 2025年12月にリリースされ、現在はさらなるプロダクトの進化に向けた開発が進んでいます。今回は、Sansan Data Intelligenceに携わる3名——プロダクト戦略を統括する猿田、エンジニアリング組織を率いる多賀谷、そしてビジネスサイドからプロダクトマーケティングを担う鳴海に、Sansan Data Intelligenceの現在地と、この事業を一緒につくる仲間に求めるものを聞きました。 猿田 貴之(Sansan事業部 SDIプロダクト室 室長)Sansan Data Intelligenceプロダクト全体の方向性を統括し、プロダクトマネジャー・データサイエンティスト・デザイナーのマネジメントを担当。Sansan Data Intelligenceの立ち上げ前から一貫して携わり、2025年12月のリリースを経て、さらなるプロダクトの進化をリードしている。 多賀谷 洋一(技術本部 Data Intelligence Engineering Unit 部長)2025年12月のSansan Data Intelligenceリリースタイミングで入社。エンジニアがプロダクトマーケットフィットに集中できる体制構築や、より大きな役割を定義して任せる環境づくりを推進している。 鳴海 佑紀(Sansan事業部 Sansan DI推進部 副部長)Sansan Data Intelligence推進部にて営業・マーケティング・カスタマーサポートを統括。プロダクトマーケティングマネージャー(PMM)として、プロダクトをマーケットに届けるための全体設計と戦略策定を行う。Sansan Data Intelligenceの必要性を1年近く提唱し続け、立ち上げに至った経緯を持つ。 Sansan Data Intelligenceとは何か ──まず、Sansan Data Intelligenceを一言で説明するとどういったプロダクトですか? 猿田:一言で言うと、お客様が持っている取引先マスターのクオリティマネジメントを実現するプロダクトです。もともとSansanには「Sansan Data Hub」という機能があります。これは名刺を起点に、集まったデータをCRMやSFAに流し込んで営業活動に使うというもので、主なユーザーは営業担当者です。一方でSansan Data Intelligenceが見ているお客様は、その手前で困っている人たち──情シス部門やDX部門、営業企画といった、全社のデータガバナンスを担う方々です。お客様の社内では、複数のシステムに同じ会社のデータがバラバラに登録されていて、表記揺れや重複、更新漏れが起きています。そのカオスな状態を、Sansanが持つ名寄せ技術とSOC(Sansan Organization Code/企業や事業所に対して付与される一意のコード)で整理し、クレンジングした上で足りない属性情報を補完する。 これを一気通貫で提供するのがSansan Data Intelligenceです。Sansan Data Hubが「名刺起点で営業をさらに強くする」プロダクトだとすれば、Sansan Data Intelligenceは「取引先マスター全体の営業DXの土台を作り、作り続ける」プロダクトになりますね。 顧客が抱えるデータ課題のリアル ──ビジネスの現場では、顧客のデータ課題はどのような形で見えていますか? 鳴海: 特に多いのは、情報システム部門と営業企画・事業部門の方々からの相談です。前者は「データをクリーンに保ち続けたい」、後者は「データを使って事業戦略を考えたい」という欲求を持っていて、共通しているのは「社内のシステムにあるデータが全然信じられない」ということです。例えばCRMには昔の営業担当が登録した取引先情報が入っていて、SFAを見ると別の表記で書いてある。当社で言えば、創業時は漢字の「三三」で、今はアルファベットの「Sansan」ですよね。当時取引した会社のシステムには漢字の「三三」が残っているはず。その情報は間違ってはいないけれど、今は古い。こういうことが事業部門ごとのシステムで起きていて、もう何が正しいか分からない状態になっています。あるお客様は保有データが270万件あり、毎月5,000件ずつ増えていきます。こうなると、ショットで綺麗にすること自体がもう絶対に不可能ですし、次の日からデータは汚れていく。だからこそ、データそのものを継続的に維持する仕組みが必要なんです。さらに外部環境としてAI活用が求められる中で、「来月にはデータが綺麗になります」とか「ショットクレンジング頑張ります」では間に合わない。待ったなしのスピード感で、構造的に解決し続けるプロダクトが必要とされています。 Sansan Data Intelligenceのロードマップとビジョン ──プロダクトとして、半年から1年後にどういう世界を実現したいのでしょうか。 猿田: 短期的には鳴海が言った通りで、お客様の社内に散らばっているデータがちゃんと使える状態になり続けていることを「当たり前」にしたい。具体的には、SOCとその裏側にあるデータが今、570万法人・1,000万事業所をカバーできる規模に拡大しています。これをさらに増やしていくことで、「うちのデータでは識別できない」と言われていたお客様にも応えられるようになってきています。1年後の話をするともう一段やりたいことがあって、それは「AIが使えるデータの供給レイヤー」として位置づけられることです。皆さんがAI活用でいちばんつまずくのは、AIモデルの性能そのものではなく、AIに食わせるデータが揃っていないということだと思います。社内のデータがバラバラだったり、表記揺れしていたり、重複している状態では、どんな優秀なAIがあっても精度の高い回答は返ってこない。Sansan Data Intelligenceはそこを構造的に解決し続けるプロダクトでありたい。クレンジングされて、識別コードが振られて、会社情報や事業所情報がひも付いている—いわゆるAI-Readyなデータを、APIやMCPのような形で各社のシステムやAI環境に供給していく。「まずはSansan Data Intelligenceを通してデータ活用しよう」と思ってもらえる存在を目指しています。まさにSansanが掲げる「ビジネスインフラになる」というビジョンを実現する、インフラ側のプロダクトです。 エンジニアのマインドセットと技術的挑戦 ──プロダクトを支えるエンジニアチームは、どういうマインドセットで向き合っているのでしょうか。 多賀谷:大前提として、これは新規プロダクトであり、プロダクトマーケットフィット (PMF) させることが最優先です。だからエンジニアも、お客様や営業組織、カスタマーサクセス、プロダクトマネジメントといった他部門に積極的に染み出していって、一緒にPMFさせるという姿勢が重要です。技術的には、プロダクトのコアバリューを作る領域をしっかり見極めて丁寧に設計・実装しつつ、逆に既存システムがある部分は早く作れるアーキテクチャで進めようといった意思決定をしていく。そのバランス感覚がエンジニアのマインドセットとして大事なところですね。 ──ビジネス要件が技術そのものを変えた場面はありましたか? 多賀谷: いちばん重要なのは「SOC v2」と呼んでいるデータアーキテクチャです。EntityとRelationshipという非常にシンプルな構造で、過去の履歴も含めた企業や事業所のデータ構造を表現できる。シンプルでありながら柔軟性が高い設計になっていて、ここがビジネスの競争優位性になっています。そのデザインを提案したドキュメントを見るだけでワクワクするような、そういうものですね。既存のSansan Data Hubのシステムもありながら、新しく作るところは完全に新しく、モダンなアーキテクチャになっていて、それが併存している。このビジネス要件と既存要件が技術判断に与えた影響は大きいです。 プロダクト・エンジニアリング・ビジネスの連携 ──エンジニアリングとプロダクト開発の連携は、具体的にどのように成り立っているのでしょうか。 猿田:Sansan Data Intelligenceでは、プロダクトマネジャーが所属するプロダクト側と、多賀谷率いるエンジニアチームで開発体制を組んでいます。大きな価値提供の単位としてEpicを定義し、その中にユーザーストーリーがあり、さらにその下にPBI(プロダクトバックログアイテム)がある。これを、基本的にエンジニアとPdMが一緒に作っていく運用です。単に「PdMが要件を書いて投げて、あとはエンジニアが実装する」という分業ではなく、Epicという大きな価値のレベルから一緒にリファインメントしていくのが特徴です。SOC v2のような技術的な設計議論にもPdMが入っていって、プロダクトの競争優位に関わる議論を一緒にしています。同時に、短期的な事業要求もすごく高いので、その両立をどう図るかは鳴海、多賀谷と一緒に常に議論しているところですね。SOC v2やSansan Data Intelligenceのアーキテクチャという大きなピクチャーを描きながらも、既存のSansan Data Hubのお客様をも新しい世界にお連れしたい。事業の計画とエンジニアリングの計画を綿密にすり合わせる必要があるので、そこはかなり難しいですが、やりがいのある部分です。多賀谷:別の観点で補足すると、エンジニアに対しては「シフトレフト」の考え方を推進しています。もともとはQAやセキュリティーの文脈で使われる言葉ですが、自分よりも前工程の方にどんどん染み出していこうと。さらに、Sansanが評価の仕組みとして採用している「ミッショングレード制度」では、グレードが上がるほど影響範囲を広げることが期待されています。 グループレベル、部署レベル、あるいは事業目線──次のグレードを目指して、より大きな範囲の仕事を任せ、その期待レベルで仕事をしていくように意識づけをしています。それによってエンジニア、プロダクト、営業組織が三位一体となって仕事できるマインドセットや環境を醸成しています。 部門間連携がもたらす効果 ──ビジネスサイドから見て、この連携体制にはどのようなメリットがありますか? 鳴海:お客様に対する説明の解像度がぐっと高まるというのは、営業もCSもみんな感じていると思います。データ系のプロダクトなので、お客様からの質問が深いんですよ。「なぜこのデータを識別できないのか」「識別要件は何か」「データの更新頻度はどうか」──こういう技術的な質問を毎回持ち帰ってしまうと商談は長引くし、お客様の熱も冷めてしまう。でも当社のエンジニアの特徴として、現場から「なぜこの人はそういう質問をしたんだろう」と背景を聞いてくれる方が多い。コンテキストを間違えずにやりとりができるので、営業もその解像度で話せるようになります。 実際、社内でもトップ営業の人間はSansan Data Intelligenceやデータマネジメントに関して「すごく詳しいね」とお客様からコメントをもらえたりする。それはやはり、部門を超えて近い距離で協働しているからこそだと思います。あとは、部門を超えたマネジャー会議を毎週やっていたり、四半期に1回「ざっくばらんの会」と称して、エンジニアからビジネスサイドまで全員集まってピザを食べながら話す場を設けていたり。そういうマインド醸成も含めて、みんな前のめりで参加してくれるのはありがたいですね。 Sansan Data Intelligenceにどんな人が来てほしいか ──Sansan Data Intelligenceにどんな人に来てほしいか、それぞれの視点から聞かせてください。 多賀谷: まずは、新規事業を一緒に作るというマインドを持ったエンジニアです。ただ、新しいものを作るだけでいいかというと、そうではない。裏側はデータプロダクトなので、技術的に難しい領域でもあります。その高い技術水準にもチャレンジしつつ、新規事業としてビジネスも成立させる──その両立を目指せるエンジニアと一緒に働きたいですね。鳴海:お客様の業務やペイン──つまり痛みそのものに興味を持ってくれるかどうかが大事だと思っています。自分が企画するもの、自分が作るものが、お客様の現場の何を変えるのかを自分の言葉で語ってくれる人がいると、こちらも相談しやすいし、もっと情報を取ってこようという気持ちになります。もう一つ、新規事業なので「言われたから作ります」という受け身の姿勢だとスピード感が追いつかない。「これが必要だよね。これができたなら、次はこれも必要だよね」と、連鎖的に思考を巡らせてくれる方が入ってくれると、間違いなくフィットすると思います。猿田:補足するなら、「How」に
この記事は、Sansan Data Intelligence開発Unit ブログリレーの最終回(Vol.17)です。 Data Intelligence Engineering Unitの部長を務める多賀谷洋一です。 データクオリティマネジメント「Sansan Data Intelligence」をリリースして、半年が経ちました。半年間を通じて確信したのは、このプロダクトは変化の激しいAI時代の勝負どころに立っているということです。NVIDIA CEOのJensen Huang氏が「経済価値が生まれる場所」と呼ぶアプリケーションレイヤー、ここで勝つ条件は、独自データ・ドメイン知識・顧客基盤の3つ。Sansanはその3つを揃え、今まさに最前線に立っています。リリースから半年で事業は急速に立ち上がり、チームには確かな手応えが生まれています。今回はブログリレー最終回として、今このモーメンタムの中で働く魅力をお伝えします。 AI時代の「勝負どころ」に立つプロダクト Jensen Huang氏は、AI産業を5層のレイヤー構造として描いています。下からエネルギー、チップ、インフラ、ファウンデーションモデル、そして一番上にはアプリケーション。 下の4つのレイヤーはいずれも、参入に莫大な投資を要します。年間60兆円超が動くAIデータセンター、数千億円規模の学習コスト——資本の規模がそのまま競争力になる世界です。今から新たなプレイヤーが競争優位を築くのは困難です。 しかし、アプリケーションレイヤーは違います。顧客への価値を決めるのは資本の大きさではなく、独自のデータとドメイン知識です。 ただし、ここにも落とし穴があります。 Anthropicは今、ファウンデーションモデルを提供するだけでなく、Claude CodeやCoworkなどアプリケーション層へと大胆に事業を拡大しつつあります。つまり「単なるエージェント」を作るだけでは、モデルプロバイダー自体がとてつもなく強い競合になります。 Sequoia CapitalのJulien Bek氏は、この構造を次のように捉えています。 If you sell the tool, you're in a race against the model. But if you sell the work, every improvement in the model makes your service faster, cheaper, and harder to compete with. Services: The New Software By Julien Bek - Published March 5, 2026 (翻訳)ツールを売るなら、モデルとの競争を余儀なくされる。しかし仕事の成果を売るなら、モデルが進化するたびにあなたのサービスは速く、安く、競合に強くなる。 顧客に価値を届け続けるには、ツールではなく成果を提供できる立場に立つこと。そのためには、代替不可能な土台が必要です。 その条件は3つに収束します。独自データ(他社が真似できない独自のデータベース)、ドメイン知識(業界・業務の深い理解を体現したプロダクト)、そして顧客基盤(提供した成果に対して価値あるフィードバックして共創してくださる顧客)。 Sansanには、この3つが揃っています。そしてSansan Data Intelligenceは、まさにAI時代に顧客へ真の価値を届けていくプロダクトだと言えます。 なぜなら、AIエージェントがアプリケーション層で真の価値を生むには、自律的に判断・行動できる状態が必要であり、そのためには常に正しい情報を参照できる状態が不可欠だからです。 しかし、企業のデータ管理の現実はこうです。企業は数十のシステムにまたがるデータを保有し、同じ取引先を「A株式会社」「A(株)」「A社」と別々の名前や異なるIDで参照しています。AIエージェントが「A社の与信状況を確認して」と指示されても、同一企業を正確に結びつけられなければ、エージェントは間違った判断を下します。どれだけ高度なモデルを使っていても、データが正しくなければ正しい結果は出ません。 この問題を解決するのが、企業や事業所を一意の識別子(SOC: Sansan Organization Code)で管理し、常に正確で最新の状態に保つSansan Data Intelligenceです。このプロダクトは、その基盤となるマスターデータにおいてあらゆる企業や事業所に識別子であるSOCを付与します。個人に対するマイナンバーがあることで行政システムが連携して個人へのサービスを提供できるように、SOCがあることで複数システムを跨いで正しく企業や事業所を特定して連携させることができます。これにより、AIエージェントが目的とする企業や事業所を異なるシステムにおいて正確に特定し、そのリッチデータを取得し、正しく判断して自律的に業務を実行できます。 このように、Sansan Data Intelligenceは、Sansanだからこそつくることができる「AI時代の基盤」となるのです。 データクオリティマネジメント「Sansan Data Intelligence」 Sansan Data Intelligenceは、2025年12月にリリースされた「データクオリティマネジメント」サービスです。Sansanとして約4年ぶりの新規プロダクトで、構想からリリースまでわずか半年で実現しました。 企業が抱える取引先データの品質問題は深刻です。「重複・表記ゆれ・更新漏れ」を経験している企業は約8割にのぼります。(*1) この問題は、近年急速に広がるAI活用にも直撃しています。AI導入企業の約9割が「期待通りの精度が出ない」と報告していますが、その根本にあるのがGarbage In, Garbage Out——質の低いデータを入力すれば、出力も質の低いものになるという原則です。 具体的には、営業担当者の手入力による「株式会社」と「(株)」の表記ゆれ、企業の移転・合併情報の更新放置、市場全体の未取引企業が可視化されていない状態などが挙げられます。これらはいずれも、誤った情報を元にしたアプローチや商談機会の損失に直結します。 こうした問題は、社員が主導する一時的な名寄せプロジェクトでは解決できません。手作業では対応しきれない量とスピードで、データの劣化は常に進んでいるからです。必要なのは、構造的・継続的なデータ品質の維持です。 Sansan Data Intelligenceは、企業のCRM/SFAや基幹システムに蓄積された取引先データを、Sansanの企業データベース(事業所データ1000万件超)と照合し、4つの価値を継続的に提供します。 識別・正規化:表記ゆれや重複を含むデータを一意に識別して、正しい企業・事業所単位に統合 最新化:移転や合併、社名変更などの情報を自動検知し、常に最新のマスターデータを維持 リッチ化:業種、売上高、従業員数、系列情報など、営業戦略に必要な属性情報を付与 ホワイトスペース可視化:自社データにはない、市場の未接触企業(ホワイトスペース)を可視化し、ターゲティングリストとして提供 これを支えるのが、企業・事業所の識別子であるSOCです。Sansanは10年以上の名寄せ技術の蓄積を経て、この識別子を大規模データに付与する技術を磨いてきました。SOC v2では従来のRDB(Relational Database)型から時系列グラフモデルへと全面移行しました。Entity(Node)とRelationship(Edge)で構成し、全Edgeが有効期間を保持します。最新のスナップショットだけでなく、「2020年4月1日時点ではA社はB社の子会社だった」といった過去時点の関係性まで扱う高い表現力を持つアーキテクチャへと根本から設計し直しました。 既存プロダクト「Sansan Data Hub」が名刺情報を起点としていたのに対し、Sansan Data Intelligenceは企業・事業所のマスターデータを起点としています。名刺がない企業、一度も会ったことのない取引先まで含めて、企業のデータ資産全体を統合管理できます。将来的にはリスクチェック、APIによるあらゆるシステムとの連携強化、グローバル展開へとプラットフォームを拡張していく見込みです。 (参照:Sansan Data Intelligenceリリースに寄せて) 技術的な挑戦と成長環境 Sansan Data Intelligenceを構成する技術スタックは、モダンかつSansanだからこそプロダクション利用できる要素もあり、エンジニアにとって貴重な経験ができる構成になっています。コアデータモデルの設計から、Google Cloudを活用したインフラ、Go言語とそのエコシステムを採用したバックエンド、Next.js(App Router)とTypeScriptを採用したフロントエンド、AIを活用した開発手法まで、各領域に技術的な挑戦や成長環境があります。 コアデータモデル:SOC v2 (参照:ブログリレーVol.03) Sansan Data Intelligenceのデータモデルの中心にあるのは、驚くほどシンプルな構造です。それは、Entity(Node)とRelationship(Edge)、から構成されるグラフモデルです。Sansanではこの新しいデータモデル、そしてそれに基づく識別子をSOC v2(Sansan Organization Code Version 2)と呼んでいます。 企業はNode、企業同士の関係はEdge。「A社はB社の子会社である」「A社はC拠点に所在する」「A社とD社は合併した」——現実世界の複雑な企業情報が、すべてこの2つのプリミティブで表現できます。 さらにこのモデルが強力なのは、全Edgeが有効期間を持つという点です。Edgeは「いつからいつまでその関係が成立していたか」を保持します。つまりこのグラフは現時点のスナップショットだけではなく、時間軸を含んだ企業変遷の完全な記録となります。「2025年3月1日時点でA社はB社の子会社だったか」「この合併はいつ発生したか」——任意の時点の状態を、任意の時点でグラフから取り出すことができます。 名寄せとは集合論的にはEquivalence Classを求めることであり、Graph的に表現することが自然である。 Vol.03 SOCv2: MasterData as a Service (MDaaS) 10年もののSystemを作り替える 集合論に基づいたこのモデルは、Sansanを代表するエンジニアのMakoto Nagaiが設計しました。その設計ドキュメント (Design Doc) を読んだ時に、「NodeとEdge、有効期間のみでこれだけのことが表現できるのか」という驚きと静かな興奮があったことを覚えています。10年以上にわたって企業データの名寄せと向き合い続けたSansanが、最終的にたどり着いたシンプルな構造。複雑な現実を、最小限のプリミティブで過不足なく記述できる。真に良い設計はシンプルで美しい——そう確信させてくれる設計です。 グラフデータベース:Spanner Graph (参照:ブログリレーVol.09) Spanner Graphは Google Cloud Spannerの機能の1つで、グラフ構造のデータに対して専用のクエリ言語(GQL: Graph Query Language)でクエリできます。 「合併・移転・親子関係のつながりを辿る」という、SOC v2に基づくデータ取得はグラフ探索そのものです。だからこそ、Spanner Graphとの相性は必然でした。 通常のSQLではグラフチェーンを辿るクエリは複雑になります。一方でGQLでは、例えば -[:edge]->{0,100} という1行で「エッジを0〜100回辿る」ことを表現できます。「合併や分割というイベントを何段辿っても対象企業を特定する」という処理は、まさにこのGQLが力を発揮する問題です。 ところが、プロダクションの企業データで検証を進めるうちに、Spanner Graphを使い倒しているからこそ気づく特性が見えてきました。Sansanのエンジニアの滑川が突き止めたのは「パフォーマンスの支配的要因はグラフの経路数であって、パスの長さや幅ではない」というものです。実際に検証すると、パスの長さや幅はパフォーマンスへの影響は軽微だったのに対し、経路数は次の表のようにパフォーマンスに大きな影響を与えました。 経路数 応答時間 1,024 355ms 2,048 696ms 32,768 13,733ms さらに、同一経路数であってもグラフの形状によって実行速度が約10倍違うケースがあることも突き止めました。 現在1000万件を超える企業・事業所データ、複雑な資本関係、合併の連鎖。Sansan Data Intelligenceだからこそこの特性の理解が重要です。この検証のような経路数が爆発する企業が出てきた場合には、適度なクエリ分割やSQLとのハイブリッドな運用、ショートカットを作る設計などが求められることになります。 Spanner Graphをこの深さまで実運用で検証しているのはSansanだからこそです。しかも2026年5月末時点では、Spanner GraphはEnterpriseエディションまたはEnterprise Plusエディションでしか使うことができません。Sansanでしか経験できない技術チャレンジはたくさんあり、Spanner Graphはその一つです。 モダンな技術スタック (参照:ブログリレーVol.01, Vol.03, Vol.14) 次の3つの主要システムともにGoogle Cloud上で稼働し、モダンな技術スタックを採用しています。これにより、エンジニアは顧客価値を生むためのシステム設計やプロダクト開発に注力できます。 MasterData System: 組織と組織間の関係性を表す時系列グラフモデル(SOC v2)によるマスターデータ基盤 Application: ユーザー向けのユースケースを提供するフロントエンドとバックエンドマイクロサービス Data Hub: データの識別・統合処理を行うデータパイプライン インフラ GKE(Google Kubernetes Engine)Autopilotベースの社内共通プラットフォーム Orbit を採用。開発・ステージング・本番環境のマニフェスト定義、Secret Manager統合、ArgoCDによるデプロイ管理などを担い、インフラの認知負荷を大幅に削減 Terraformによるインフラ管理(IaC:Infrastructure as Code) Argo CDとGitHub ActionsでCI/CDを整備 Observabilityは OpenTelemetry + Cloud Monitoring / Cloud Trace / Cloud Loggingを採用 認証は社内共通基盤 Auth Oneを採用 データ基盤 メインデータベースにCloud Spannerを採用。前述のように、グラフデータベースもSpanner GraphによりCloud Spannerで完結 MasterData SystemのデータパイプラインはDataflow with Apache Beamで構築し、バッチとストリーミングを透過的に実装可能 バックエンド・API バックエンドにはGo言語を採用。goroutineによる軽量な並行処理がデータパイプラインと高い相性。MasterData SystemとData Hubで言語を統一しており、人材の流動性を高めている バックエンドとフロントエンドのAPI通信は <a href="https://connectrpc.com
Sansan株式会社では、技術イベントや勉強会の主催・登壇・協賛を行っています。 各イベントの詳細については、以下のリンクからご確認ください。 ※開催状況により、すでに受付を終了している場合がございます。 ※掲載している内容は公開当時の情報です。最新情報は各イベントページをご確認ください。 イベント開催情報 2026/06/17(水) h4 a { color: #1487BD !important; text-decoration: none !important; font-weight: 700; } h4 a:hover { color: #0E5F8F !important; } 食べログ x ANDPAD x Sansan モバイル勉強会 #4 食べログ、アンドパッド、Sansanが共催するMobile勉強会です。 今回のテーマは「OS・アップデート」。OS 標準 API の進化や UX 改善の話から苦労話まで、モバイル開発ならではの知見を共有します。 開催日時:2026/06/17(水)19:00 〜 21:30 開催場所:Sansan株式会社 本社 参加方法:https://andpad.connpass.com/event/395841/ 協賛情報 2026/06/08(月)~06/12(金) h4 a { color: #1487BD !important; text-decoration: none !important; font-weight: 700; } h4 a:hover { color: #0E5F8F !important; } 2026年度 人工知能学会全国大会(第40回)(JSAI2026) 2026年度 人工知能学会全国大会(第40回)(JSAI2026)に、Sansan株式会社はプラチナスポンサーとして協賛します。 また、ポスター発表2件と口頭発表1件を行います。Sansanブースもありますので、ぜひお立ち寄りください。 ■ポスター発表 ① RT-QMC: 順序変換を用いた準モンテカルロサンプリングによるロバストな初期学習データ選択 発表者:田柳 俊和 セッション会場:Y会場(展示ホールAB-1) セッション日時:2026年6月8日(月)16:10 ~ 17:40 講演番号:1Yin-B-10 ② スキャン文書画像を用いたページ欠落検出手法の検討 発表者:宮本 優一、吉村 皐亮 セッション会場:Y会場(展示ホールAB-1) セッション日時:2026年6月8日(月)16:10 ~ 17:40 講演番号:1Yin-B-21 ■口頭発表 GITによる複数ページ請求書の文脈を考慮したページ単位画像分類 発表者:ライ シンユ、本田 悠太郎 セッション名:非構造データからの情報抽出(OS-19) セッション会場:F会場(メインホールB) セッション日時:2026年6月9日(火)15:30 ~ 15:45 講演番号:2F5-OS-19a-01 フォローお願いします! Sansanの公式アカウントでは、最新のイベント情報をいち早くお届けします。 ぜひフォローしてください! X: @SansanTech connpass:Sansan株式会社
はじめに こんにちは!技術本部Sansan Engineering Unit Mobile Applicationグループに所属しているiOSエンジニアのヤズジュ夢佐です。 Sansan iOSアプリは、開発生産性においてビルドの重さが課題でした。全体のfull buildにかかる時間は2分程度。Incremental buildでも、差分がない状態で28秒程度、差分があればさらにそれ以上かかる状態でした。 本記事では、このincremental buildを高速化するために行った取り組みを紹介します。 遅延の原因はbuild phase script 調査の結果、差分のないincremental buildでも毎回28秒以上かかってしまう原因は、差分の有無に関わらずbuild processの中で毎回実行されるbuild phase scriptであることがわかりました。 怪しいbuild phase scriptを一時的に外しつつ、incremental buildを実行することによって、どのbuild phase scriptがどの程度のbuild遅延を引き起こしているのかを簡単に特定できました。 特に遅延が大きかったのは、R.swiftとmockoloでした。 遅延の原因1: mockolo mockoloは、mockコードの生成を自動化するライブラリです。@mockableのアノテーションが付いたprotocolのmock実装を自動で生成でき、それをUnit Testなどで活用できます。 github.com このmockコードの生成処理がbuild phase scriptとして組み込まれており、差分の内容に関わらず毎回実行されるようになっていました。 遅延の原因2: R.swift R.swiftは、リソースへのアクセスをtype safeに行うための参照コードを生成してくれるライブラリです。Sansan iOSでは、画像、文字列、storyboard、xibなどの定義にtype safeなアクセスを行うために利用されています。 github.com このR.swiftのコード生成処理も、差分の有無に関わらず毎回実行されるようになっていました。この処理がすべてのbuild phase scriptの中で最も大きな遅延を引き起こしており、その遅延時間はおよそ18〜19秒でした。 解決策: 必要な時だけ実行する mockoloもR.swiftも、すべてのbuildで毎回実行する必要はありません。mockoloの場合は、@mockableのアノテーションのついた定義が編集された時だけ実行すれば十分です。それ以外の実行は無意味な遅延です。 R.swiftも、画像、文字列、storyboard、xibに変更がある時にのみ実行すればよく、それ以外の実行は無意味なbuild遅延です。 解決策として、Build phase scriptとして実行するのではなく、Xcodeのbuild processの外側で任意のタイミングで実行できるようにしました。 Sansan iOSでは、開発中に使うさまざまなcommandをMakefileにまとめています。その中に、make mockolo, make rswiftというcommandを追加しました。 make mockolo commandの実装 make mockoloは、全ターゲット分のMock.generated.swiftを削除した後、ターゲットごとにmockoloを実行して再生成するcommandです。 ポイントは、mockoloをxcrun経由で呼び出す際に、Xcodeのbuild phase scriptが持っているような環境変数(SRCROOTやTARGET_NAMEなど)を明示的に付与する必要があることです。これによって、Xcodeのbuild processの外側からでもMockoloが正常に動作します。 また、削除フェーズでは-pruneで不要なディレクトリを除外して走査コストを削減し、各ターゲットの生成処理は&+waitで並列実行しています。 このアプローチによって、make mockoloの実行は1秒程度で完了するようになりました。 .PHONY: mockolo mockolo: ## 全ModuleのMock.generated.swiftを生成 @# -pruneで不要なディレクトリを除外して走査コストを削減 @find $(MAKEFILE_DIR) \ -type d \( -name Pods -o -name build -o -name DerivedData ... \) -prune \ -o -name "Mock.generated.swift" -type f -print0 | xargs -0 rm -f @# 各targetは互いに独立しているので並列実行 @for target in <ターゲット名...>; do \ (export SRCROOT=$(MAKEFILE_DIR) && export TARGET_NAME=$$target && export ACTION=build && \ xcrun --sdk macosx mint run mockolo \ --sourcedirs "$$SRCROOT/$$TARGET_NAME" \ --destination "$$SRCROOT/$$TARGET_NAME/Mock.generated.swift" \ --mock-final --enable-args-history --macro "DEBUG") & \ done; \ wait make rswift commandの実装 make rswiftは、全ターゲット分のR.generated.swiftを生成するcommandです。 R.swift CLIはもともとXcodeのbuild phase script内で動作することを想定しているため、SDKROOTやPROJECT_FILE_PATHといったXcodeが自動で設定する環境変数を必要とします。 Build Phaseの外から実行する際には、これらの値を事前に設定する必要があります。 export DEVELOPER_DIR="$(xcode-select -p)" export PLATFORM_DIR="$DEVELOPER_DIR/Platforms/iPhoneOS.platform" export SDKROOT="$(xcrun --sdk iphoneos --show-sdk-path)" export PROJECT_FILE_PATH="$SRCROOT/Sansan.xcodeproj" export SOURCE_ROOT="$SRCROOT" 環境変数を設定した後は、ターゲットごとにR.swift CLIを並列実行します。複数ターゲットを並列で処理することで、実行時間を大幅に短縮しました。実行は1秒未満で完了します。 # ターゲットごとに並列実行 "$RSWIFT" generate --target <ターゲット名> "<出力先パス>" & # ...(全ターゲット分) # 全ターゲットの完了を待ち、1つでも失敗したらその結果を保持する for pid in "${PIDS[@]}"; do wait "$pid" || FAILED=$? done 既存のmake commandに組み込む Sansan iOSには、make generateという開発に必要なものを一通り生成できる便利なcommandが用意されています。 make generateでは、xcodegenによるxcodeprojファイルの生成や、KMPをiOSに埋め込むためのxcframeworkの生成、環境変数が入ったファイルの生成などを行っています。 make mockolo, make rswiftは、make generate commandの中で実行されるようにしました。 これによって、branchを切り替えるなどの大規模なコード変更の後に、開発者は今まで通りmake generateを実行するだけで、rswiftやmockoloの生成物も更新できるようにしました。 まとめ 本記事では、差分なしのincremental buildを28秒から2秒に高速化した取り組みを紹介しました。 R.swiftとmockoloをbuild phase scriptから外し、必要な時だけ手動で実行できるようにすることで高速化 既存のmake generateに組み込み、追加の手間を軽減 Buildが遅い、特に差分がないincremental buildでも時間がかかる場合は、差分に関わらず毎回実行されるようになっている不要なbuild phase scriptがないか、疑ってみてください。 Sansan技術本部ではカジュアル面談を実施しています Sansan技術本部では中途の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。
技術本部Contract One Engineering Unitの髙野です。2024年に新卒でSansanに入社しました。 Contract Oneでは、お客様が契約書の情報をさらに活用できるよう、APIの提供を開始しました。 Contract One APIの開発では、OpenAPI 3系準拠のAPI仕様書を作成しましたが、開発を進める中で「仕様書と実装の乖離」問題に直面しました。 本記事では、この問題を人によるレビューや運用で防ぐのではなく、仕組み化して防いだ事例を紹介します。 仕組み化のモチベーション 仕様書と実装が別ファイルで管理される以上、手作業での同期には限界があり、次のような問題が起こります。 仕様書を更新したのに、実装は古いまま 実装を更新したのに、仕様書は古いまま 仕様書ではnon-nullだが、それに気づかずnullを返してしまう 人手によるレビューは属人化しやすく、このような問題の見落としは避けられません。見落としによる不具合は利用者の信頼を損ないます。また、問い合わせ対応により開発チームの時間も奪われます。誰が仕様書や実装を触っても乖離を発生させないためには、人手のレビューに頼らず、仕組みで防ぐのがいいと考えました。 仕組み化の内容 Bean ValidationとHibernate Validatorを活用し、次の3つの仕組みを導入します。 仕様書からのコード生成: OpenAPIの制約をKotlinのアノテーションとして生成する APIリクエスト時のバリデーション: 利用者側の不正入力を弾き、仕様外データの侵入を防ぐ APIレスポンス時のバリデーション: サーバー側の実装バグによる、仕様外データの出力を防ぐ 1. 仕様書からのコード生成 仕様書からコードを生成することで、人手で書く余地を減らし、仕組み上、乖離が起きづらい状態を作ります。 具体的には、openapi-generatorのKotlin系generatorを使い、リクエスト・レスポンスモデルとKtorの@Resourceクラスの2種類を生成します。ルーティングは生成せず、手書きで実装します。 生成コードにBean Validationアノテーションを付与する 単にリクエスト/レスポンスモデルを生成しただけでは、保証できるのは「フィールド名と型」に限られます。maxLength: 255やminimum: 1といったOpenAPI上の制約は、Kotlinビルトインの型ではそのまま表現できません。OpenAPI上の制約を実装レベルで強制するために、Bean Validationアノテーションをコード生成に含めるようにします。 Ktorのテンプレートが含まれる公式のgenerator1はデフォルトでアノテーションを生成しません。そこで、jakarta.validationアノテーションを生成するようにテンプレートをカスタマイズしています。 > data_class.mustache例(クリックで表示) {{#isModel}} @field:Valid {{/isModel}} {{#isArray}} {{#items.isModel}} @field:Valid {{/items.isModel}} {{#minItems}} @field:Size(min = {{{minItems}}}{{#maxItems}}, max = {{{maxItems}}}{{/maxItems}}) {{/minItems}} {{^minItems}} {{#maxItems}} @field:Size(max = {{{maxItems}}}) {{/maxItems}} {{/minItems}} {{/isArray}} {{^isArray}} {{#isString}} {{#minLength}} @field:Size(min = {{{minLength}}}{{#maxLength}}, max = {{{maxLength}}}{{/maxLength}}) {{/minLength}} {{^minLength}} {{#maxLength}} @field:Size(max = {{{maxLength}}}) {{/maxLength}} {{/minLength}} {{#pattern}} @field:Pattern(regexp = "{{{pattern}}}") {{/pattern}} {{#isEmail}} @field:Email {{/isEmail}} {{/isString}} {{#isInteger}} {{#minimum}} @field:Min({{{minimum}}}) {{/minimum}} {{#maximum}} @field:Max({{{maximum}}}) {{/maximum}} {{/isInteger}} {{#isLong}} {{#minimum}} @field:Min({{{minimum}}}) {{/minimum}} {{#maximum}} @field:Max({{{maximum}}}) {{/maximum}} {{/isLong}} {{#isFloat}} {{#minimum}} @field:DecimalMin("{{{minimum}}}") {{/minimum}} {{#maximum}} @field:DecimalMax("{{{maximum}}}") {{/maximum}} {{/isFloat}} {{#isDouble}} {{#minimum}} @field:DecimalMin("{{{minimum}}}") {{/minimum}} {{#maximum}} @field:DecimalMax("{{{maximum}}}") {{/maximum}} {{/isDouble}} {{/isArray}} OpenAPI上の制約とアノテーションのマッピングは次の通りです。 OpenAPI上の制約 アノテーション minLength / maxLength @Size pattern @Pattern format: email @Email minimum / maximum(整数) @Min / @Max minimum / maximum(小数) @DecimalMin / @DecimalMax minItems / maxItems @Size ネストオブジェクト @Valid(再帰的に検証させる) コード生成例 例として、次のようなOpenAPIの定義があったとします。 components: schemas: SearchRequest: type: object required: - limit properties: pageToken: type: string limit: type: integer minimum: 1 maximum: 100 default: 100 この仕様書から、SearchRequestのモデルは次のように生成されます。minimumやmaximumが@Min/@Maxに反映されていることがわかります。 data class SearchRequest( val pageToken: String? = null, @field:Min(1) @field:Max(100) val limit: Int = 100, ) 2. APIリクエスト時のバリデーション リクエストモデルの制約に違反するのは、利用者側が不正なリクエストを送ったタイミングです。この場合は、400 Bad Requestを返したいです。 そこで、Ktorのプラグインを定義し、Bean ValidationアノテーションをHibernate Validatorで検証するようにします。バリデーション違反があればReceiveValidationExceptionを投げ、Ktor公式のStatusPagesプラグインで共通のエラースキーマに整形して400 Bad Requestを返します。 // リクエストボディがデシリアライズされたタイミングで呼ばれるHook private object RequestBodyTransformed : Hook<suspend (call: ApplicationCall, content: Any) -> Unit> { override fun install( pipeline: ApplicationCallPipeline, handler: suspend (call: ApplicationCall, content: Any) -> Unit ) { pipeline.receivePipeline.intercept(ApplicationReceivePipeline.After) { handler(call, subject) } } } // RequestBodyTransformed 時に Hibernate Validatorでリクエストボディを検証する val ReceiveValidationPlugin = createApplicationPlugin(name = "ReceiveValidationPlugin") { on(RequestBodyTransformed) { _, content -> val violations = validator.validate(content) if (violations.isNotEmpty()) { throw ReceiveValidationException(formatViolations(violations)) } } } これだけではリクエストボディの違反しか検知できないので、@Resourceクラスに定義されたパスパラメータ・クエリパラメータにも同じバリデーションを行えるような仕組みを用意します。 具体的には、Ktor標準のget<T> / post<T>をラップし、@Resourceクラス自体をバリデーションする validatedGet<T> / validatedPost<T>といった拡張関数を用意します。 inline fun <reified T : Any> Route.validatedGet( noinline body: suspend PipelineContext<Unit, ApplicationCall>.(T) -> Unit ): Route = get<T> { resource -> validateResource(resource) body(resource) } @PublishedApi internal fun <T : Any> validateResource(resource: T) { val violations = validator.validate(resource) if (violations.isNotEmpty()) { throw ReceiveValidationException(formatViolations(violations)) } } ルート定義側からは、get / postを使う代わりにvalidatedGet / validatedPostを使うだけで、勝手にバリデーションが行われます。validatedXXを使うことを強制したいので、プレゼンテーション層でio.ktor.server.resources.getなどをimportしている箇所がないかをテストで検査し、CIで弾くようにしています。 3. APIレスポンス時のバリデーション レスポンスモデルの制
Sansan株式会社 技術本部 研究開発部の齋藤です。 2026年3月26日に勉強会「自社で育てるLLM/VLM/VLA:学習・活用の実践知」を、Sansan株式会社、株式会社ABEJA、株式会社松尾研究所の3社共同にて開催しました。 sansan.connpass.com 本ブログでは、勉強会の様子をご紹介します。 本勉強会の背景 本勉強会では、LLM、VLM、VLAなどのモデルを自社でファインチューニングし、活用している方、あるいは予定している方にご登壇いただき、それぞれの取り組み内容や、実践を通じて得られた知見を共有していただきました。 生成AIのAPIを活用する形とは異なり、LLM、VLM、VLAを自社でホストする場合には、さまざまな課題があります。そうした課題に対して各社がどのように向き合い、工夫しているのかを共有することで、日本企業におけるLLM、VLM、VLAの活用に少しでも貢献できればと考えています。 登壇内容 今回はSansan株式会社、株式会社ABEJA、株式会社松尾研究所からそれぞれ1名が次のタイトルにて登壇を行いました。 登壇タイトル 登壇者 VLAモデル間におけるピック&プレース性能の比較評価 株式会社ABEJA DP開発本部 データサイエンス部 データサイエンス2G ロボティクス・基盤モデル強化T チームリーダー/瀧田浩平 メール検索に特化したエージェントの強化学習 株式会社松尾研究所 共同研究チーム シニアデータサイエンティスト/太田幹 契約書からの情報抽出を行うLLMのスループットを、バッチ処理を用いて最大40%改善した話 Sansan株式会社 研究開発部 シニアリサーチャー/齋藤慎一朗 VLAモデル間におけるピック&プレース性能の比較評価(株式会社ABEJA DP開発本部 データサイエンス部 データサイエンス2G ロボティクス・基盤モデル強化T チームリーダー/瀧田浩平) LLMやVLMの技術を応用し、自然言語の指示理解やロボットの視覚認知、高度な運動制御を実現するVLA技術について、論文評価だけでは見えにくい実環境での精度や挙動の違いを調査されていました。さらに、ロボットアームの実際の動きを動画を用いて発表されていました。 普段の私の業務では触れることがないVLA技術に関する実体験を聞くことができ、将来性を強く感じました。また、学習したモデルを搭載したロボットアームを勉強会会場に持ってきていただき、場が盛り上がりました。 メール検索に特化したエージェントの強化学習(株式会社松尾研究所 共同研究チーム シニアデータサイエンティスト/太田幹) メール検索に特化したエージェントを自社で強化学習し、フロンティアモデルと同程度の軽量モデルを構築する方法を紹介されていました。またどのような学習ツールを用いたか、どのような試行錯誤を経て実験を進めたかについても語られていました。 詳細は松尾研究所さんの次のブログにて公開されています。 zenn.dev エージェントを自分で学習しようとしたことがなかったため、従来の機械学習モデルの学習方法と、エージェントの学習方法の違いが面白いと思いました。また、今回の検証の条件において、フロンティアモデルよりも精度、速度が上回るエージェントを構築されていたことに驚きました。 契約書からの情報抽出を行うLLMのスループットを、バッチ処理を用いて最大40%改善した話(Sansan株式会社 研究開発部 シニアリサーチャー/齋藤慎一朗) 私からは、ファインチューニングしたLLMをvLLMで運用する際に発生していたタイムアウトエラーを、バッチ処理を活用することで削減した事例を紹介しました。特に、バッチ処理を導入したことでスループットを最大40%改善できた点について共有しました。何を計測し、どのような判断のもとでバッチ処理を採用したかの思考の流れについて詳しく発表しました。 資料は以下です。 speakerdeck.com 懇親会 懇親会では軽食と飲み物を片手に、多くの参加者とお話ししました。特に、APIで運用しているモデルを自社での運用に切り替える際の進め方に困っている方が多かったと感じました。 最後に 本勉強会は定期開催を予定しており、次回は夏頃の開催を企画しております。実施の際には改めて告知します。 また、Sansan技術本部ではカジュアル面談を実施しています。 Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。
Sansan株式会社研究開発部ArchitectグループでPlatformエンジニアをしている上田です。ML PlatformチームではCircuitと呼ばれる研究開発部向けのKubernetes基盤を開発・運用しています。研究員が作ったMLモデルやAPIをリリースするためのKubernetes基盤で、120以上のサービスがこの基盤上で動いています。今回は、その非本番環境(開発環境とステージング環境)で課題だった、使わないのに起動し続けているワークロードの問題に対して、Slackからオンデマンドで起動・停止できる仕組みを開発した話をします。既存のKEDA+Prometheusのスケーリング機構を生かしつつ、ArgoCDのGitOpsと衝突しない設計に焦点をあてようと思います。 背景 研究開発部のKubernetes基盤 Circuit 研究開発部ではCircuitと呼ばれるEKSベースのアプリケーション基盤を運用しています。ArgoCDによるGitOps、KEDAによるオートスケーリング、Karpenterによるノードプロビジョニングを組み合わせた構成になっています。ステージング環境だけでバッチを含む140以上のワークロードが存在し、GPUインスタンス(g6e.xlarge、g6.2xlargeなど)を含む約120ノードが稼働しています。 課題: 全サービス一律起動の無駄 従来はKEDAのCron Scalerを使い、平日7:00〜22:00に全サービスを一律起動していました。しかし、Istioのリクエストログ(`istio_requests_total`)を1カ月分分析したところ、実態は次の通りでした。 カテゴリ サービス数 割合 備考 完全未使用(月0リクエスト) 11 13% 1カ月間一度もリクエストなし 月1〜6日しか使われていない 28 33% 利用日も数時間程度で終了 月の半分程度(7〜19日) 22 26% 利用日でも平均4〜5時間程度 ほぼ毎営業日利用(20日以上) 24 28% それでも夜間・土日は不要 全体の46%が月に数日しか使われていませんでした。特にGPUワークロード(g6e.xlarge: Spot $0.95/h、OnDemand $2.69/h)が平日日中ずっと起動していたため、コストインパクトが大きい状態でした。 解決策 従来のCron Scalerは平日日中に起動しておき、夜間に停止するというアプローチでした。デフォルトが起動状態であり、業務終了しているであろう時間帯だけ止めるという考え方です。今回採用した思想はこの前提を逆転させています。 デフォルトは停止であり、必要な時だけ起動し、自動で停止する。 起動しっぱなしを構造的に防ぐことで、開発者自身がコストコントロールを取りやすくなります。使う人がいなければコストはほぼゼロです。 コンセプト 使いたいサービスだけ、使いたい時だけ起動する、をシンプルに実現する仕組みです。設計に当たり、次の原則を置きました。1. 開発者体験を損なわない → GitOps(ArgoCD)を採用しているため、ワークロードの状態変更には本来PRを作成してmainにマージする必要がある。kubectlで直接変更する場合はselfHealを無効にしなければならない。今回の仕組みはこれらの手間なく、Slackで完結する操作体験を提供する。 2. 既存エコシステムに乗る → KEDA+Prometheusのスケーリング機構をそのまま活用し、新しいOperatorは作らない 3. 消し忘れのセーフティネット → 22:00 JSTに自動全停止 4. サービス登録の手間ゼロ → ScaledObjectのtrigger変更だけで自動検出 5. 強い依存をしないようにする → もし利用されなくなった時でもすぐに引き剥がせるようにする 設計 設計に当たり、いくつかの方式を検討しました。 選択肢 理由 KEDA Prometheus scaler + Workload Controller ArgoCDと完全共存。Git管理リソースの変更不要 `paused-replicas` annotationを操作 ScaledObjectをArgoCD管理外にする必要がある ConfigMap + ArgoCD ignoreDifferences ignoreDifferencesの設定が必要で運用が複雑化 PRでreplicasを変更 遅い(マージ待ち)、Revert忘れのリスク 現状維持(Cron Scalerのみ) 利用者不在でもリソースが起動し続けコスト効率が悪い 最大の論点はどうやって開発者体験を損なわず、ArgoCDのGitOpsと共存するかでした。ArgoCDはGitをSingle Source of Truthとして、クラスタの状態をGitに合わせる(selfHeal)仕組みです。Controllerが直接Deploymentのreplicasを変更すると、ArgoCDが即座にGitの値に戻してしまいます。これを回避する方法として、次の3つが考えられます。 ignoreDifferencesで特定フィールドを除外する → 設定が煩雑、管理対象が増えるたびに追加が必要 selfHealを無効にする → GitOpsの恩恵を失う Git上のmanifestを変更せずにスケーリングを制御する → 採用 3つ目の方法として、KEDAのPrometheus Scalerを起動ゲートとして活用する設計を採用しました。ControllerはPrometheusメトリクスの値を切り替えるだけで、Git上のScaledObject manifestは固定です。KEDAがメトリクスを参照してスケーリングを行うため、ArgoCDから見ると何も変わっていない状態を維持できます。 全体構成 開発環境・ステージング環境の各クラスタに同一構成でデプロイされます。 Circuitには管理クラスタが存在しないため、管理都合上、異なる環境のクラスタへのアクセスをしないようにしています。そのため環境ごとに個別デプロイしています。 実装の詳細 1. ScaledObjectの自動検出 起動時に全namespaceのScaledObjectをlistし、稼働中はwatchでイベントを監視します。`scaleTargetRef`から対象リソースのnamespace、名前、種別(DeploymentまたはRollout)を抽出し、インメモリのレジストリに登録します。ScaledObjectが削除された場合はレジストリから除去し、メトリクスも削除します。 2. Prometheusメトリクスによるスケーリング制御 `circuit_workload_activation`はPrometheus Gaugeメトリクスで、ラベルに`env`、`namespace`、`workload`を持ちます。 ワークロード起動時: 値を`1`に設定 → KEDAがreplicasを1以上にスケール ワークロード停止時: 値を`0`に設定 → KEDAがreplicasを0にスケールダウン KEDAは複数triggerの中で最大のdesired replicasを採用するため、負荷ベースのスケーリング(例: HTTPリクエスト数ベース)と併用できます。 `circuit_workload_activation` リクエスト数 結果 0 — replica: 0(全トリガーinactive) 1 50 req/s replica: 1 1 300 req/s replica: 3(負荷ベースが採用される) 3. Slack BotのModal UI Slack Appの呼び出しは`/command`にて行います。 実行後、Modalから起動対象のNamespaceを選択し、起動したいワークロードにチェックを入れてPlanします。 変更のプレビューとともに起動するワークロードが確認できるため、問題なければApplyを実行することで対象ワークロードを起動できます。 4. 夜間自動停止 CronJobが毎日22:00 JSTに全停止APIを呼び出します。 ユーザーが起動したものの停止を忘れるケースも想定されます。 特に金曜や長期休暇前などでも確実に停止させることで、余計なコストを削減しています。 導入後の削減効果 コスト削減効果 移行済みサービスの稼働率とコストを計測し、3つの方式で比較しました。 方式 月間稼働時間/サービス 稼働率 コスト比 常時稼働 720h 100% 100% 夜間停止Cron(平日7:00〜22:00) 330h 44% 約46% オンデマンド起動(実測) ~24h 3.3% 約3% 従来330時間/月起動していたワークロードが、実際に必要とされていたのは月24時間程度でした。残りの306時間は誰も使っていないのに起動していた時間だったということになります。特にGPUワークロード(g6e.xlarge、g6.2xlarge、g4dn.xlargeなど)はPodがノードを専有するため、稼働時間の削減がそのままノードコストの削減に直結します。移行済みのGPU/アクセラレータ系ワークロードではCron時代と比較して約92%の削減を確認しました。 まとめ 非本番環境の、使わないのに起動している問題に対して、KEDAのPrometheus Scalerを起動ゲートとして活用するアプローチで解決しました。ControllerはPrometheusメトリクスの値を0/1で切り替えるだけで、Git上のmanifestには一切触れません。ArgoCDのGitOpsと衝突せず、サービスオーナーはScaledObjectのtriggerを差し替えるだけで移行が完了します。移行済みの16サービスではCron Scaler時代と比較して約93%のコスト削減を確認しており、全サービスに展開すればさらに高い削減が見込めます。それ以上に、開発者がSlackから数クリックで環境を制御できるようになったことで、自分たちでコストをコントロールしているという意識が自然に生まれているのを実行ログから観測でき、非常に良い取り組みになったと感じました。Sansan技術本部ではカジュアル面談を実施していますSansan技術本部では中途の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。
キャリアは、選択の積み重ねでできています。 どんな経験を経て、なぜSansanを選び、いまどんな挑戦に向き合っているのか。 「Sansan 3 Stories」では、これまでの歩みと、実際にSansanで働いて感じたこと、そしてこれからの挑戦を「過去・現在・未来」の3つのストーリーでお届けします。今回のインタビューでは、外資系企業やスタートアップで数々の開発組織をけん引し、現在は「Contract One」でAIを活用した開発の最前線に立つのペトラスに話を聞きました。 ペトロ ペトラス / Petras PetroškevičiusContract One Engineering Unit日本でデータベースマーケティング企業を共同創業し、CTO兼取締役として電通へ売却後、日本最大の携帯電話保険代理店のエンジニアリングチームとシステムをゼロから構築。外資系大手生命保険会社のアーキテクチャの近代化や、手書きAI-OCRクラウドサービスのR&D、地図広告配信プラットフォーム構築とエンジニアリング組織立ち上げをリードした。 2026年にSansan株式会社に入社。AIを活用した契約管理プロダクトの設計・開発に携わりながら、エンジニアリング組織のマネジメントも担っている。 Story 1 過去|「やり切った」の先へ。次の挑戦を求め続けたキャリア ──これまでのキャリアについて教えてください。 ペトラス:元々はリトアニア出身で、大学では物理学を専攻していました。 当時、日本の半導体技術は世界的にも有名で「日本語を覚えれば、日本の技術論文や特許を直接読めるようになるかもしれない」と思ったのが日本語を学び始めたきっかけです。大学を卒業する頃に日本で仕事が決まり、そこから20年以上日本で働いています。来日した時はちょうどインターネットブームで、自分で会社を立ち上げてデータベースマーケティングの事業を行い、最終的には事業売却も経験しました。その後は、外資系生命保険会社でアーキテクチャチームを立ち上げたり、100億円規模のシステム刷新プロジェクトに携わりました。また、大手通信キャリア向けの携帯電話の保証サービスの事業にも携わり、日本市場向けにエンジニアリング組織もシステムもゼロから作り、最終的には数千万人規模のお客さまに利用いただくサービスへと成長しました。その中で100名規模の組織づくりやCTOも経験しています。その後も、AI-OCRクラウドサービスのR&Dヘッドを務めたり、直近では地図広告プラットフォームの構築やエンジニアリング組織立ち上げにも関わったりと、常に新しい技術と事業に向き合ってきました。 ──さまざまな経験を積まれてきた中で、転職を考えるタイミングには共通点があるのでしょうか。 ペトラス:「やり切った」と感じたタイミングですね。事業が大きく成長して、自分の中でもある程度やるべきことをやり切ったと感じると、「次はどんな挑戦をしようか」と自然に考えるようになります。たとえば、携帯電話の保証サービスの事業では、最終的に全国規模まで成長しました。そこまでいくと、新しい挑戦が必要になる。常に、「次にどんな課題へ向き合うか」を考えてきた気がします。 ──数ある選択肢の中で、なぜSansanを選んだのでしょうか? ペトラス:会社を選ぶときに、私が大事にしていることは3つありました。1つ目は、自社プロダクトを作っていること。業務課題に対して、自らプロダクトを開発して解決しようとしている会社に魅力を感じていました。2つ目は、最新技術を積極的に取り入れていること。エンジニアにとって、新しい技術を使える環境はとても重要です。そして3つ目が、エンジニアリングに本気で投資している会社であること。Sansanは、採用も含めて技術への投資が非常に強く、成長に対する強い意思を感じました。AIの波が本格的に来たタイミングで、これまで自分が経験してきた「スタートアップでのスピード感」「大規模システムの厳格さ」「AIを活用した開発」、これらを生かせる場所を探していたんです。そんな中でContract Oneに出会い、すぐに興味を持ちました。 単に契約書を管理するためのツールではなく、AIを活用しながら契約データを横断的につなぎ、企業活動における「Single Source of Truth(信頼できる唯一の情報源)」になろうとしている。その思想に強く惹かれました。さらに、経営陣やContract Oneのメンバーと話した時に、昔スタートアップを立ち上げていた頃と同じ熱量を感じたんです。「テクノロジーでビジネスを変えたい」という強い意思がある。そこも、Sansanを選んだ大きな理由でした。 Story 2 現在|AIで開発サイクルを最速化。変わり始めたエンジニアの役割 ──現在、どのようなプロダクトや課題に向き合っていますか? ペトラス:現在、全社の契約書をデータ化し、一元管理できる「Contract One」というプロダクトのエンジニアリングチームを率いています。Contract Oneは、すでにARR(Annual Recurring Revenue/年間経常収益)10億円を突破した急成長中のサービスです。プロダクト紹介資料より私たちは今、AI時代を見据えた検索機能やAIチャット、さらには、AIエージェントが自律的に契約データへアクセスし、活用できるようにするための「MCP」の開発も進めています。また、「何を作るか」だけでなく、「どうやって作るか」というテーマにも向き合っています。AIの最新手法を活用しながら、いかに開発サイクルを最速で回していくか。ここが今、大きなテーマです。 ──最速で開発サイクルを回すために、どのようなことに取り組んでいるのでしょうか? ペトラス:Contract Oneではすでに、ビジネスサイドとエンジニアの境界線がなくなってきています。プロダクトマネジャーがAIツールを使って、「UI変更」や「簡単な機能変更」などの機能開発ができるようになっています。 一方で、エンジニアもAIを活用してプロダクトの利用状況などを分析し、自ら新しい機能の起案を行うことが当たり前になってきています。こうした動きを可能にしているのが、自社開発した独自のAI駆動開発のプロセスとツール群です。AIを用いて膨大なコンテキストを収集し、コード生成まで含めて開発サイクルを高速化する取り組みを進めています。しかし、ただ早く開発するだけでは品質やセキュリティーのリスクが高まります。 そこで、これらを「安全に」実現するために、開発プロセス全体を8つのフェーズに分解し、それぞれのフェーズについてプロセス、ツール、基準、リスクレベル、成熟度モデルを定義しています。開発プロセスについては、こちらのTech Blogでも紹介しています。 buildersbox.corp-sansan.comこれにより、プロダクトの課題発見やアイデアの起案から、実装、運用検知までの一連の流れを「誰でも進められる仕組み」にしています。重要なのは、開発プロセスの中からいかに人間の介在を減らし、AIを活用して高速に回せるかです。そうすることで、アイデアからリリースまでの時間を大幅に短縮できるようになっています。 ──AIがあることで、ペトラスさん自身の働き方やモチベーションに変化はありましたか? ペトラス: 以前は、身につけた技術を使って勝負していました。 でも今は、それがかなり簡単になり、自分で作れる生産量が圧倒的に増えました。エンジニアって、やっぱり作ったものが動いていることを見るのが一番楽しいんです。 今は作ろうと思えばほとんどのものが作れちゃうので、自分のキャリアの中でも今が一番楽しい、一番ハッピーな時期ですね。 Story 3 未来|AI時代のビジネスインフラをつくる、新たな挑戦 ──今後、Sansanでどんなことにチャレンジしていきたいですか? ペトラス: Contract Oneを、企業の利益を守る「AI契約データベース」としてさらに進化させていきたいと考えています。これまで人が契約書を読んで、確認して、管理していたものを、今後はAIエージェントが人に代わってやっていくことになるでしょう。ほとんどの企業の既存のシステムが「人間が使うこと」を前提に作られていて、AIエージェントがそのまま使える状態になっていません。そこに大きなビジネスの可能性があると考えています。 契約のライフサイクル全体をAIエージェントが理解し、人間の代わりに仕事をこなす世界を目指したいと考えています。今はどの会社も同じようなツールやAIモデルを使える時代です。市場調査をしてビジネスアイデアを出して、同じようなスピードで開発することができますが、だからこそ最終的に重要になるのは「データ」だと思っています。Sansanの強みは、企業・組織・個人のデータに加え、Bill Oneの請求・発注データ、そしてContract Oneの契約データまでを扱っていることです。企業の一連のビジネスの流れを掴めることは、他社に真似できないSansanの強みだと考えています。Sansan全体のデータ基盤を生かした横断的なプラットフォームとして価値を出していきたいですね。 ──最後に、今後どんな人と一緒に働きたいですか? ペトラス:最新技術をキャッチアップしていることはもちろんですが、AIをはじめ、フロントエンド、バックエンド、インフラまで幅広く興味を持ち、ユーザーの課題を自ら考えながらプロダクトを作れる人と一緒に働きたいですね。今のContract Oneでは、エンジニアも単に実装するだけではなく、プロダクトの企画や課題整理にも深く入り込んでいます。実際に、まだ詳細化されていないアイデアを、週単位でエピックとして形にしていくような動き方も増えています。このように、エンジニアもただ作るだけでなくプロダクトマネジャーの領域に踏み込んでおり、両者の境界線はどんどんなくなってきています。 おそらく半年も経つと、もう完全に同じチームになっていると思いますね。新しい技術を使いこなし、AI駆動開発のプロセスを確立しながら、ユーザーの課題の本質に向き合っていく。そして、次世代のビジネスインフラを共につくり、業務とエンジニアリングの未来を変えていきたいと思っています。 今のSansanは、本当に可能性の最前線にいます。 Sansanでは一緒に働く仲間を募集しています! Sansanでの働き方や仕事の魅力などについて、実際に働くメンバーがお話しします。 少しでも興味を持っていただけた方は、ぜひカジュアル面談にお申し込みください! 関連する求人 open.talentio.com open.talentio.com</ci
はじめにこんにちは、Sansan株式会社コーポレートシステム部(いわゆる情報システム部門、情シス)の坂本です。 皆さんのオフィスにおいて、「地味だけど、確実にストレスを生んでいる運用」はありますか?私たちは、その一つが「複合機」まわりの体験だと考えています。「フロアが変わるたびにドライバーを入れ直す」「スキャンしたデータをメールで送って、またPCで保存し直す」「入退社のたびに、情シスなどの管理部門が複合機用のIDをポチポチ管理する」…。こうした、一つひとつは小さいけれど積み重なると重い負の体験を解消することは、私たちの部が掲げる「EX(従業員体験)をシンプルにする」というミッションそのものです。今回Sansanでは、複合機のリプレースを機に、複合機のユーザー管理を認証プラットフォームの「Okta」へと統合し、周辺の仕組みを丸ごと再構築しました。本記事では、単なる機器の入れ替えに留まらない、「攻めのコーポレートIT」としての取り組みを記録します。 目次 ・背景:ID管理と複合機ごとのドライバー設定からの脱却 ・Before / After で見る変化 ・ソリューション:ID基盤のハブとして「RICOH Smart Integration」を選択 ・【運用の変化】RSIとOktaプロビジョニングによる「管理負担ほぼゼロ」の実現 ・【社員体験の変化】マルチOS対応と「共通ドライバー」による解放 ・【業務の変化】Box連携によるスキャン刷新 ・Appendix:ここだけの構築秘話 ・おわりに 背景:ID管理と複合機ごとのドライバー設定からの脱却これまでSansanでは、社内のID管理やSSOの中心はOktaでしたが、複合機のユーザー管理については別系統での管理が必要な状態でした。また、運用面で課題となっていたのが複合機1台ごとに異なるドライバーの問題や、スキャンの作業における運用です。ID情報の分散と運用コスト:社員IDのマスターはOktaにあるのに、複合機のために別のディレクトリ情報をメンテナンスする手間が発生。社員IDの一元管理による運用負荷を軽減したい。物理的な「個体」に縛られた印刷環境:社内の複合機ごとにドライバーをインストールしていたため、他拠点への出張はもちろん、同じオフィス内でのフロア移動や目と鼻の先の複合機を使う際にも、その都度新しいドライバーのセットアップが必要でした。スキャン業務の手間:従来はスキャンしたデータを自分宛のメールで送る「メールスキャン」が主流でした。しかし、これではメール容量を圧迫しますし、さらにデータ保存のためにPC側でメールからストレージへ移し替える二度手間が発生していました。こうした課題を解消するために掲げたのが、 「複合機まわりのユーザー管理の起点をOktaに寄せて一元管理し、運用をシンプル化する」こと。そして「どのマシンの前に行っても、追加設定不要ですぐに印刷・スキャンができる体験」を作ることです。Before / After で見る変化今回のプロジェクトによる変化をまとめると次の通りです。 項目 Before(従来) After(リプレース後) ユーザー認証(SSO) 別系統での個別管理 OktaのSAML連携でRSIにSSO プロビジョニング 入退社時の手動メンテナンス OktaのSCIM連携でRSIへ自動同期 印刷設定 複合機1台ごとにドライバーが必要 共通ドライバーでどこでも印刷可 スキャン メールスキャン(都度PCで整理) Box直接連携(自動アップロード) ソリューション:ID基盤のハブとして「RICOH Smart Integration」を選択今回のリプレースにおいて、私たちが選んだ解決策は株式会社リコーが提供するプラットフォーム「RICOH Smart Integration(以下、RSI)」です。選定の決定打となったのは、RSIが「クラウドサービス間のハブ」として設計されており、OktaとのSAML連携とSCIM連携の両方に対応している点です。これにより、これまで別系統で管理していた複合機まわりのユーザー管理をOktaに統合し、入退社に伴うユーザー管理も自動化することが可能になりました。このRSIを軸とした新基盤によって実現した、 「運用の自動化」「ロケーションフリーな印刷体験」「スキャン作業の刷新」の3つのポイントを紹介します。【運用の変化】RSIとOktaプロビジョニングによる「管理負担ほぼゼロ」の実現検証・導入して驚いたのは、リコー社が提供するOktaプロビジョニングの精度の高さと、挙動の安定感です。ID基盤の連携時、RSIの連携機能はとてもスムーズに機能しました。運用の自動化と負荷軽減:入社・退職に伴う同期で、手動運用が発生しない仕様でした。OktaとRSIはSCIM連携により統合されており、Okta側でのユーザー追加などが自動的にRSIへ同期されるため、管理側での二重メンテナンスは完全に解消されました。レポート機能:管理画面から各拠点の機器状態を一元管理できるほか、レポート機能により「機器別やユーザー別のカウンター」が出力可能なため、コスト適正化の判断や機材配置の最適化も容易になりました。迅速な保守体制:導入後の保守体制についても迅速で、万が一の際にも信頼できるサポートを受けられる点は、大規模なリプレースを支える大きな安心材料となりました。【社員体験の変化】マルチOS対応と「共通ドライバー」による解放WindowsとMacが混在するSansanにおいて、どちらのOSでも同等の体験を提供できることは必須条件でした。今回のリプレースで最も現場の体験を変えたのが、「物理的なマシンに縛られない印刷環境」への転換です。ローカルドライバーとロケーションフリーの両立:これまでは、社内の複合機ごとに個別のドライバーをインストールする運用でした。そのため、同じオフィス内でのフロア移動や、普段とは違うマシンを利用するたびに、その都度新しいドライバーのセットアップを強いる状況がありました。今回はリコー社の「ロケーションフリー設定」をローカルドライバーに適用。これにより、Windows/Macそれぞれ1種類の共通ドライバーをインストールしておけば、社内のどの複合機からでも出力が可能になりました。フロア移動をしても、PCの追加設定は不要です。「このプリンターを使うから、このドライバーを入れる」という概念をなくしました。管理デバイスへの配布方法:・Windows:リコー社提供の専用パッケージツールを利用し、一括展開。・Mac:デバイス管理ソフトの「Jamf Pro」を用いてSelf Serviceから、全端末へサイレント配布。社員の設定の手間を低減しました。Windows/Macユーザーどちらも、2〜3分程度のドライバー設定で、すぐ印刷可能となっています。【業務の変化】Box連携によるスキャン刷新スキャン作業についても、RSIの「カンタンストレージ活用for Box」を導入し、従来のメールスキャン運用を廃止しました。業務効率化:複合機から直接Boxの指定フォルダへアップロード。PCを介さずとも即座にBox上でデータ共有やスキャンが可能となりました。メール容量の逼迫はもちろん、メールを確認してファイルを移動させる手間が消え、業務効率化につながりました。Appendix:ここだけの構築秘話今回のプロジェクトは、外部への委託をせず、設計から実装までをすべて自分たちの手で行いました。その中で直面した、リアルなエピソードを少しだけ紹介します。Okta設定は驚くほどスムーズだったはずが…リコー社が提供するRSIの公開マニュアルは非常に整備されており、基本的な連携設定はなんと即日で完了。あまりにスムーズな滑り出しに、拍子抜けするほどでした。独自環境ゆえの壁と、エンジニアの伴走しかし、検証を進めると弊社固有の命名規約によって、標準設定ではうまくRSI側へ反映されない事象が発生しました。「もしかして弊社の環境では、このままでは連携できないのではないか?」そんな不安がよぎりましたが、こうした現場のリアルな詰まりどころに対し、リコー社のエンジニアの方々が真摯に伴走してくれたのが非常に心強かったです。私たちの環境に合わせた調整をスピーディーに行い、無事に最適解へと導いていただきました。技術にダイレクトに触れ、メーカーの方と議論しながら自分たちの手でサービスを組み上げる。このプロセスを経て完成した仕組みだからこそ、細部まで自分たちの意思が通った、納得感のある基盤を作り上げることができました。おわりに今回のリプレースを経て、複合機は「設定や運用を意識しなければならない機器」から、 「いつでも迷わず使える当たり前のインフラ」 へと進化しました。RSIを通じたOktaとのSCIM・SAML連携に加え、OSや場所を問わずシームレスに利用できる共通ドライバーの導入。どのマシンを使うか、どう設定するか。そんな「印刷のための思考」を排除する仕組みは、社員の生産性を最大化させるための強力な武器になったと感じています。なお、今回のリプレースはAppendixでも触れたように、自分たちの手で設計から実装までを行いました。現場の課題を日々真剣に考える私たちだからこそ、妥協のない「シンプルさ」を追求できるのだと信じています。こうした技術にダイレクトに触れる環境で、泥臭くもクリエイティブに、一緒に「シンプルさ」を突き詰めてみませんか?Sansanでは、これらを一緒に作り上げてくれる仲間を募集しています。「自身のスキルを武器に、もっと広い領域へ挑戦してみたい」「Sansanのコーポレートシステムチームがどんな雰囲気か、もう少し詳しく聞いてみたい」そう感じていただけたなら、ぜひ一度カジュアルにお話ししましょう。あなたの挑戦を、心よりお待ちしています。最後までお読みいただき、ありがとうございました。Sansan技術本部ではカジュアル面談を実施していますSansan技術本部では中途の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。
はじめに こんにちは!技術本部Sansan Engineering Unit Mobile Applicationグループに所属しているiOSエンジニアのヤズジュ夢佐です。 Sansan iOSアプリでは、5年以上前からマルチモジュール化の取り組みが始まっています。 buildersbox.corp-sansan.com しかし、まだ過半数のファイルが1つのSansan Main Targetに詰まっています。この巨大なSansan Main Targetのビルドは重たく、Full Buildには2分程度かかります。コンパイル時間の長さによって、開発体験は悪化していました。部分的に進んでいるマルチモジュール化も、責務が明確ではないモジュールが複数存在しており、どのコードがどのモジュールに配置すべきか分かりにくい状態でした。 本記事では、これらの課題に対して取り組んだ施策を紹介します。画面単位で独立したFeatureモジュールの導入、モジュール再設計によってFeatureモジュールの依存物を最適化し、Buildを高速化するところまで、どのようなアプローチを取ったか解説していきます。 前提: マルチモジュール化で得られる恩恵は何か マルチモジュール化の話に入る前に、まずはマルチモジュール化に期待する恩恵を整理してみました。期待するものが明確でないと、モジュール構成をどのように組んでいくかの軸が決まらない上に、マルチモジュール化の取り組み自体の優先度が下がり、先延ばしされてしまいます。 1: 小さな範囲でBuildを素早く実行でき、開発生産性が向上する 開発中の機能に閉じた範囲でBuildを実行でき、アプリ全体をBuildするよりもはるかに短い時間でBuildを完了できるようになります。コンパイルを素早く終えられることで、コードの変更が有効であることを瞬時に確認できます。SwiftUI Previewも高速に動作させることが可能になり、UI開発効率も向上します。 2: Incremental Buildの高速化を期待できる すべてのコードベースが1つのモジュールに詰まっている場合、一部を変更した後のBuildでプロジェクト全体が再コンパイルされてしまいます。しかし、マルチモジュール化が進んでいれば、変更のないモジュールについてはキャッシュを利用でき、Incremental Buildの再コンパイルの範囲を限定できます。 3: モジュール境界による責務の分離・設計の明確化 モジュール分割は物理的なバイナリ境界を設けるため、コードレベルの依存関係を制限できます。Swiftのアクセスレベルのデフォルトはinternalです。コードのアクセス可能な範囲は、明示的にopenやpublicに指定しない限り、モジュールに閉じた状態にできます。すべてのコードが1つのモジュールに詰まったモノリス構造では、デフォルトでコードのアクセス可能な範囲はプロジェクト全体になるため、意図せず誤った依存関係が生まれやすいです。 他にも、モジュール単位でテストを書けることによってテストパフォーマンスが向上したりするなど、マルチモジュール構成の恩恵はいくつもあります。これらの恩恵を踏まえた上で、Featureモジュール導入についての話に移っていきます。 Featureモジュールの導入 Sansanでは、KMPとFluxアーキテクチャを組み合わせた設計の導入がされています。 buildersbox.corp-sansan.com この新しい設計で作られた機能は、すべてFluxViewという1つのモジュールに詰め込まれた状態でした。このFluxViewモジュールには、十数個の画面の実装が存在していましたが、それぞれの画面の実装同士は依存関係がなく、同じモジュールに存在させる必要がありませんでした。 そのため、FluxViewモジュールに存在していた実装を、画面単位でFeatureモジュールとして切り分けることにしました。 これによって、次のような恩恵を得ることができます。 各FeatureモジュールにFeature独自の実装が閉じた状態になり、Feature間のCode Isolationが促進され、機能開発効率の向上や、意図しない副作用による不具合の発生率の低下などを期待できる 1つのモジュールあたりに含まれるコードや依存物が最小化され、より高いBuildパフォーマンスを得られる このFeatureモジュール構成の導入によって、KMPとFluxアーキテクチャで実装された比較的新しい設計の機能は、すべてFeatureモジュールとして切り分けられた状態を実現できました。 課題: Featureモジュールのビルドが重たい Featureモジュール導入直後の各FeatureモジュールのFull Buildは平均40.2秒でした。これはSansan Main Targetが2分程度かかるのに比較すると3倍近い速さです。コンパイルが素早く完了するため、コードの編集作業はより高速に進められるようになりました。そして何より、SwiftUI Previewの立ち上げのパフォーマンスが大幅に向上したことが、UI開発効率を大きく改善しました。 しかし、この時のFeatureモジュールには、本来不要な依存物が多く含まれており、無駄にBuildが遅延している状態でした。ここからは、FeatureモジュールのBuild高速化を実現するために行った取り組みについて話していきます。 Featureモジュールの高速化準備: 現状を分析する Buildが遅い時は闇雲に対策を打つのではなく、まず何を理由としてどの程度Buildに時間がかかっているかを分析するのが効果的です。Xcode Build Timelineを使うことで、Build中にどのモジュール・ライブラリ・ファイルがどれだけの時間を消費しているかをタイムライン形式で可視化できます。 詳しい活用方法は、こちらのWWDCの動画が参考になります。 developer.apple.com この機能によって、導入当時のFeatureモジュールのBuildを分析した結果の一例がこちらです。 左端が0秒地点、右端がBuild完了時の37秒あたりの地点となっており、左から右へ、Build中に何のコンパイルに対して時間を使っているのか可視化できます。 Realmによる遅延が大きい この画像では、左の方の3秒地点から13秒地点あたりに広く水色のエリアが広がっています。その右隣の13秒地点から18秒地点まで広がっている黄色のエリアも大きいですね。 この水色のエリアと黄色のエリアは、すべてRealmのコンパイルです。つまり、Realmのためだけにこれだけのコンパイル時間がかかっている。逆に言えば、Realmを外すだけで、15秒近くのコンパイル時間を削減できるわけです。 UIKit実装による遅延が大きい さらに右の方も見てみましょう。25秒地点から30秒地点まで、緑色のエリアが広がっています。さらに、その右隣の30秒地点から36秒地点まで、ピンクのエリアが広がっています。これらのほとんどはxibファイル、つまりUIKitの実装にかかっているコンパイル時間です。 Featureを超えて共有される汎用的なUIKitの実装が入ったモジュールが、この緑色とピンク色のエリアを埋めています。FeatureモジュールにはUIKitで実装されているものもSwiftUIで実装されているものもあります。SwiftUIで実装されているFeatureでは、このUIKitの汎用実装に依存する必要がありません。 まとめると、Featureモジュールは平均でおよそ40秒をBuildに費やしますが、そのうちの15秒程度は不要なRealmへの依存、10秒程度はUIKitへの依存によって生まれていることが分かりました。 このあとは、Realm、UIKit実装を依存物から外すためにやったことを話していきます。 Featureモジュール高速化施策1: Realmの依存を解消する FeatureモジュールからRealmの依存を外すことは簡単ではありませんでした。理由は、RealmのObjectが、Realmとは関係ない処理でも乱用されていたためです。 課題: Realm ObjectがRealm以外の処理で乱用されている Data LayerでのAPI decode処理や、Domain Layerでのデータの受け渡しなどで、Realm Objectがそのまま使われていました。そのため、Data LayerやDomain Layerが必然的にRealmに依存することになり、連鎖してFeatureモジュールもRealmの依存を持たざるを得ない状況でした。 実際のコードに近い、少し単純化したサンプルコードを用意しました。 public final class CompanyRealmObject: Object, Decodable, @unchecked Sendable { @objc public dynamic var id = "" @objc public dynamic var name: String? @objc public dynamic var country: String? override public class func primaryKey() -> String? { "id" } } 継承されているObjectは、RealmのObjectであることを示します。このRealmのObjectがDecodableに準拠し、API responseのdecodeにも乱用されていました。 対処1: RealmのObjectとRealm以外の処理向けの構造体を分ける Realm処理専用のRealm Objectと、それ以外の処理に向けて作られた構造体を分けて用意します。既存のRealm Objectと全く同じproperty構成を持つ、immutableでsendableなstructを用意しました。Realmの処理に関係のない箇所で使われているRealm Objectを、このような単純なstructに置き換えます。 public struct Company: Decodable, Sendable, Equatable { public let id: String public let name: String? public let country: String? public init(id: String, name: String?, country: String?) { self.id = id self.name = name self</spa
こんにちは。情報セキュリティ部 Product Securityグループの北澤です。 昨年に引き続き、新卒エンジニア向けに研修を行いました。この記事では、本年度の研修内容についてお伝えします。 研修について 昨年と同じく、丸一日かけて実施しました。 研修スケジュール 基礎編では、Web、インフラやモバイルなどの領域を問わず必要な情報セキュリティーの基礎に関する講義を行いました。午後にはSQL InjectionやXSSなどをはじめとしたWebアプリケーションに関する攻撃手法とその防衛について講義を行いました。 講義内容 基礎編 午前中は基礎編の講義を行いました。 最初にそもそも「情報セキュリティ」とは何か? から始まり、対策をしない場合はどのような被害が考えられるのか? Webサービスを公開していると実際にどの程度攻撃が行われるのか? ということをお話ししました。 そこから、対策するためには何を知るべきか? 脆弱性とは何か? 脆弱性情報を渡されたときにリスクと対応優先度をどう考えればいいのか? という実際の現場で直面するだろう課題と考え方について講義を行いました。 また、Claude CodeやCodexなど、AIエージェントの活用が加速している現状を鑑みて、昨年の内容に加え、AI周りのセキュリティについても触れるようにしました。内容としてはローカルでAIエージェントでコーディングを行う際に注意すべき点、プロダクトにAI機能を組み込む際に気を付けるべき点の二つについて話をしました。 いくつか資料を紹介します。 情報セキュリティとは 機密性についてのスライド 可用性についてのスライド 対応優先度を考える上で大切な項目 深層防御/多層防御 を説明するためのたまねぎ Web編 午後はインジェクションなどWebサービスに埋め込んでしまいやすい問題について、項目ごとに脅威・攻撃手法・対策についての講義を行いました。具体的には以下です。 SQL Injection XSS 認証不備 認可不備 SSRF CSRF 設計に関する問題 コンポーネント(ライブラリなど)に関する問題 ログに関する問題 暗号化の不備 SQL Injectionの概要の説明 SQL Injectionの影響と対策について XSSの概要の説明 昨年との違いとしては、内容に応じてローカルでハンズオン用アプリケーションを動かしてもらい、実際に講義ごとに攻撃を体験してもらうようにしました。 SQL Injectionのハンズオン操作説明スライド ハンズオンアプリケーションに攻撃ペイロードを送信しようとしている画面 攻撃ペイロード送信によって、パスワード無しでadminにログインできてしまった状態 裏話ですが、ハンズオンのアプリケーションは実験的にAspireを使い、開発や立ち上げが楽になるようにしてみました。SSRFのような複数アプリケーションを扱うものや、SQL Injectionのようにアプリ+postgresというDBコンテナを扱うものに対してはかなり開発体験が良かったですね。 aspire.dev 新卒のメンバーにアナウンスする際も、「Hostプロジェクトディレクトリでdotnet runしてね」で済むのでめちゃくちゃ楽でした。 CSRFは実際に攻撃されることの体験をして欲しいなと思っていたので脆弱な簡易SNSサービスと罠サイトである出席確認サイトを用意してみました。 簡易SNSサービスにログインしてもらった状態で、罠である「出席確認」のボタンを押してもらい、裏でスクリプトが動き、「ざわさんにごはんおごります!」という意図しない書き込みが簡易SNSに対して行われることを体験してもらいました。(ざわさんとは講師である私のことです) CSRF体験用の簡易SNS 簡易SNSにログインした状態で出席ボタンを押すと 罠サイトの出席確認システム画面 講師にごはんをおごる内容がSNSに投稿される。新卒のみなさん、ごちそうさまです。 ざわさんにごはんおごります!が投稿されます。 「やられた~」という声もあがり、実際にCSRF脆弱性を作り込んでしまったときの怖さをわかっていただけたかなと思います。 脆弱性について話したあとに、脆弱性を防ぐ・発見するためのセキュアな開発プロセスについて講義を行いました。 セキュアな開発プロセスの大事さについての説明スライド 設計時には設計時の、実装時には実装時に必要な対策をする、といった内容です。 設計時の問題がリリースギリギリになってわかるものほど悪夢的なことはないので、それを防ぐためにそれぞれの工程ごとにどのような検証を行う必要があるかについて話しました。 CTF Web編の後には楽しみながら実践的に知って記憶をしてもらうためのCTF(Capture the Flag)を行いました。昨年と同じく、攻撃対象のアプリケーションはASP.NET Coreで自前で用意し、スコアサーバにはCTFdを利用しました。 簡単なSQL InjectionからBlind SQL Injection、XSSによる管理者セッション窃取やjwtのalg:noneまでとさまざまな難易度の問題を用意し、挑んでもらいました。 CTFでは実際の攻撃者の動きを学んでもらうことを心がけました。 例えば、XSSではただアラートを表示させるなどではなく、実際に管理者にその画面を送信し、管理者のセッションを奪取してログインするという、より本格的な導線を用意していました。 CTFのXSS1問題 以下ブログ用画像のためにローカルで動かしています。 サンドイッチショップの検索バーでalert()を表示させるスクリプトを記述している <figure class="figure-image figure-image-fotolife" title=
はじめに Digitization部でグループマネジャーをしている中村です。Sansanは4社目の会社で、入社して半年弱が経過しました。外資系IT企業と国内製造業IT部門での経験から、Digitization部の魅力を語りたいと思います。 まず、Sansanという会社の魅力は、外資系IT企業の文化と国内企業が持っている文化をうまく融合したような文化だと感じています。Sansanは創業20期を迎える成長企業ですが、カリフォルニアのIT企業のようなアントレプレナーシップに富んだ元気な側面がある一方で、日本の老舗企業から感じられる企業理念への共感力や全社の一体感のようなものを強く感じました。そんなSansanのDigitization部の魅力は大きく以下の3つだと思います。 0.1円、0.1%が事業インパクトにつながる部門 人とAIのオペレーションが交差する難しさに向き合っている コードの向こう側にあるリアルな世界との接点 Digitization部の仕事 Digitization部の魅力を語る前に、この部門がどんな仕事をしているのかをご紹介します。Digitization部のミッションは「データ化で事業をリードする」です。名刺・請求書・契約書といった紙媒体で表現された情報をデジタルデータに変換する仕事を担っています。このデジタイズ業務の大きな流れは、データスキャンとデータエントリーに分かれます。 データスキャンは、紙をスキャンしてPDFや画像データに変換する業務です。段ボールで配送された名刺・請求書・契約書を開梱・開封・枚数の確認などを経てスキャニングを行うため、物理的なオペレーション設計や、スキャンデータと物理資料のひも付けなどのデータ管理設計が重要になります。 スキャンを経てPDFや画像になったデータから、意味のあるデータ(企業名、氏名、取引日、銀行名、口座番号、金額、契約の種類など)を抽出する業務がデータエントリーです。データエントリーではAIによるデータの識別とオペレーターによるデータの入力の2つの視点で業務が設計されています。特にオペレーターによるデータ入力では、入力ミスを防ぎ、認知負荷を下げるためのUX設計と、複数のオペレーターで1件のデータを分担して入力するための分散処理設計が重要になります。 このようにDigitization部の仕事は、Sansanのプロダクトセグメントである名刺・請求書・契約書のすべてに深く関わり、コスト・品質・納期について、常に最適なバランスを取ることを大切にしています。 Digitization部の魅力 0.1円、0.1%が事業インパクトにつながる部門 Digitization部は、単に特定のプロダクトの一機能を作る部門ではありません。Sansanの価値の源泉である「アナログ情報を正確なデジタルデータに変換する」という仕組みを支える部門です。 名刺、請求書、契約書などの情報は、そのままでは検索も集計も分析もできません。そこに書かれている情報を、正確で使いやすいデータに変換してはじめて、プロダクトの中で価値を持ちます。また、この仕組みはSansan、Bill One、Contract Oneなど、複数のプロダクトを横断しています。 Digitization部の魅力の1つは改善のインパクトが事業成果に近いことです。データ化の品質が上がれば、ユーザー体験が向上します。処理速度が上がれば、価値提供までの時間を短くできます。コストが下がれば、事業全体の収益性に貢献できます。 扱うデータ量が月間で億単位と大きいため、0.1円のコスト削減や0.1%の精度向上といった改善が大きなインパクトにつながります。自分の改善が、品質・コスト・スピードを通じてプロダクト価値や事業成果に直結する。そこに、Digitization部ならではのエンジニアリングの面白さがあると感じています。 人とAIのオペレーションが交差する難しさ AIによるアナログデータの認識能力は大きく向上しています。一方で、紙媒体に記録されたアナログデータには、人にしか理解できないケースが多くあります。 例えば、業界特有の表記、企業ごとの慣習、文脈に依存して省略された情報などは、単純な文字認識だけでは扱いきれません。そこには、ドメイン知識や業務理解を持ったオペレーターの判断が必要になります。 Digitization部では、AIによる自動化と、オペレーターによるデータスキャン・データ入力・確認作業を、どう効率的につないでいくかを考えます。これはソフトウェアエンジニアリングであると同時に、人の作業をどう設計するかというオペレーションエンジニアリングでもあり、人とマシン、あるいはアナログとデジタルという両面から難題に挑戦していく面白さがあります。 コードの向こう側にあるリアルな世界との接点 Digitization部では、技術的に正しいだけでは不十分です。現場で無理なく運用できること、品質が担保されること、コストが合うこと、そしてスケールすることが求められます。 そこには、リアルな大量データ、リアルな人の作業、リアルなコスト制約の中で、最適解を探る難しさがあります。 実際、Digitization部の職場では、オペレーターチームはすぐ隣の席にいて、開発中の画面を見せながら「これでOKですか?」「ここは、こんな感じでお願いします」といった会話が日常的に行われます。仕様書だけで完結するのではなく、現場の反応を見ながら改善していける環境があります。 また、データスキャンを担当するチームは、スキャンセンターのオペレーションにも向き合います。そこでは、請求書や契約書をまとめるクリアフォルダの重量さえも課題となります。お客様からお預かりした書類をどの棚に分類して、今どこにあるのか、という製造業のロケーション管理のような仕組みも構築しています。 このようなリアルな世界との接点を持てるDigitization部は、金融システムの保守開発や、製造業の情シスを経験してきた私から見ると、「止められない基盤」と「改善し続ける現場」の両方を持っている部門だと感じています。 Digitization部の今後の挑戦 Sansanは名刺のデータ化から始まり、請求書・契約書とそのデータ化の領域を広げてきました。これは、Digitization部が持つ人とAIのオペレーションで「多様な情報を精度高くデータ化できる」という価値に支えられているからだと感じています。 この価値を2つの観点から、よりスケーラブルなものにしていくのが今後の挑戦です。 まずは、多能工化(ひとりが複数業務を担える状態)の推進です。名刺・請求書・契約書は、扱う書類の形式も、読み取る項目も、品質の基準も大きく異なるため、これまではドメインごとに特化したチームが個別に運営されてきました。ひとりが複数のドメインを担えるようになれば、業務量の波や新しい業務の立ち上げにも、機動的に人を再配置できるようになります。これは前職の製造業でも同様の流れがあり、人手不足への対応という見方をされることが多いですが、実態としてはそこで働く人の成長を促し、組織として人を大切にしていきながら全体の効率を高める取り組みだと捉えています。 もう一つは、名刺・請求書・契約書の次の領域である企業の組織情報や取引情報などのビジネスデータへの拡大です。この領域は、これまでの紙媒体の情報をデジタル化することとは違って、定期的に更新され続ける企業情報を取り扱うという点で、大きなチャレンジがあります。ここでも人にしかできない判断や設計が重要になり、人とAIの双方に向き合う、とても面白い仕事になると思っています。 Sansan技術本部ではカジュアル面談を実施しています Sansan技術本部では中途の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。
こんにちは。名刺アプリ「Eight」でエンジニアをしている鳥山(@pvcresin)です。 4月に函館で行われたRubyKaigi 2026に、Eightのエンジニア数名で参加してきました! 参加したメンバー 今回は、それぞれの視点から感想や印象に残ったセッションをご紹介します。 目次 鳥山 平石 間瀬 まとめ 鳥山 RubyKaigiへの思い RubyKaigi参加は、沖縄・松山に続き今回で3回目です。Rubyの今後の方向性や最新トレンドを多角的に学びたいと思い、参加しました。特にRubyの型やRailsのView層、WebAssemblyに関心があるため、関連トークを中心に聴いてきました。 気になったセッション Blazing-fast Code Indexing for Smarter Ruby Tools RubyのLSPやドキュメント生成、型チェックツールなどがそれぞれ持つ「コード理解の仕組み」を統一的に扱うために、RubydexというCode Indexerを開発したという話でした。既にRuby LSPやTapioca、Spoomとの統合で高速化やメモリ削減の成果が出ており、パフォーマンスやメンテナンスの観点でとてもよいツールだと思いました。EightでもRuby LSPやSorbetを利用しているため、開発者体験が良くなりそうでうれしいです。 HTML-Aware ERB: The Path to Reactive Rendering HTML用のERBを単なる文字列生成ではなく、構造を持ったテンプレートとして扱うという話でした。その上で、リアクティブなレンダリングや開発支援を提供するReActionViewというツールが紹介されました。ベースとなっているのは同じ作者のHerbというHTML用のERBパーサーで、フォーマッターやリンターの機能も備えている万能なツールなので、以前から注目していました。今回の発表ではReactのような画面の差分更新を実現する仕組みの説明やデモもあり、Rails Viewの新しい開発スタイルを感じさせる内容でワクワクしました。 全体の感想 RubyKaigiに参加して、Rubyのエコシステムがどんどん前進していくのを肌で感じました。個人的には、ここ最近コミットしていたTypeProfという型推論ツールに関するトークで、作者のmameさんに発表の中で褒めていただいたことがとてもうれしかったです。また、海外の開発者ともツールの仕様や今後の動向について議論ができたことに少しだけ自身の成長を感じられました。そのほかの時間も、温泉や桜が満開の五稜郭など、函館を満喫できてよかったです。 タワーから眺めた五稜郭 平石 RubyKaigiへの思い 昨年に続き、今年で3回目の参加でした。RubyKaigiの一番の楽しみは、さまざまなエンジニアの方々と交流できることです。今回は、普段の業務に持ち帰れそうな学びを中心にセッションを聴いてきました。 気になったセッション Back to the roots of date Rubyのdateライブラリは、長らくメンテナンス体制が不安定で、timeライブラリへの統合案も議論されてきました。そんな中で、dateライブラリをPure Rubyで書き換え、持続可能にしていく取り組みについての発表でした。Cで書かれた実装をRubyに置き換えていく過程で、AIの活用についても触れられていました。特に印象に残ったのが、「AIは強力なパートナーではあるが、我々の主ではない。我々は構築したものに対して責任を持ち続けなければいけない」という言葉でした。AIが急速に進化する今、たとえAIが優れたコードを生成できたとしても、それが長期的に保守できるかを判断し、責任を負うのは人間のエンジニアだと改めて感じました。 Matz Keynote 毎年恒例のMatz Keynoteですが、今年はMatzさんがAIをフル活用して、作りたいものを次々に作っているという話が印象的でした。中でもSpinelというRubyのAOTコンパイラの話が中心でした。最近は、どのカンファレンスでも「AI時代のエンジニアの働き方」に関する発表が多い印象があります。そんな中で、Matzさんが本当に楽しそうに作ったものを披露していたのがとても印象的でした。「この人はものづくりが大好きなんだな」と感じると同時に、自分もAIをうまく活用して、作りたいものを形にしていきたいと思いました。 全体の感想 毎年のことですが、今年も改めてRubyコミュニティーの熱量に触れ、大いに刺激を受けた3日間でした。企業ブースや本編後のDrinkupで、各社のエンジニアの方々と意見交換できるのもRubyKaigiの良さだなと改めて思いました。また3日間とも天気に恵まれ、桜や夜景、海鮮など函館も満喫できたのがとても良かったです! 函館山からの夜景 間瀬 RubyKaigiへの思い 今回が初参加です。普段はインフラ/SRE寄りの領域を軸にしつつ、チームのマネジメントにも関わっています。昨年のKaigi on Railsに続いて、Rubyコミュニティーの熱量を肌で感じたいと思い参加しました。Ruby言語そのもののカンファレンスと聞いていたので、会場の雰囲気やセッションのレベル感を知りたいと思っていました。 気になったセッション The Journey of Box Building Ruby 4.0で導入された実験的機能であるRuby::Boxについて紹介するキーノートでした。仕組みの概要だけでなく、アイデアが形になるまでの経緯もあわせて語られていて、全体像が掴みやすかったです。特に「なぜ今この仕組みが必要なのか/どんな場面で役立ちそうか」といった背景まで触れられていたのが印象に残りました。 Pure Ruby Apache Arrow reader/writer Apache Arrowのreader/writerをPure Rubyで実装する取り組みの発表です。導入のハードルを下げつつ、Rubyでも扱えるデータ入出力の選択肢を増やしていく、という狙いが分かりやすく面白かったです。ちなみに、登壇者のSutouさん目当てで聴いたセッションでもあります。 全体の感想 RubyKaigiはセッション(本編)と、Drinkupなどのコミュニティー活動がセットになっていて、全体として体験の密度が高いイベントだと感じました。学びと交流のバランスがよく、参加して良かったです。 美味しかった塩ラーメン まとめ 桜の咲いている函館、最高でした。 来年は宮崎での開催ということで、今年よりも暖かそうですね。 来年の会場が発表される様子 より深い議論ができるようにこれからもRubyやRailsを追いかけていきたいと思います! Sansan技術本部ではカジュアル面談を実施しています Sansan技術本部では中途の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。
はじめに 技術本部 Quality Assurance Engineering Unitの杉本です。SETチームが発足したのは2023年6月。あれから3年が経ちました。最初の成果については以前SETチーム始動 Playwrightで実現した最初の成果 - Sansan Tech Blogで紹介しているので、本記事ではそこから先、この3年で積み上げてきたものと、直面した難しさ、そしてこれからの展望について書いてみようと思います。3年というのは、振り返ってみると短いようで長い時間でした。フレームワークの選定から始まり、社内への展開、そしてAIエージェントによる自動運用に至るまで、技術の話だけでなく組織や制度、QAという職能の意味合いまで考え続けた3年でもありました。少し長くなりますが、お付き合いいただけるとうれしいです。 この3年でやってきたこと 1. 始まりはBill Oneビジネスカードから 詳しくは前述の記事に譲りますが、ざっくり振り返ると、2人体制でスタートしたSETチームは最初の四半期でBill OneビジネスカードのE2Eテスト自動化を完了させ、2人で半日から1日かかっていたリグレッションテストを15分まで短縮することができました。E2EテストフレームワークにはPlaywrightを選定し、Page Object Model(POM)を採用。「プロダクトエンジニアもE2Eテストを書く」という思想を軸に据えた、最初の小さな成功体験でした。Bill Oneビジネスカードを最初の対象に選んだのは、当時リリースされたばかりのサービスだったからです。今後機能がどんどん追加されていくフェーズで、リグレッションテストの負荷もこれから急増していくことが見えていました。自動化の投資対効果が一番高いプロダクトを狙った、という言い方もできます。 2. プロダクトへの横展開 Bill Oneビジネスカードでの成果を皮切りに、当社の主要プロダクトへPlaywrightを広げていきました。順序としてはまずBill One全体に波及させ、続いてSansan、Contract One、Eightの順で展開しました。主要4プロダクトすべてにPlaywrightが入っている状態を、3年かけてつくることができました。プロダクトごとに使用言語も文化も異なる中で、ひとつのフレームワークでここまで横断できているのは、社内でもあまり例を見ないことだと思います。とはいえ、横断するうえで一番の壁は技術ではなく文化でした。「テストにどれだけコストをかけてよいのか」という考え方が、プロダクトによって本当に違うのです。リリース速度を最優先するプロダクト、自動テストへ手厚く投資したいプロダクト、業務委託の方による手動テストを中心に回しているプロダクト。それぞれに事情があり、それぞれに正しさがあります。加えて、各プロダクトはエンジニアの人数も業務委託の方の人数も多く、役割分担も異なるため、ステークホルダーが非常に多くなりがちです。誰がE2Eテストを書き、誰がメンテし、誰がレビューし、誰が壊れたときに直すのか。こうした役割を、プロダクトごとに改めて握り直す必要がありました。技術的にPlaywrightを入れるよりも、この合意形成のラウンドを回すことのほうが時間も気力も使った、というのが正直なところです。さらに、主要4プロダクトに加え、Sansan Labs、Auth One、デジタル名刺メーカーといった周辺プロダクトにもPlaywrightが入っていきました。SETはレビューや初期セットアップの支援といった形で関わり、結果として社内でPlaywrightが使われる範囲はじわじわと広がっていきました。 3. プロジェクト型Jump!という仕組み・人の課題に向き合う ここまで書くと拡張が順調に進んだように聞こえますが、横展開を進めるうえで一番の壁はやはり人の問題でした。SETチームに入りたいという人は決して多くなく、採用も簡単ではありません。そこで、期間限定で他部門からSETに移動し、もとのプロダクトに戻っていく仕組みを導入しました。当初は「留学制度」と呼んでいた仕組みですが、その後社内で整備が進み、現在は「プロジェクト型Jump!」という名称になっています。プロジェクト型Jump!は4カ月という期間で、これまでにContract Oneから1名が参加してくれました。Jump!参加者がContract Oneに戻ってからは、同プロダクトに自らPlaywrightを導入・運用するというサイクルが生まれ、SETチームが直接書かなくてもPlaywrightが広がっていく動線をつくることができました。体験談の記事もどうぞ。Jump into puddles〜SETエンジニアに転生してみた件〜 - Sansan Tech Blogただし、この仕組みで一番難しいのは技術ではなく評価の部分です。本人が一時的に所属している部門から外れるため、誰と・どのように評価を握るのかをあらかじめ決めておかなくてはなりません。さらに、4カ月間で何を達成できたら成功と言えるのかというゴール設定も、事前に合意しておく必要があります。これらを曖昧にしたまま走り出すと、本人が一番苦しむことになります。仕組みとして整備された今も、ここは毎回丁寧に握る必要があると感じています。 4. 少人数で広げきったこと 人の話でもう一点、誇りに思っていることがあります。この3年、SETチームは業務委託の方に入っていただく時期もありましたが、コアメンバーは入れ替わりを繰り返しつつも基本的に3人前後で推移してきました。3人前後で主要4プロダクトと周辺プロダクトをカバーし、さらにプロジェクト型Jump!を展開し、後述するAIエージェント群まで構築できたことは、素直に誇れる成果だと思っています。少人数だからこそ、自分たちだけで継続することを早々に諦め、留学制度やAIへと思想を寄せられたとも言えます。3人ですべてのプロダクトの手を動かそうとした瞬間に破綻するので、必然的に「自分たちで書かない仕組み」をつくる方向に振り切ることができました。 5. ジョブディスクリプションを作り続けたこと ここはあまり外に出してこなかった部分ですが、SETでは3年の中で「自分たちはどうあるべきか」というジョブディスクリプションをメンバーと何度も協議し、アップデートし続けてきました。バージョン1、2、3、3.1、3.2と、合計5回作り直しています。メンバーの入れ替わりがあり、横断するプロダクトが変わり、AIという前提が登場し、走りながら考えが変わる部分もある。だからその都度メンバーと話し、「何をやるべきか」「何をやらないべきか」を必ず再定義して進めてきました。SETの根本にあるのは「技術で品質を保証する」というシンプルな思想で、その品質がいかにエンジニアに届き、会社のバリューとして外に出ていくのか。そこを常に問い直してきました。技術選定や成果物よりも、このジョブディスクリプションを5回書き直してきたことこそが、3年で一番大事な営みだったと、いま振り返って思います。 6. AIによる自動生成と自動修復 そして、直近の大きな進展がAIの活用です。 きっかけ 正直に書くと、AIに踏み切ったきっかけは綺麗な話ばかりではありません。ひとつには、業務委託に依存している部分のリソースコストを最適化したい、という現実的な事情がありました。もうひとつは、QAがボトルネックと言われがちな構造があり、本当にボトルネックだったかどうかは別として、そう言わせない状態をつくる必要があった、ということです。速度を上げるための自動化、その自動化を回し続けるためのAI、という順序です。開発中に、PlaywrightからAgent機能が公式リリースされました。世界中で同じ方向に走っている人がいるとわかり、自分たちの方向性は間違っていなかったと確信できた瞬間でもありました。Playwright Agentsについては別記事で詳しく書いていますので、興味のある方はそちらもご覧ください。Playwright AgentsでE2Eテストの民主化を実現する - SEED構想の提案 - Sansan Tech Blog 仕組みの全体像 日々のメンテナンスのため定期実行を回し、結果はGitHub ActionsでSlackとNotionに連携、Notionにスタックされていく仕組みを構築しました。そのうえで、Generator系とOperation系という2系統のエージェントを実装しています。 Generator・テストコードを書くエージェント Generatorの役割は、Notionのテスト仕様DBに書かれたテストケースから、実際に動くPlaywrightのテストコードを生成し、PRとして上げるところまでを一貫して行うことです。具体的には次のような流れになります。仕様取得 → 新規ブランチ作成 → コーディング(UIコンポーネントの扱い、要素特定、テストデータ生成・識別などのSkillsを参照)→ 検証ループ(実際にPlaywrightで走らせる)→ 静的解析 → コードレビュー → PR作成。ポイントは、コード生成をワンショットで終わらせていないことです。生成→実行→エラー→修正、を内部で何度も回し、最終的に全テストが通った状態のものだけがPRに上がるように設計しています。さらに、UIコンポーネントの扱い方や要素特定の作法、データ生成・データ識別といったテスト固有のノウハウを「Skill」としてMarkdownで切り出し、エージェントが必要に応じて読み込む構造にしています。プロダクト固有の構造情報(Page Objectやアーキテクチャ)も別途定義してあり、テスト技法とプロダクト知識を分離するという設計思想を取っています。 Operation・エラーを仕分けし、原因を追い、自動で直すエージェント群 Operation側はGeneratorよりさらに分業されていて、実際にはSorter、Tracer、Healerの3つのサブエージェントが連携して動きます。1つ目がSorterです。定期実行で出たエラーログをNotionから拾い、既存の修正チケットとの類似性を判定します。エラーが新規なのか、過去に起きていたものの再発なのかを見極め、新規なら新規チケットを起票し、既存なら同じチケットに発生日情報を追記します。これで「いつ、何回起きているエラーなのか」が自動で蓄積されていきます。2つ目がTracerです。エラーがLocator系やAssertion系のものであれば、フロントエンドリポジトリのGit履歴・PR・Notion上のPBI(プロダクトバックログアイテム)を辿り、対応する変更を探します。スコアリングして「仕様変更の可能性が高い/可能性あり/手動調査推奨」のいずれかを判定し、仕様変更であれば修正提案まで生成します。3つ目がHealerです。Tracerが出した修正提案を、Git worktreeで隔離された環境に適用してPlaywrightで実行し、通ればそのままPRを作成します。失敗すればチケットに失敗理由を残します。つまりSorterで「これは仕様変更か不具合か」を見立て、Tracerで「どのコミット・PRが原因か」を突き止め、Healerで「直してPRを上げる」までを自動でやる、ということです。仕様変更による壊れは自動で修復され、不具合の可能性があるものだけ人間にエスカレーションされる。そういう運用フローになりました。結果として、テストコードの生成も、実行後のエラー仕分け・原因追跡・修復も、ほぼ自動化することができています。なお、このAI運用の取り組みは2026年3月のJaSST'26 Tokyoでも発表しました。生成AIで支える自動E2Eテストの継続運用 - Speaker Deck 苦労したこと 1. コスト削減というものさしのズレ E2E自動化はどうしても、時間短縮、リソース削減という文脈で語られがちです。確かに最初の成果はそこに表れますし、わかりやすい指標でもあります。ただ実際にやってみるとわかるのですが、テストコードのメンテナンスにかかるコストは決して小さくありません。QAの世界では、品質が良くなったことを数値で表すのが本当に難しいです。だからこそ、自動化率やテスト工数の削減といった、目に見えやすい指標が成果として持ち上げられがちで、それが本来の目的だと勘違いしてしまう人も少なくありません。結果として、とにかく自動化を進めようという案がどんどん上がってきます。けれど、その自動化したテストを誰がメンテし、何回実行し、どんな安心を担保するのか。その先の未来についてはあまり語られないのです。なので、SETに自動化してほしいという依頼が来るたびに、同じような会話を繰り返してきました。何回実行することを想定しているのか、そのテストが壊れたとき誰が直すのか、本当に自動化すべきシナリオなのか。中にはこちらがやりたくないだけと受け止めた人もいたかもしれません。それでも、先述した通り、ジョブディスクリプションのおかげで、SETとしての方針は3年間ぶれていません。コスト削減ではなく、何度も繰り返し実行できることそのものが価値である。この強い気持ちで言い続けてきました。繰り返し実行できることのメリットは、「いつもの仕様通りに動いている」という確認になり、担当者が代わっても安心できる土台になることです。コスト削減が結果的についてくることはあっても、それを目的に置くと、メンテコストとの綱引きで必ず破綻します。 2. 横断ゆえの限界 SETチームは各部門を横断する形で動いていますが、本来E2Eテストのコードを一番うまく書けるのは、そのプロダクトを深く知っているプロダクトエンジニアです。横断チームが3人前後で書き続けるモデルには構造的な限界があり、プロジェクト型Jump!の整備や、AIによる自動修復の構築は、この課題への現実的な回答でもありました。 3. 副産物としての発見 苦労ばかりではなく、副産物として得られたものもあります。E2Eテストのコードが、実質的に仕様書として機能する場面です。「検索した後に検索ウィンドウが表示されなくなった」というような、ドキュメントに落とし込まれておらず、人の記憶も曖昧な仕様について、E2Eテストのコードを読めば過去の挙動がわかる。これは当初想定していなかった効用でした。 3年間で自分自身に起きたこと 少しだけ個人的な振り返りをさせてください。私はもともとプロダクト開発を長くやってきた人間です。QAというポジションに就いていなくても、品質に頭を悩ませることはずっとありました。ただ、QAとしてのポジションに移り、第三者の視点から改めて品質を問い直す機会を持てたことは、自分にとって本当に良かったと感じています。JSTQB Foundation Levelも取得しました。正直に書くと、シラバスが直接実務で役に立つ場面はあまりありませんでした。テストケースありきでテストコードを書いてきましたし、プロダクト開発で品質に向き合ってきたため、テスト観点自体が大きく変わるということもありませんでした。ただ、JSTQBを通じてひとつ強く理解したことがあります。テストという行為に対して、人類はかなり体系的にナレッジを積んできているということです。シラバスに記された膨大な観点・技法・用語の整理は、それ自体が知の蓄積です。そして、ここから先の話なのですが、この体系的なナレッジをAIに持たせれば、AIはテストに関してもっと賢くなれるはずだ、ということもこのタイミングで実感しました。JSTQBは、人間がテストを学ぶためのものだと最初は思っていましたが、いまでは、AIにテストを学ばせるためのものでもあると考えています。E2Eテストを通して、ユニットテストの重要性については、3年経ってむしろ確信を深めました。E2Eテストでカバーできない粒度の品質保証、リファクタリング時の安全網、仕様としてのコードの読みやすさ、実行速度、いずれを取ってもユニットテストの果たす役割は大きいです。AIが入ってきて、テストのやり方そのものが大きく変わっていく時代に身を置けたことは、得難い経験でした。手動テストから、Playwrightを使ったE2Eテスト、さらにAIでテストコードが生成される、このテスト手法のあり方の変遷を経験できたことは、この3年を通して一番貴重な体験だったと思います。 これからどうなっていくのか E2E自動テストは、これからプロダクトエンジニア自身の手によって、あるいはAIの手によって、常に書かれ・修復され続けるものになっていきます。QAだから書く、SETだから書く、というポジションや概念を一度外して、誰でもE2Eテストを書き、メンテナンスできる状態にしていく。これがこれからの時代の潮流になっていくはずです。E2Eテストの民主化が進むと、エンジニアはコードを書く段階でテストにコミットするシーンが増えていくでしょう。設計・開発・テストというサイクルの中で、テストという工程はむしろ縮小していくと想像しています。これはとても良いことです。リリースサイクルは速くなり、ドメイン知識を深く持ったエンジニアがテストを書くほうが、結果的により高い品質を担保できるはずです。時間がなくて後回しにされがちだったテストが、最初から組み込まれた状態で進むようになる。これは大きな前進です。ではQAという職能が消えるかというと、私はそうは思っていません。AIがコードを生成するとき、AIはどうしても「テストを通すためのコード」を書いてしまう可能性があります。あるいは逆に、「コードを通すためのテスト」を書いてしまう可能性もあります。AIを役割ごとに分けて、批判的なレビューを別エージェントに任せる、そういう工夫はしていきますが、それだけではどうしても拾いきれない領域が残るはずです。そこで重要性を増すのが、人による探索的テストであり、第三者であることだと考えています。プロダクトの内側からは見えない違和感、仕様書にもテストケースにも書かれていない「ユーザーが本当に困るところ」を、第三者の視点で拾いに行く役割。これは、AIが進化すればするほど、むしろ希少価値が高まっていく仕事だと思っています。ユニットテストやE2Eテストの機械的な実行はAIに任せられる時代になりました。その先で、人がやるべきテストとは何か。この問いに向き合っていくことが、これから
こんにちは!中部支店で働く、CTO室の中村です。「Web系のエンジニアになるには、東京で働くのが当たり前」——そんな固定観念に対して、別の選択肢を提示してくれる人がいます。研究開発部ArchitectグループでEKSや監視基盤の運用を担う上田は、新卒1年目にして"中部支店勤務"を選びました。そこには、地方ならではの生活のしやすさや、働き方の工夫、そしてキャリアの考え方が詰まっています。このインタビュー記事では、上田が歩んできた技術の変遷から現在の業務、支店勤務を選んだ理由や暮らしの実感、これからの展望までを根掘り葉掘り聞きました。ぜひご一読ください。 プロフィール 上田 昂明 2025年、Sansan株式会社へ新卒入社。入社時から中部支店に勤務。研究員の成果物を本番利用するためアプリケーション実行基盤「Circuit」の運用やコスト削減、監視の最適化を行っている。趣味は、BBQ。 ←上田 中村→ 学生時代:興味はハードウェアからソフトウェア、そしてインフラへ —— まずは学生時代の話から。どんな技術に興味がありましたか? 工業高校だったこともあって、最初は電子工作が趣味でした。回路を設計して、ブレッドボードやユニバーサル基板でPoCを作って、ファームウェアを書いて、最終的にプリント基板に起こすというサイクルをずっと繰り返していました。マイコンはESP系列を一番触っていて、中でもESP32がお気に入りです。基板設計にはAutodeskのEagleを使っており、Fusion 360との連携の便利さが印象に残っています。ちなみに初めて買った機械はCNCです。高校卒業後はソフトウェアエンジニアリングにハマって、GoでバックエンドやCLIツールを書くようになりました。専門学校の2年次からは毎月のようにハッカソンに出て、Goでバックエンドを書く日々でした。そこから少し経った頃、高校時代のインフラへの興味が再燃しました。自宅サーバーにKubernetesを入れて運用し始めたのがきっかけで、どんどんインフラ寄りの領域にのめり込んでいきました。 —— インターンではどんな経験を? インターン(アルバイト)ではGoを使ったバックエンド開発が中心でした。インフラにはあまり触れず、がっつりバックエンドばかり書いていた記憶があります。 現在の仕事:研究開発部の"基盤"を支えるPlatformエンジニアリング —— 今の担当業務やロールを教えてください。 研究開発部のArchitectグループで、Platformエンジニア的な立ち位置でEKSや監視基盤の管理・運用をしています。(基板作りから基盤作りに進化しました) —— 具体的にはどんな業務がありますか? 基盤チームとして、共通利用できる抽象化コンポーネントの提供や新規機能検証 (直近だとDRAなど)、可観測性基盤の選定と導入、EKSクラスタの管理(バージョン更新やアラート対応など)まで、幅広いです。https://buildersbox.corp-sansan.com/entry/2024/11/14/110000 —— 直近の取り組みで印象的なものはありますか? 研究開発部の監視を強化しつつ、コスト予測などの最適化を目標にDatadogからNew Relicへの移行を進めています。3月下旬から導入を始めて、4月末で研究開発部環境ほぼすべての移行が完了しました。こちらの話は 5/27に Nagoya Tech Talk #3 〜CloudNative × Platform〜 - connpass で登壇するので興味があればぜひ! それと、Kubernetes WorkloadをSlack上から任意のタイミングで起動するControllerも実装しました。このあたりは別の記事で詳細をまとめる予定です。 Sansanに入社した理由: —— Sansanに入社を決めた理由は何でしたか? 大きく2点あります。 1つ目は、インターン時代※のチームの雰囲気がとても良かったことです。 2つ目は、名古屋でKubernetesを中心としたPlatformエンジニアリングのポジションがあったことです。 規模もそこそこ大きく、GPUなど変わったWorkloadが多い"珍しい基盤"に触れてみたいと思い入社しました。※当時のblog buildersbox.corp-sansan.com 中部支店を選んだ理由: —— 中部支店で働くことを選択した理由を教えてください。 生まれも育ちも岐阜で、この土地が好きだからです。中部支店なら岐阜から無理なく通える距離だったのも大きいです。 それ以外だと、東京よりも地元の生活環境のほうが自分に合っていた、というのも大きいです。生活を考えたとき、部屋の広さと通勤時間、コストの問題が大きいと感じました。 研修期間やインターン期間に東京エリアで1カ月程度住んでいたこともあります。しかし生活圏のスーパーが狭く高額だったり、生鮮食品がなかなかセールにならなかったりして、「これは自分には合わないかも」と実感しました。また評価や給与面など、どの地域にいても本社基準から変わらないことが大きかったです。 拠点も広すぎず、オフィスから外に出るまで1分程度なのでコンビニや食事処まですぐに行けるのも魅力です。 参考blog buildersbox.corp-sansan.com buildersbox.corp-sansan.com 日常の働き方: —— 本社チームとは普段どうやってコミュニケーションをとっていますか? 組織編成の変更などもあり、今はチーム全員が東京にいます。普段のコミュニケーションはオンラインが中心で、特にGoogle Meetを使って朝会や週次の〆会などを行っています。同期的なタスクが少ないため、リモートでも不自由なく業務を進められています。必要に応じて本社への出張も可能です。 —— 出社頻度や本社出張の頻度は? 中部支店への出社頻度は週2〜3日です。本社へはバラつきがありますが、均すと月1回程度出張しています。 —— 出社するときのモチベーションはどう高めてますか? 在宅時に比べて、出社時はランチを食べるようにしているので、いつもよりちょっといいものを食べることをモチベーションにしています。仕事のモチベーションにつながりづらいのと、開拓しつくしたときに飽きそうなので、今のうちに別の出社モチベーションも探すようにしています。 qu’il fait bonのメロンタルト 生活: —— 中部エリアでの暮らしはどうですか? 生活の質(QoL)はかなり高いと思っています。BBQをやる仲間が近くにいるので、週末に楽しめるのがいいですね。庭先でBBQできるのも高評価です。スーパーが近くに3件くらいあって、ホームセンターや家電量販店も2〜3店舗あるので、生活に困らないです。大型商業施設なども車を出せば行けるので不自由ないですね。 —— エンジニアコミュニティーとの接点はありますか? 名古屋発のコミュニティーは少ない印象ですが、NGKやJAWSなどがあるので参加できるときは参加しています。また、東京まで2時間あれば行けるので、気になるイベントはあらかじめ予定を作って参加しています。 これから: —— 2年目以降、どんなことにチャレンジしたいですか? 1年目はEKSクラスタ更新の検証やNew Relicなどのツール導入、コスト削減施策などをやってきました。2年目は以下に取り組みます。 New Relicを中心に部内の可観測性を向上 開発者によるセルフサービス化を軸に部内の新規オンボーディングや障害、大規模アップデートなどの課題を解決 最後に: —— 地元で働くことを考えている学生にメッセージをお願いします。 昨今のITエンジニアの就職=東京、という固定観念にとらわれる必要はないと思います。自分のライフスタイルに合わせて地元(から通える場所)を選ぶのもいいと思います。ただ、チームメンバーが分散しているとコミュニケーションコストが増加したり、存在感が薄くなったりする課題もあるので、天秤にかけてじっくり選んでください。 —— 最後に、本社配属を選ばなかったことに後悔はありますか? 一切ないです!都会の喧騒に疲れた方、レジャーなどが好きで毎週末行きたい方、オフィスの近くに住みつつ車を持ちたい方にはおすすめです! Sansan技術本部ではカジュアル面談を実施しています Sansan技術本部では中途の方向けにカジュアル面談を実施しています。 Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。 「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。
はじめに こんにちは。技術本部 Sansan Engineering Unit Mobile Applicationグループの上野(@cardseditor)です。2025年4月に新卒で入社し、SansanのAndroidアプリ開発に携わっています。 Sansanでは、iOS版で先行リリースされたAI音声入力機能をAndroidでも提供するための開発を進めてきました。商談中の音声を録音し、文字起こしと要約までを一連の体験として提供する機能です。 jp.corp-sansan.com 録音機能は、一見するとマイクを起動して音声を保存するだけのシンプルな機能に見えるかもしれませんが、実装に取り組む中で、録音という単機能だけでも考慮すべきことが非常に多いことに気づきました。 本記事で扱う観点は以下の6つです。Androidで録音機能を実装する方の参考になれば幸いです。 録音設定の選定 - コーデック・サンプルレート・ビットレートの決め方 音量の可視化 - 振幅をそのまま使うとなぜ波形が小さく見えるのか 波形アニメーション - 100ms間隔の振幅データを補間した滑らかな描画 バックグラウンドでの録音継続 - Foreground Serviceの位置づけ マイク割り込みへの対応 - 通話や他アプリのマイク占有に耐える設計 タイマーと経過時間 - スリープ中も進む単調増加クロックによるセグメント累積方式 1. 録音設定の選定 MediaRecorder と AudioRecord Androidで録音する手段は大きく2つあります。 MediaRecorder - 音質パラメータを設定し、エンコード済みの音声ファイルを直接書き出す高レベルAPI AudioRecord - 生のPCMバッファを取得できる低レベルAPI iOSではサンプルレートごとのバッファデータを直接扱い、バッファ単位の書き込みや音量の波形表示を実現していました。AudioRecordのほうがiOSと近い実装になりますが、私たちはMediaRecorderを採用しました。 理由は次の2点です。 データサイズ 生PCMで2時間録音すると、サンプルレート44.1kHz/モノラル/16bitの前提で600MBを超える。本記事で採用する16kHzでも約220MB。圧縮済みのM4Aで書き出せば、同じ2時間でも数十MBに収まる。 実装コスト AudioRecordを使う場合、PCMからM4Aへのエンコードを自前で実装する必要がある。低レイヤーのハンドリングは保守コストが高い。 クラウドAPIにアップロードして文字起こしする以上、データサイズはユーザーの通信量に直結します。録音中の波形表示も、後述するMediaRecorder.getMaxAmplitude()だけで実現できる目処が立っていたため、低レベルAPIを採用する積極的な理由はありませんでした。 コーデック・サンプルレート・ビットレートの選定 設定値の方針は「文字起こし精度とデータサイズから設定値を決める」というものです。録音単体ではなく、後段の文字起こしの入力でもあるため、必要十分な品質を探って設定しました。 項目 設定値 意図 ファイル形式 M4A(AAC) 標準的な非可逆圧縮形式で、同じビットレートでmp3より高音質、または同等音質をより低ビットレートで実現できる サンプルレート 16kHz 話し声の主要帯域(〜4kHz)に十分な余裕を持つ ビットレート 48kbps 2時間録音で約43MBに収まり、文字起こし精度とファイルサイズのバランスが取れる 2. 音量の可視化 getMaxAmplitude()だけでは波形が小さく見える 録音中に「音が入っているか」を効果的に表現するため、波形を表示しました。 MediaRecorderにはgetMaxAmplitude()というAPIがあり、前回呼び出し以降にサンプリングされた最大絶対振幅を返してくれます。これを一定間隔で呼べば、各区間の最大音量が取れます。 単純に振幅をそのまま正規化して波形に当てはめると、こうなります。 val normalizedVolume = amplitude.toFloat() / Short.MAX_VALUE ところが、この方針で実装してみると普通に話している声でも波形がほとんど動かず、会話レベルの声ではほぼフラットな波形しか得られません。 Weber-Fechnerの法則 この違和感の原因は、人間の聴覚特性にあります。Weber-Fechnerの法則によると、人間が感じる「感覚の強さ」は、受ける「刺激の強さの対数」に比例します。 静かな環境では音量のわずかな変化に敏感ですが、騒がしい環境では同じ大きさの変化に気づきにくくなります。つまり、振幅をそのまま線形にマッピングしても、人間の感覚には合いません。 これをデジタル音声に当てはめるなら、振幅をdBFS(フルスケール基準のデシベル)値に変換することで感覚に近い表現になります。 正規化の3ステップ 振幅を「人間の感覚に合った0〜1」に変換するため、次の3ステップで処理します。 Step 1: 振幅正規化 生の振幅 (0〜32767)を0〜1に正規化します。 Step 2: dBFS変換 対数を取ってdBFS値に変換します。 は の発散を避けるための下限値(実装では0.0001)です。これにより は約 〜 の範囲に収まります。 Step 3: 表示用の正規化 表示に使う0〜1の範囲に変換します。 ※ Kotlinのx.coerceIn(a, b)を便宜上 と表現しています。 はdB調整範囲で、実装では40dBにしました。 まとめると次の式になります。 コードに落とすと、こうなります。 private const val MAX_AMPLITUDE = Short.MAX_VALUE.toFloat() // 16bit PCM private const val EPSILON = 0.0001f private const val DB_RANGE = 40f fun convertAmplitudeToNormalizedValue(amplitude: Int): Float { val normalizedVolume = (amplitude / MAX_AMPLITUDE).coerceIn(0f, 1f) val db = 20f * log10(max(normalizedVolume, EPSILON)) return ((db + DB_RANGE) / DB_RANGE).coerceIn(0f, 1f) } この変換を入れると、会話レベルの音量変化に追随して波形が変化するようになります。線形な振幅ではなく、人間の感覚に合わせた可視化を、数行の数式で実現できます。 3. 波形アニメーション 100ms間隔の更新だけでは描画がカクつく dBFS変換で波形は動くようになりましたが、100ms間隔で更新するとコマ送りのように見えます。バーを横に流していくUIだと、移動が0.1秒刻みになるためカクついて見えるのです。 静かな環境ではさらに問題が顕著です。振幅の閾値以下が続くと波形が完全に止まってしまうため、「音が入っているか不安」というユーザー体験になりかねません。 withFrameMillisでフレームごとに描画する そこで、波形の位置を時刻 の関数として表現する方針に切り替えます。バーは100msごとに新しいデータが入ってきますが、その間も等速で左に流していけば滑らかに見えます。 Jetpack ComposeにはwithFrameMillisというAPIがあります。新しいフレームの描画が要求されるまで待機し、要求された瞬間にフレーム時刻(システム起動からのミリ秒)を渡してくれます。差分を取ることでフレーム間の経過を扱えます。これを使うと、フレームレートに合わせた滑らかなアニメーションが書けます。 private const val SAMPLE_INTERVAL_MS = 100L @Composable fun WaveformDisplay( bars: List<Float>, tickTimestamp: Long, isRecording: Boolean, modifier: Modifier = Modifier, ) { <sp
はじめに こんにちは!技術本部 Sansan Engineering Unit Mobile Applicationグループに所属しているiOSエンジニアのヤズジュ夢佐です。 ビジネスデータベース「Sansan」は、アプリを開いて録音を開始するだけで、商談中の会話を自動で文字起こしし、AIが要点を整理して商談記録を作成してくれる機能をリリースしました。 2025年11月にiOSの一部の端末へ向けて限定公開されましたが、2026年4月にiOS・Androidすべての端末へ公開されました。 prtimes.jp 本機能の中核となる文字起こし処理は、ユーザーがアプリを閉じてもBackgroundで継続される必要があります。 Androidでは、Background処理に対してOSレベルで細かなリソース制限が課されているため、これらを正しく理解した上で実装する必要がありました。 本記事では、制限が強まり続けているAndroid Background処理、16年の歴史を振り返ります。 そして、実際にSansanアプリの中でどのようにBackground処理を扱っているのか、文字起こし機能を例に解説します。 AndroidにおけるBackground処理制限の歴史 Androidは、登場初期は非常に自由なBackground処理が可能でしたが、ユーザー体験を守るためにバージョンを重ねるごとに段階的に制限が強化されてきました。 ここからは、各メジャーバージョンでどのような制限が導入されてきたのか、順を追って解説していきます。 なお、本記事ではすべてのBackground関連のアップデートを網羅しているわけではなく、筆者が独断で特に大きな変更だと判断したものを選び抜いて紹介しています。 Androidの初期 (〜 Android 5): 制限のない自由なBackground処理 初期のAndroid、特にAndroid 5未満の時代は、Background処理に関して非常に自由かつ無秩序でした。 これは、当時のAndroidが「ユーザーにアプリの終了を意識させない」「すべてのアプリが常に動いているように見せる」という設計思想を取っていたことに由来します。 2010年4月、Android公式ブログに投稿された "Multitasking the Android Way" には、当時の設計上の制約と思想について次のように述べられています。 We did not want to require that users close applications when "done" with them. (...) Switching between applications must be fast, since users tend to switch between applications very frequently. The requirement here was that the launch of an application from the background take significantly less than 1 second in addition to any inherent work the application needs to do. Android Developers Blog - Multitasking the Android Way (28 April 2010) Androidはこれらの制約を満たすため、ユーザーがアプリを離れてもプロセスをBackgroundに残し続け、見かけ上すべてのアプリが常に動いているような状態を維持する設計を採っていました。 When the user leaves an application, its process is kept around in the background, allowing it to continue working (for example downloading web pages) if needed, and come immediately to the foreground if the user returns to it. If a device never runs out of memory, then Android will keep all of these processes around, truly leaving all applications "running" all of the time. Android Developers Blog - Multitasking the Android Way (28 April 2010) また、同記事ではBackground処理に関するAPIの「すべてのアプリに平等に提供する」という思想についても触れられています。 Googleの組み込みアプリで使われるBackground音楽再生・データ同期・GPSナビ・ダウンロードなどの処理は、サードパーティ開発者が同じAPIを使って実装できる形で公開されていました。 The available APIs must be sufficient for Google's own built-in applications to be written using only public APIs and be able to run as a normal application that can be installed or removed by the user. (...) This includes things like background music playback, data syncing, GPS navigation, and application downloading. Android Developers Blog - Multitasking the Android Way (28 April 2010) このように、初期AndroidのServiceは強力なBackground処理APIとして公開されており、非常に自由度が高い設計となっていました。 自由すぎて発生していた問題 しかし、この自由さは多くの問題を引き起こしていました。 起動が重い アプリは「端末の起動完了 BOOT_COMPLETED」を検知し、なんらかの処理を行うことができました。 これによって、当時はスマホを起動した瞬間に数十のアプリが一斉にBackgroundサービスを開始しており、これが起動時間の低下と起動直後の重たい状態を引き起こしていました。 2014年頃のユーザーフォーラムでは「インストールしたアプリのほとんどが端末起動時に立ち上がる」という不満が投稿されています。 So many downloaded apps start at device boot. Things like facebook, eBay, Tapatalk, gmail, camera apps. Why do so many apps start at boot? - Android Central Forum (10 July 2014) 寝ている間にバッテリーがゼロになる Androidには、画面がオフになってもCPUをスリープさせないWakeLockという仕組みがあります。 当時は開発者がWakeLockを取得してreleaseし忘れると、画面が消えているのにCPUだけが動き続けてしまう症状が発生していました。 2014年のフォーラムには、Wake Lock起因のバッテリー消費に苦しむユーザーの悲鳴が残っています。 Charge it up at night, place it by my bed and it's completely dead in the morning! So it loses 100% battery overnight, with no usage whatsoever. Wakelocks and Massive battery drain! - XDA Forums (25 April 2014) 特定の時間に処理を行うAlarmManagerという機能もありますが、Android 4.4以前はsetRepeating()で正確なタイミングでのアラーム発火が保証されていました。 裏で定期実行するアプリが複数あると、それぞれが個別にデバイスを起こすため、バッテリーを消費し続けます。 As of Android 4.4 (API Level 19), all repeating alarms are inexact alarms. Schedule alarms - Android Developers メモリ不足によるフリーズ・ガタつき 多くのアプリが常駐するので、重いゲームなどを起動しようとすると、OSが必死にBackgroundアプリを殺してメモリを確保する必要があり、その間操作がフリーズしました。 When playing heavy 3d games like Nova 3 or Sonic, the framerates are really slow or stutter a lot until the games force-close. Confirmed temporary solution to game freezing and browser/system lag - XDA Forums (4 June 2012) Android 5: Battery Saver modeの登場とProject Volta このバッテリー問題に対処するため、Googleは2014年のGoogle I/OでAndroid 5.0 Lollipopの目玉機能のひとつとして "Project Volta" を発表しました。 これはバッテリー寿命の改善を主目的とした一連の取り組みで、その中心となる施策のひとつが、Android標準機能としてのBattery Saver modeの導入でした。 www.youtube.com Battery Saver mode Battery Saver modeは、バッテリー残量が一定値を下回ったときに自動で発動するOSレベルの省電力モードです。 発動すると、デバイスのパフォーマンスやBackgroundデータ通信の制限などが行われ、全体的な制限がかかるようになりました。 それまでも一部のOEM各社は独自のPower Saving Modeを搭載していましたが、Android 5でOS標準機能として導入されたことにより、すべてのAndroid端末に共通のBackground処理制限の仕組みが提供されるようになりました。 Google added a "Battery Saver" mode to Android with Android 5.0 Lollipop. (...) When Battery Saver is enabled, Android will decrease your device's performance to save battery power (...) Most background data usage will also be restricted. (...) Some manufacturers offer their own battery saver modes. For example, Samsung offers an "Ultra Power Saving Mode," HTC offers an "EXTREME Power Saving Mode," and Sony offers a "STAMINA mode" and "Low battery mode." How to Use and Configure Android's "Battery Saver" Mode - How-To Geek JobScheduler API Project Voltaのもうひとつの中心的な施策が、JobScheduler APIの導入です。 これは、Backgroundで実行したいタスクを「特定の時刻」ではなく「条件」(充電中、Wi-Fi接続中、アイドル中など)で予約できる新しいAPIです。 OSは複数アプリのジョブをまとめて、デバイスの起動回数を最小化します。 これにより、開発者が指定したタイミングで個別にCPUを起こすのではなく、OS側がBackgroundタスクの実行タイミングを統括し、リソースを節約する設計への転換が始まりました。 New job scheduling APIs allow you optimize battery life by deferring jobs for the system to run at a later time or under specified conditions, such as when the device is charging or connected to Wi-Fi. Android 5.0 APIs - Android Developers Android 6: Doze modeとApp Standbyによる制限導入 Android 6では、Doze modeとApp Standbyという2つの仕組みが導入されました。 Doze mode Dozeは「居眠り」「軽い短い睡眠」という意味です。デバイスが充電されておらず、静止した状態で、画面がオフになってから一定時間が経過した場合、Doze modeに入ります。 When a user leaves a device unplugged and stationary for a period of time, with the screen off, the device enters Doze mode. Optimize for Doze and App Standby - Android Developers Doze modeでは、システムがバッテリー消費を制限するために、アプリがネットワークやCPUに高い負荷がかかる処理を行わないように制限します。 Doze modeに入ったあとは、定期的にDoze modeを短時間だけ抜け出すmaintenance windowが用意され、この時間にアプリが処理を行うことが可能です。 これによって、無秩序な時代に抱えていた「寝ている間にバッテリーがゼロになる」問題がある程度解消されました。 App Standby Doze modeが端末未使用の状態で発動するのに対し、App Standbyは端末を使用中であってもBackground状態のアプリに対して制限をかけます。 App Standby lets the system determine that an app is idle when the user isn't actively using it. The system makes this determination when none of the following conditions applies: The user explicitly launches the app. The app has a process currently in the foreground, either as an activity or foreground service, or in use by another activity or foreground service. The app generates a notification that users see on the lock screen or in the notification tray. Optimize for Doze and App Standby - Android De
はじめに こんにちは。技術本部CTO室、中部支店勤務の中村です。 先日、4/23にオフラインイベント「Sansan Tech Talk @関西 vol.3 〜データ活用のリアル〜」をSansan関西支店にて開催しました。 本記事では、イベントの様子を簡単に振り返ります。 前置き:そもそもSansan Tech Talkとは? Sansanは現在、地方拠点のエンジニア採用に力を入れており、その一環として各拠点で技術イベントを開催・共催しています。各拠点に所属するメンバーや地域コミュニティーの傾向などを考慮してイベントを設計しており、2026年4月時点では、 関西支店では、「Sansan Tech Talk」シリーズ(#kansai_33techtalk) 福岡支店では、「Fukuoka_33tech」シリーズ(#fukuoka_33Tech) 中部支店では、「Nagoya Tech Talk」シリーズ(#nagoya_tech_talk) を開催しています。また、シリーズものではありませんが、京都にはSansan Innovation Labという拠点もあり、「ICLR2026 論文読み会」などのアカデミック向けイベントを開催しています。 本編:イベントレポート 本イベントでは、Sansanのエンジニア3名と一般応募枠2名によるデータ活用に関する実践知の共有LT、および懇親会を行いました。 オープニング オープニングでは、CTO笹川からSansan関西支店の紹介がありました。関西支店は、大阪駅や西梅田駅、北新地駅などから堂島の地下街を抜けて徒歩5〜10分ほどでアクセス可能です。エンジニアは30名ほどが所属しており、地方拠点の中でも最大規模の支店です。 また、関西支店を含めた地方拠点のエンジニア採用に力を入れている背景や地方拠点勤務のメリットなどの話もありました。詳細が気になった方は、ぜひこちらの記事をご覧ください。 buildersbox.corp-sansan.com Talk1. 運用システムにおけるデータ活用とPlatform トップバッターはPlatform Engineering Unit所属の水谷から、Platform Engineeringにおけるデータ活用についての発表でした。 インフラやアプリケーションのログ・トレースなどのデータは、活用はおろか収集すらできておらず、障害調査や設定が属人化しやすい——そんな課題はよくありますが、Sansanでも例外ではありませんでした。 そこでPlatform Engineering Unitでは、Orbitというコンテナアプリケーション基盤を開発・提供しました。Orbitを利用することで、アプリケーション開発者は運用データを意識することなく、整備された形で収集できます。また、アラート原因調査においてもGemini Cloud Assist Investigationsを活用し、膨大なテレメトリデータから原因特定の仮説を提示することで、調査の属人化を防ぐ仕組みを提供しているという内容でした。 CTO室でもAIアプリケーションを開発しており、その一部をOrbit上で稼働させていることから恩恵を実感しています。(Orbitについては実録・Platform Engineering 失敗から学び、AI時代の波を乗りこなす技術 - Speaker Deck にて詳しく紹介しています。) speakerdeck.com Talk2. これからの「データマネジメント」の話をしよう Sansan事業部プロダクト室の坂口からは、AI時代の今、そしてこれからに求められるデータマネジメントについての発表がありました。 データ基盤の技術スタックや手法が進化・確立し、構造化データのマネジメントは成熟しつつある一方、生成AIが普及する中では、構造化データに加えて、外部のドメイン知識や業務コンテキストを構造化したナレッジ基盤を構築することが重要である。そうすることで、AIによるデータ分析の精度向上や、これまで人手で行ってきた構造化データ整備の自動化につながるという内容でした。 また、実際にこの取り組みの検証として進めている、Bill Oneデータ分析エージェントの構成についても紹介されました。 speakerdeck.com Talk3. Data Enabling Teamのこれから CTO室の矢田は、Data Enabling Teamの立ち上げとこれからの取り組みについて発表しました。 Sansanのデータエンジニア組織は少人数の職人集団で構成されており、データ活用ニーズが高まるにつれ、各事業部から寄せられる分析依頼やダッシュボード作成依頼などがボトルネックになりやすいという課題がありました。 その課題に対処すべく、「データエンジニアはデータ基盤を作りデータを提供する」から「現場がデータで意思決定できる状態を作る」への転換を目指して、Data Enabling Teamを立ち上げました。 そして具体的な取り組みとして、「過去の分析依頼内容を元にした、AIによる一次回答の自動提供」や「法務部・情報セキュリティ部と連携した、データアクセス管理統制の最適化」を進めているという内容でした。 speakerdeck.com Talk4. AI時代に増えるデータ活用先 LT登壇枠一人目は、STORES 株式会社の小野さん(https://x.com/taaaaaaakayuki) による、全社のデータ利活用促進に向けた取り組みの発表でした。 STORESでは、エンジニア以外の職種の方にもClaude Codeを導入してEnablingを行うことで、データ抽出や分析のハードルを下げているとのことでした。また、データの可視化についても、既存のBIツールを提供するのではなく、フロントエンドとSQLを書けばデプロイできるダッシュボードアプリケーション基盤を提供することで、誰でも手軽にデータを可視化できる仕組みを整えているそうです。 ダッシュボードはコード管理されないことが多く無法地帯になりがちで、その回避策としてBI as Codeのソリューションが選ばれることが多いと思いますが、スクラッチアプリケーションを提供し、誰でもAIコーディングエージェントを用いて開発できるようにするというアプローチは非常に興味深いものでした。 speakerdeck.com Talk5. データ定義の混乱と戦う 〜管理会計と財務会計〜 LT登壇枠二人目は、株式会社ヌーラボの尾上さん(https://x.com/wonohe)による、泥臭い会計データに向き合ったという内容の発表でした。 尾上さんが所属する部署では、上場準備のためにIRデータを整備するデータ基盤を構築することになり、数字の正確性が求められたため、複雑な計算ロジックを元に実装されたそうです。 上場後、マーケティングメンバーが意思決定のためにIRデータを参照した際、数字に違和感があるという事態が発生しました。 原因は、IRとマーケで求められるデータ定義が異なっていたことでした。尾上さんは、IRデータ(財務会計)とマーケデータ(管理会計)の定義の違いを明確にして両者を分けることで、データ定義の混乱と戦ったと語られていました。 私も日々、SFAなどの営業・契約データを元にした分析を行っており、定義や解釈の違いに悩まされることが多いので、とても共感できました。 speakerdeck.com 懇親会 トークセッションの後には懇親会も行われました。 さまざまな職種の方が参加されており、登壇内容を深掘りした議論や、各社のデータ活用に関する課題・取り組みについての情報交換が行われていました。私は関西支店でのエンジニアイベントに参加するのは初めてだったのですが、拠点エリアが異なるため普段はお会いする機会のない方々と交流でき、貴重な体験となりました。 おわりに 改めて、ご登壇いただいた皆さま、会場にお越しいただいた参加者の皆さま、本当にありがとうございました。 5/21(木)にも関西支店で次回イベント「Sansan Tech Talk @関西 vol.4 〜データ正規化の深淵〜」を予定しておりますので、ぜひ奮ってご参加ください!! また、5月には中部支店、Sansan Innovation Lab(京都)でもイベント開催を予定しておりますので、こちらもぜひご参加ください。 5/27(水) 19:00~ Nagoya Tech Talk #3 〜CloudNative × Platform〜 5/28(木) 19:00~ 【ハンズオンあり】MLOps勉強会 @京都 Sansan技術本部ではカジュアル面談を実施しています Sansan技術本部では中途の方向けにカジュアル面談を実施
Sansan株式会社は、「TSKaigi 2026」にSilver Sponsorとして協賛します! 本記事では、TSKaigi 2026への協賛について、背景や登壇情報をご紹介します。 TSKaigi 2026について TSKaigi 2026は、TypeScriptに関わるエンジニアが集まり、知見や経験を共有する国内最大級のカンファレンスです。 フロントエンドからバックエンドまで、TypeScriptに関する幅広いテーマのセッションが展開されています。 Sansanでも複数のプロダクト開発においてTypeScriptを活用しています。 開催日時:2026年5月22日 (金)~23日 (土) 会場:ベルサール羽田空港 2026.tskaigi.org 協賛の背景 Sansanでは、「出会いからイノベーションを生み出す」というミッションのもと、さまざまなプロダクトを開発しています。 その中でTypeScriptは、フロントエンドだけでなくバックエンド開発にも活用されており、プロダクトの品質向上や開発効率の改善を支える重要な技術の一つとなっています。 また、社内では技術イベントへの登壇やTech Blogなど、技術的な知見の発信にも取り組んでいます。 TSKaigi 2026への協賛を通じて、Sansanの取り組みや知見を広く発信できればと考えています。 登壇内容のご紹介 Sansanからは3名のプロポーザルが採択されましたので、セッション登壇を予定しています。 関係性から理解する"同一性"の型用語たち 日時:2026年5月22日 (金) 11:50~12:20 場所:UPSIDERトラック 発表者:Eight Engineering Unit 鳥山 TypeScriptの話題において、nominal / structural / branded / opaque / phantomといった型に関する用語を見かける機会が増えてきました。 しかし、これらの違いや関係性を説明しようとすると曖昧になりがちです。 私自身も「なんとなく分かったつもり」で流してしまった過去があり、後からうまく説明できずに後ろめたさを感じる場面がありました。 本発表ではTypeScriptが構造的型付けであることを起点に、型の同一性に関するこれらの用語を、コード例を交えて紹介します。 そのうえで、これらの違いと関係性を理解し、解説記事や議論を追える状態を目指します。 childrenの順序まで型で縛る ── Branded Typesで実践するJSXの構造安全 日時:2026年5月23日 (土) 16:40~17:10 場所:Leveragesトラック 発表者:Eight Engineering Unit 藤野 JSXのchildrenに何が来るべきか、順序はどうあるべきか、ReactNodeでは全部素通りで、型が構造を守ってくれる世界になっていません。 このセッションでは、Branded Typeでコンポーネントのreturn型に「slot名」を付け、childrenをタプル型で制約することで、構造の間違いをコンパイル時に弾くアプローチを紹介します。 asアサーションをライブラリ内部に閉じ込めて利用側は何も気にしなくていい設計や、JSX記法だとブランドが消える問題とそのworkaroundにも触れます。 string地獄を脱出する — ValueObject + Zod 実践パターン 日時:2026年5月23日 (土) 16:40~17:10 場所:Leveragesトラック 発表者:Digitization部 高田 string や number だけでドメインの値を扱っていると、本来は別物である値を誤って渡しても型で検知できず、実行時の不具合につながることがあります。 この問題を避ける方法の一つが ValueObject ですが、素朴に実装すると、型定義・バリデーション・生成処理が値ごとに重複し、保守が大変になります。 本セッションでは、Zod による型とバリデーションの一元管理に加え、Plop を使ったコード生成を組み合わせることで、ValueObject を型安全かつ DRY に運用する実践パターンを紹介します。 実際のプロダクト開発で "string 地獄" から抜け出すための、現実的な設計と運用の工夫をお話しします。 最後に 当日は登壇者の3名をはじめ、Sansanのエンジニアも複数参加予定です。 会場でお見かけの際は、ぜひお気軽にお声がけください! Sansanはこれからも、プロダクト開発や技術発信を通じて知見をお届けしていきます。 TSKaigi 2026に参加されるみなさんにも、セッションを通じてSansanの取り組みをを知っていただければ幸いです。
はじめに こんにちは!技術本部Sansan Engineering Unit Mobile Applicationグループに所属しているiOSエンジニアのヤズジュ夢佐です。 Sansan iOSアプリは、2025年11月に、アプリを開いて録音を開始するだけで、商談中の会話を自動で文字起こしし、AIが要点を整理して商談記録を作成してくれる機能をリリースしました。 prtimes.jp 本機能に搭載されている録音画面では、録音中の音声をリアルタイムで波のようなアニメーションとして描画する機能が実装されています。 このアニメーションの実装戦略について解説します。 今回解説する機能 まずは今回実装方法を紹介する機能がこちらです。 録音中に、リアルタイムで右から左へ波が流れていきます。 波の高さは録音中の音声の音量が大きいほど高くなり、小さいほど低くなります。 録音が停止すると波のアニメーションも停止し、再開されると波のアニメーションも再開します。 本機能では最大2時間の録音をサポートするため、長時間でも耐えられるパフォーマンスが求められます。 https://youtube.com/shorts/Im0KJmiThXM?feature=share 実装戦略 録音した音声をリアルタイムで波に変換して描画するという処理は、次のような流れに細分化できます。 録音中のデータをストリーミング形式で細かく受け取る 受け取ったデータから音量の値を取得する 音量の値から描画すべき波の高さを計算する 波として描画する これらの処理を一つひとつ分解して解説していきます。 1. 録音中のデータをストリーミング形式で細かく受け取る 録音中の音声を、Bufferという切り分けられた単位で取得します。 AVAudioRecorderは不採用 iOSで録音を実装する場合、最もシンプルな方法はAVAudioRecorderを使うことです。 developer.apple.com AVAudioRecorderはシンプルで扱いやすいものの、録音中のデータをリアルタイムに扱うAPIは提供されていません。 Apple公式ドキュメントでは、高度な録音関連の処理には代わりにAVAudioEngineを使うことが推奨されています。 For more advanced recording capabilities, like applying signal processing to recorded audio, use AVAudioEngine instead. AVAudioEngineを採用 AVAudioEngineは、連結されたNodeの流れを通じて音声データを処理できる、より低レイヤーなAPIです。 inputNodeはマイクなどの入力デバイスから音声データが流れ込むNodeで、AVAudioEngineのプロパティとして最初から用意されています。 本機能では、inputNodeの直後にAVAudioSinkNodeを接続してBufferを受け取る方法を採用しました。 AVAudioSinkNodeは、流れてきた音声データをBufferとして一定間隔で切り分けて取り出すためのNodeです。 音声データのフォーマットは接続される入力ハードウェアに依存しますが、iPhoneやiPadの内蔵マイク、AirPodsなどを使用している場合、Sample Rateが48000Hz、1Bufferあたり1104Frameで切り出されます。 1104 / 48000 = 0.023なので、23msごとに1つのBuffer、つまり1秒間のデータが43~44のBufferへと切り分けられることになります。 AVAudioSinkNodeをAVAudioEngineのinputNodeに接続するコード例はこちらです。 let sinkNode = AVAudioSinkNode { [weak self] timestamp, frameCount, audioBufferList in // 23ms分の音声データが入ったBufferを用いて任意の処理を行うことが可能 } audioEngine.attach(sinkNode) // AVAudioEngineにNodeを接続する audioEngine.connect(inputNode, to: sinkNode, format: recordingFormat) // inputNode -> sinkNodeへ繋げる 2. 受け取ったデータから音量の値を取得する SinkNodeから受け取ることができるBufferに入っているのは、Core Audioのデータ構造であるAudioBufferListです。AudioBufferListはC言語ベースの構造体で、データの中身を解釈するための便利なAPIは提供されていません。 音声データへ型安全にアクセスできるSwift向けのラッパーとしてAVAudioPCMBufferが用意されています。 そのため、AudioBufferListに含まれるデータをAVAudioPCMBufferに詰め替えます。 let ablPointer = UnsafePointer<AudioBufferList>(audioBufferList) let audioBuffer = ablPointer.pointee.mBuffers guard let buffer = AVAudioPCMBuffer( pcmFormat: recordingFormat, frameCapacity: frameCount ) else { return noErr } buffer.frameLength = frameCount let sourceData = audioBuffer.mData?.assumingMemoryBound(to: Float.self) if let bufferChannelData = buffer.floatChannelData, let source = sourceData { // iPhone内蔵マイクやAirPodsなど、ユーザーが利用する大半のマイクはモノラル入力(channel数が1)である。 // ステレオ入力(channel数が2)の場合でも、波の表現には1つのchannelデータで十分なため、1つ目のchannelだけ使用する。 bufferChannelData[0].update(from: source, count: Int(frameCount)) } 3. 音量の値から描画すべき波の高さを計算する AVAudioPCMBufferから取得できる音量の値を元に、波として描画すべき高さを計算します。 実装の全体像はこちらです。細かく分けて順に解説していきます。 private func convertVolumeToHeight(from buffer: AVAudioPCMBuffer) -> Float { guard let channelData = buffer.floatChannelData?[0] else { return 10.0 } // データが無効な場合は初期値の10を返す let maxValue = Array(UnsafeBufferPointer(start: channelData, count: Int(buffer.frameLength))).map { abs($0) }.max() ?? 0.0 // buffer内の最大音量を抽出 let db = 20 * log10(maxValue) // 人間の聴覚特性に合わせてlogをとってデシベル化する let adjustedDB = max(0, db + 40) // デシベル値の範囲を0..40に切る let normalizedDB = adjustedDB / 40 // 0..1の値になるように調整 let height = Float(normalizedDB * 120) // max 120pxとして高さを計算 return max(10, height) // 最低でも10pxになるようにする } まずは、Bufferに含まれる音量の値の中から最大の絶対値を取り出します。 これによって、そのBufferの区間の中で一番大きな音量の値が取れます。 let maxValue = Array(UnsafeBufferPointer(start: channelData, count: <s
キャリアは、選択の積み重ねでできています。 どんな経験を経て、なぜSansanを選び、いまどんな挑戦に向き合っているのか。 「Sansan 3 Stories」では、これまでの歩みと、実際にSansanで働いて感じたこと、そしてこれからの挑戦を「過去・現在・未来」の3つのストーリーでお届けします。今回のインタビューでは、メルカリ、スマートニュースというメガベンチャーで開発組織のマネジメントや全社プロジェクトをけん引し、現在はSansanの新規プロダクトであるデータクオリティマネジメント「Sansan Data Intelligence」の開発に挑む多賀谷に話を聞きました。 多賀谷 洋一 / Yoichi TagayaData Intelligence Engineering Unit 部長メーカーや初期フェーズのスタートアップでのソフトウェアエンジニアを経て、2017年に株式会社メルカリに入社。急成長期から変革期におけるプロダクト開発や技術組織のマネジメントに従事。2022年にスマートニュース株式会社に入社。社長室技術担当として経営戦略に基づく部門横断プロジェクトを推進。 2025年12月にSansan株式会社に入社。部長として新規・既存プロダクトの技術・組織マネジメントに従事している。 Story 1 過去|どんな課題に向き合い、なぜSansanを選んだのか ──これまでのキャリアについて教えてください。 多賀谷:大学院の博士課程で機械理工学の分野で微細システムを扱っていました。実は「とにかく何でもやる」という学際領域だったので、「どんな技術領域であってもまずは自ら学んでキャッチアップし、結果を出す」というマインドセットが培われたのかなと思っています。キャリアのスタートは外資系の計測機器メーカーで、1台数千万円の装置向けのソフトウェア開発に携わっていました。しかし、iPhoneなどが登場してアプリやサービスの一般ユーザーが急拡大する中で「もっと多くの人が直接使うソフトウェアの領域に携わりたい」と考えるようになり、その後フリーランスや初期のスタートアップを経験しました。 ──その後、メルカリに入社されたのですね。 多賀谷:はい。2017年にメルカリに入社しました。日米のフリマ事業や新規事業の立ち上げを経験した後、エンジニアリングマネジャーになり、その後エンジニアリングディレクターとなって、組織の拡大とエンジニアが自律的に開発する環境作りをけん引しました。この時期には、レガシーシステムの改善や置き換えという、急成長スタートアップにありがちなハードな課題にも向き合いましたね。その後メルペイでは、Fintech特有の高い基準が求められる中でのプロセス整備やセキュリティー対策など、一般的に想像されるコンシューマー向けサービスよりも「きっちりつくる」という経験をしました。急成長する組織でのマネジメントではとてもストレッチした経験をさせてもらった一方で、次の挑戦として「技術でちゃんと事業成果を出したい」という思いが強くなりました。プロダクトマネジャーとエンジニアなど分業化が進むとどの会社でも起こり得ることなのですが、後工程となるエンジニアが少し受け身になり「言われたものを作る」という状態になりがちです。当時、私自身が事業のことを深く理解して、技術で事業成果を作るための経験や知識がまだ足りていないと感じるようになりました。だからこそ、それができる新しい環境を求めてスマートニュースへ移りました。エンジニアバックグラウンドを持つ共同創業者で現CEOの浜本階生さんとダイレクトに働く機会をいただいたからです。 ──スマートニュース入社後は、どのようなミッションに取り組んだのでしょうか? 多賀谷:入社当初は、事業損益の改善や収益拡大のため、インフラコストの最適化や、国内の大手通信会社との提携に際して求められる厳しいセキュリティー基準のクリアなど、成果が事業数字に直結する領域で課題解決に取り組みました。その後はCEO室の一員として、「技術を強みとしてどう売上を成長させるか」という全社組織横断のプロジェクトを任せてもらいました。この領域はものごとが複雑に絡み合っていて、何か1つのことをして売上が生まれるような単純な領域ではありませんでした。広告プロダクト、エンジニアリング、営業組織の役員とも密に連携し、3つの組織が同じ方向を向いて、広告主様や広告代理店様のためのプロダクトを正しく作っていけるようにプロジェクトを推進しました。結果として、売上成長につなげる成果を出すことができ、エンジニアリングと事業を結びつけるという他では得られない経験をさせてもらいました。 ──BtoC向けのイメージが強いキャリアですが、なぜSansanへの入社を決めたのでしょうか? 多賀谷:メルカリやスマートニュース出身というと、「BtoC(コンシューマー)」のプロダクトに携わっている人と思われがちです。でも実際には、スマートニュースの広告プロダクトやメルペイの加盟店向けシステムなど、「BtoB(ビジネス)」の要素を持った領域で多くの経験を積んできました。そんな中でAIの時代が来て、「AIの時代だったらよりBtoBの領域でこそ、大きな価値を出せるのではないか」と考えていたところ、さまざまな巡り合わせがあってSansanと出会いました。Sansanへの入社の決め手は大きく3つありました。1つ目は、Sansanの新規プロダクト「Sansan Data Intelligence」が、まさにAI時代の基盤になっていくと思えたことです。2つ目は、一緒に働く人の魅力です。技術本部長の塩見やCTO笹川と何度も話をさせてもらう中で、塩見の「この人と一緒にやっていきたい」と思わせる人柄や、笹川の技術力の高さに魅力を感じました。3つ目は、このフェーズになっても創業経営者が3人も最前線で活躍していることです。「Founder Mode(創業者モード)」という言葉もありますが、創業経営者が素早く意思決定できるので、最速で事業インパクトを出せる環境があることも魅力的でしたね。 Story 2 現在|壮大なプロジェクトとSansanのカルチャー ──現在、どのようなプロダクトや課題に向き合っていますか? 多賀谷:現在、「Sansan Data Intelligence」という、新規プロダクトに向き合っています。これは、企業の取引先データを最新・正確な状態に保つ「データクオリティマネジメント」サービスです。社内システムに散在する取引先データを取り込んで、表記揺れ・重複・陳腐化といった品質問題を解消することにより、全社横断的なデータ活用を促進します。プロダクト紹介資料よりこの裏側では、日本に存在するあらゆる企業や事業所(本社、支社、店舗、工場、病院、学校など)を一意に特定して管理するマスターデータを構築し、整備しています。簡単にいうと、個人に対して識別子を振る「マイナンバー」がやっているように、あらゆる企業や事業所に対してSOC(Sansan Organization Code)と呼ぶ識別子を振るという、実はかなり壮大なプロジェクトなんです。企業の設立や統廃合、移転、代表者の変更など、変化し続ける実態に合わせて、誰も集中管理していないデータを収集して更新をかけていく必要があります。日本国内だけでなく海外の企業や事業所まで考えると、さらに壮大です。企業や事業所の識別子があることで何がすごいかというと、AIの時代において圧倒的な力を発揮するんです。この識別子があれば、AIが複数のデータベースやシステムをまたいでも同一の企業や事業所を正確に特定し結果を出してくれるようになります。それにより、「Sansan」「Bill One」「Contract One」といったSansanの各プロダクト間でデータがシームレスに連携するだけでなく、さらに、お客様が持つ社内システムとも正確につながるための、強力な基盤となります。そして、識別子によって一意性を担保した高品質なマスターデータがあることによって、実現できるアプリケーションもあります。たとえば、識別子を持たずに管理している取引先リストの重複排除や表記揺れの訂正(データクレンジング)、リスト情報への属性情報の付与(リッチ化)、未アプローチ企業の可視化(ホワイトスペース可視化)などです。企業のオペレーションや営業戦略を変革するサービスを提供していきます。 ──技術的な難易度はどのあたりにあるのでしょうか? 多賀谷:まず、データソースの品質がまちまちなんです。自社収集を含む多様な手段で収集したデータソースがあり、それらを結合して、本当に1つの正しいもの、いわゆる「Single Source of Truth(信頼できる唯一の情報源)」を作ることが簡単ではないのです。データプロダクトを扱ったことがある方は経験があるかもしれませんが、一度作ったデータが壊れたり不整合が発生したりすると、その問題の調査や修正にはとても苦労します。そうならないように、データパイプラインやデータ結合のロジックを慎重に構築しているのです。さらにエンジニアリングで重要なのは、1回作って終わりではなく、そこに時間軸があることです。常に品質が保たれて、企業マスターデータが最新であり続ける状態にするのがエンジニアリングとして本質的に難易度が高いです。あとは、最速でPMF(プロダクトマーケットフィット)を達成すること。でも達成して終わりじゃなくて、そこから事業が大きく成長し続ける状態を作らないといけない。そしてマスターデータの提供以上にやっていきたいこともたくさんあります。実際に事業を作るのは不確実性があって非常に難易度が高く、同時に最高に面白いですね。 ──Sansanに入社して、良い意味でのギャップや発見はありましたか? 多賀谷:入社後の発見で言うと、Sansanのバリューズが、組織に深く浸透していることに驚きました。私は入社当時から「意思と意図をもって判断する」というバリューが特に好きです。「誰が言ってるか」ではなく「コトに向かう」ためのベースになる考え方で、自律的に判断し、その結果に責任を持つ。さらに、それを繰り返すことで人が成長して、成長した人が事業成果をもっと大きくする。そういう意味ですごく好きなバリューです。Sansanのミッション・ビジョン・バリューズあとは、学んでよかったなと思うのが「グロースマインドセット」です。 当初は、グロースマインドセットは単に新しいことを学ぶ意思くらいにしか理解していませんでした。でも、原典となる本*1を読んでみたら、「硬直マインドセット」と対になる概念だと知りました。グロースマインドセットは日本語版の本では「しなやかマインドセット」と訳されていて、「人間の能力は努力や経験、学習によって伸ばすことができる」と考える心の状態です。逆に硬直マインドセットは「人間の能力は生まれつき決まっていて変えられない」というものです。この対比による理解は、もう目から鱗でしたね。失敗を避けるんじゃなくていいチャンスだと捉える姿勢が、心理的安全性や高いパフォーマンスにすごく強く結びついてるんだなっていうのが、私にとってすごい発見でした。部署の月例全体ミーティングで使ったスライドあともう1つ入社して気づいたことなのですが、経営陣のスタンスも印象的でした。CEOの寺田は事業成果を出すための強い意思を持ちつつも、メンバーの話を深く理解した上で建設的でしかも本質的な意見を出してくれます。 その影響もあってか、前向きで意思と意図を持ってプロジェクトを押し進めている人が多いなと感じています。 Story 3 未来|AI時代、エンジニアはどう事業を創っていくべきか ──今後、Sansanでどのようなことにチャレンジしていきたいと考えていますか? 多賀谷:事業での成功は当然として、それを作る組織や人が成長して、やりたいキャリアに進んでいけるようにしたいですね。事業面で言うと、Sansan Data Intelligenceが市場にしっかりと受け入れられ、本当にお客様のシステムに組み込まれている状態を作りたいです。それによって、お客様が持っている複数のシステムを連携させて、AIがその中で自律的に仕事をしているような価値を生み出していきたいです。現在、Claude、Gemini、GPTなどAIのファウンデーションモデル(基盤モデル)は海外の企業が握っています。だからこそ、その上の「エージェントやアプリケーションのレイヤー」は日本の会社が戦っていくところだと思うんです。 ただ、Anthropicが事業領域を広げるなど、そこも競争が激しいのは事実です。単なる孤立したエージェントやアプリケーションじゃなくて「Sansanだからこそできる」という強固な土台が必要です。Sansanには「独自のデータ」と、それを生み出す「Digitization(人も間に入ったオペレーションによる正確なデータ化)」を行う強力な部門があります。他社には絶対に真似できないデータを土台としたAI活用で、Sansanだからこそ作れる強固なAIエージェントやアプリケーションを提供していきたいですね。<img src="https://cdn-ak.f.st-hatena.com/images/fotolife/s/sansantech/202