Cybozu Inside Out | サイボウズエンジニアのブログ

 

kintone 新機能開発 23 卒 1 年目を振り返って

kintone 新機能開発チームに 23 新卒で入った、エンジニアの宇都宮、柿崎、宮村、和渕、デザイナーのかやです。

23 新卒として一緒に入社した他チームの同期から、kintone 開発について聞いてみたいことを募集しました。 その中からいくつかピックアップして、みんなで答えていきました!

以下のような方の参考になると嬉しいです。

  • サイボウズに興味を持ってくれている就活生
  • 24 卒・25 卒で kintone の開発に興味を持ってくれている人
  • 配属で kintone 新機能開発を希望するサイボウズの新入社員

質問

質問 1 : kintone と他チームを比べて特徴的だなと新卒目線で感じていることは何ですか?

質問1 : kintone と他チームを比べて特徴的だなと新卒目線で感じていることは何ですか?
質問 1 : kintone と他チームを比べて特徴的だなと新卒目線で感じていることは何ですか?

モブプログラミング

和渕:これはモブプログラミング。モブプログラミングの時間多いねって他の同期に言われた。他チームと比較すると、長いんだと感じた。

領域ごとのチーム分割

宮村 : ユーザーの動線ごとに、チームが分かれているっていうところも特徴的だなと。

かや : 質問なんだけど、エンジニアが領域ごとにチームを分割しているのってなぜ?

和渕 : 1 つは、認知負荷を下げる目的がある。あと、自立性を高めて、価値提供のスピードを上げる目的があるね。 この記事がわかりやすいかも。

speakerdeck.com

宮村 : あと、担当範囲が狭いからこそ、その分野にフォーカスすることができて、その領域の専門になれるっていう側面もあるなと感じた。

新機能開発ができる

宇都宮 : 他チームでここまで新機能の開発してるとこってあんまりないかも。

和渕 : ユーザーにダイレクトに届く新機能開発ができるっていうのは嬉しい。

柿崎 : SNS で自分が開発した機能にポジティブな反応を見つけたときは嬉しかった。そういうのも kintone 新機能開発ならでは。

和渕 : こんな魅力的なチームがあっていいんでしょうか。

質問 2 : 1 年を振り返って 1 番大変だったことは何ですか?

質問2: 1 年を振り返って 1 番大変だったことは何ですか?
質問 2: 1 年を振り返って 1 番大変だったことは何ですか?

リリースのプロセス理解が大変

和渕:リリースのプロセスが 1 番大変だった。提供環境が複数あるし、提供順のルールもある。そこに、アップデートチャネルの概念もあるし、把握が難しい。今でもドキュメントを見返すときがある。 「アップデートチャネル」っていうのは、以前は新機能が適用されるタイミングが一律で月一度だったのが、2023年5月以降「アップデートチャネル」の「最新チャネル」か「月例チャネル」を選択することで、新機能を使えるタイミングをユーザーが変えられるようにした機能だね。

アップデートチャネルについて
アップデートチャネルについて

柿崎:いまだにちゃんとわかってるか微妙。リリース担当の方の仕事も複雑そう。

宮村:確かに複雑。ただ、今社内で改善の動きが起こっている。 リリースのやり方がより自由になっていくと思われるので、逆に考慮すべき点は増えそうな気がしている。 例えばデスクトップ通知とか。お客様の反応を見ながらリリースしたい機能だったので、一部の環境のみに先行提供してみたり。考えることが多かった。

和渕:考えることが多いことに対して、進め方でそこを軽減していった感じ?

宮村:決めの問題の調整が難しかった印象かな。

判断の難しさ

宇都宮:サブチームも裁量が大きいのは感じてて、その中で判断基準となるユーザー像がないから、実装時の判断に自信持てない。ベテランの人もそうだと思うけど、それが 1 番大変。自分の判断がいいのか悪いのか評価が難しいってところ。

かや:自分たちも普段 kintone を使う 1 ユーザーだけど、それで理解できてるのってエンドユーザーにとっての kintone のごく一部に過ぎなくて、情シスとかアプリ管理者とか立場が違えばよく使う機能や画面も違うし、価値を感じてもらえる部分も違う。だからそこを想像して判断するの難しいよね。

柿崎:今 React 化をやっていて、どこからやってくかという判断が難しい。大体先んじて React 化検証していたチームが大体優先順位を決めてくれているけど、自分のチームも担当することになって、何をどこまでやるかを自分たちで決めないといけないから、このフィールドはユーザーが何のためにどういう場面でどこまで作るのかっていうのが難しい。先輩の判断見てうまく判断できるようになりたい。

和渕 : みんな共通して、ユーザー像の把握が難しいっていうのがありそう。

柿崎 : いろんな属性のユーザーがいるからね。

質問 3 : 想定通りだったこと / 想定外だったことは何ですか?

質問 3 : 想定通りだったこと / 想定外だったことは何ですか?
質問 3 : 想定通りだったこと / 想定外だったことは何ですか?

機能理解が大変

和渕:kintone の全体感を掴むのは大変なんだろうな、と想定していたが、想定通り大変だった。 いつになったら kintone なんとなくわかったって言えるんだろうって今でも感じる。

柿崎 : 機能領域ごとにサブチームに分かれている。それでも、自分の領域でもわからないことはまだあるし、他の領域となるとわからないことが全然あって、確かにって思った。 チーム内でのタスクの進め方として、モブプロが中心だったのは想定通り。kintone 新機能開発チームならではの色だなと思う。

和渕 : エンジニアは機能領域ごとにチームが分かれていて、業務を進める上で理解するべきことの幅が限られているけど、デザイナーは領域分かれていない。kintone 全体を見ないといけない大変さとか難しさとかある?

かや :「前に同じようなところのデザインを担当した人が継続して担当する」とかざっくりとした分担は部分的にあるけど、明確には分けてないので担当しうる範囲は基本全部。なので今は新しいタスクに取り組む度に該当する機能とか画面を理解するところから入っている。デザイン変えるとなった場合全体がわかってないと他の部分にどう影響するかを考慮しきれないから、それは難しいなと思う。

和渕 : エンジニアの自分の領域内ですら、こんな機能あったんだ、ってなるから、全領域見てるってことはその発生頻度が凄そう。

かや :特定の領域に特化していると、ユーザー像とか機能の使われ方がある程度限定される分想定しやすいとかある?自分の担当領域だったら結構理解できてる?

和渕 : 理解しているかどうかだと、まだまだ難しい。ユーザー理解の部分が特に乏しい。

宮村 : ユーザー理解は自分でも難しいなあと感じる。自分たちが必要だと判断したところが意外と必要なかったり、逆に、あまり使われないかな、と想定したところが重要だったり。

リリースのスピード感

かや : 新機能をリリースするときにしっかりユーザーテストしたりして徹底的に検証するイメージを持っていたけど、素早くリリースして実際のユーザーに使ってもらう中で判断していくというスタンスがあることに驚いた。それを可能にしているのは先述の「アップデートチャネル」というもので、「最新チャネル」を選択したユーザーにより多くの新機能を早いタイミングで使ってもらえるようになったというのが背景としてあるよね。

提案が通りやすい & 変化が求められる

柿崎 : サブチームに分かれていて、1 つのチームが小さいから、開発の進め方とかスクラム開発の体制とかに対して、提案が通りやすいのは嬉しい想定外だった。とりあえずやってみようっていう流れがある気がする。

和渕:わかる。成長途中のチームだなっていうのが想定外。チームとして歴史があるから、安定していて、進め方の型決まってると思ってたけど、変化を求められてる段階だったっていうのが意外だった。

柿崎:ちょこちょこ変わってく部分がある気がする。

製品の伸び代

かや:製品の「伸び代」も予想以上に大きかった。歴も長いし既にたくさんの人に使ってもらっているから、ほぼ完成系の製品だと思ってた。けど実際は「ここ改善したい」っていうのがたくさん出てくるし新しい機能や画面を追加することも多いし、まだまだやれることがたくさんある。伸び代だね。

和渕 : 伸び代あるのはいいことですから。

コードが整備されている

宇都宮:読みづらいコードなかったのが意外。歴史長いと割と読みづらいコードが多いイメージ。ただ、そんなコードがなくて、いい想定外だった。

柿崎:フロントエンドのフレームワークの刷新もするしね。Closure Toolsに比べて React はわかりやすい気がする。

質問 4 : やりがいを教えてください!

質問 4 : やりがいを教えてください!
質問 4 : やりがいを教えてください!

ユーザーからのフィードバック

和渕 : 1 つはさっきも話に上がった、自分の作った機能に対してのフィードバックが直接くるっていうのがあるよね。

柿崎:Days とかでも大規模なイベントがあって、実際のユーザーと関われたりどう使ってくれているのかを知られたりするのはモチベーションになる。

days.cybozu.co.jp

様々な制約を超える達成感

和渕 : kintone に限らないかもだけど、サイボウズとしてパートナービジネス戦略をとっているからこその考慮点がある。考えないといけないこと多くて難しいからこそ、そこを超えるやりがいはある。 これはサイボウズじゃないとできない経験の 1 つなんだろうなと感じる。

かや : 制約がいっぱいあるからこそ、そこを超えられたときの達成感がある。

ユーザーとして改善したいところを自分で改善できる

かや : 普段 kintone を使っているからこそ「ここ直したい」「こうだったらいいな」みたいなことが出てきやすくて、それを自分の手で良くしていける可能性があるのはいいなと思う。愛着も湧くしね。 実際に、入社後に行った改善の例としては、チェックボックスの改善があるよ。

note.com

コード書くのが楽しい

宮村:コード書くの楽しい。kintone のコードを書くの楽しい。

和渕:どこら辺楽しい?これだけ規模が大きいコードを書けるところとか?

宮村:それもある。元々のコードが綺麗だから、あ、いいコード書けたってなりやすい。

和渕:既存コードがしっかりしているよね。

わからないことをどんどん追求できる

宇都宮:kintone の仕様や設計などで、わからないこと多すぎる。芋蔓式に出てくる。終わりが見えないから、いつまででもできる。kintone はドキュメントが整備されているから、自分で解決できる。人に聞かないとわからないでしょみたいなのが、他のプロダクトに比べるとなさそう。調べやすいのはいい。

対象顧客が多いからこそのやりがい

和渕:対象顧客が多いからユーザー像掴みにくい、手触り感持ちにくいなとは感じつつ、やりがいだなと感じている。

まとめ

みんな、楽しく色々なことを学びながら、 1 年間を過ごせていたことがわかりました! kintone の新機能開発について少しでも雰囲気が伝わっていると嬉しいです。

2 年目も頑張っていきます!

 

リレーブログ企画:スクラムマスターって何をするの?チームを健全に保つ多様なリーダーシップの実際

こんにちは。シニアスクラムマスターの天野 @ama_ch です。

スクラムマスターとは一体何をする人なのでしょうか。スクラムマスターを職能と位置付けているサイボウズ社内でも、まだまだ理解が浸透しているとは言えず、啓蒙活動の必要性がよく話題に上ります。

スクラムマスターの理解が進むことで、チーム・組織はスクラムマスターからより多くのメリットを享受することができ、スクラムマスターの能力を伸ばす機会も増えると考えます。そこで、スクラムマスターについてより理解していただくための実験として、リレーブログ企画を始めることにしました。

本記事に、企画の概要と各記事へのリンクをまとめます。

タイトル

スクラムマスターって何をするの?チームを健全に保つ多様なリーダーシップの実際

概要

「スクラムマスターって何をするの?」

スクラムを知って間もない人に最もよく聞かれる質問のひとつです。それはスクラムマスターを職能として定義・運用しているサイボウズにおいても同じです。サイボウズのスクラムマスターの責任は「チームを健全に保つこと」と定義していますが、この責任を果たすために実際に行う活動は多岐に渡ります。よってスクラムマスターの具体的な活動はイメージしづらく、どんな期待を持って接すれば良いのか分からないという問題がよく起こります。

このリレーブログ企画では、サイボウズで実際にスクラムマスターとして活動しているメンバーの具体的な仕事に焦点を当てます。この企画を通じて、スクラムマスターの日々の業務、直面する課題、そしてチームや組織に対してどのような影響を与えるか紹介します。具体的な事例をもとに、スクラムマスターについての理解を深め、みなさんがスクラムマスターから享受できるメリットを最大化することを目指します。本企画を通じて、スクラムマスターと効果的にコラボレーションできるようになれば幸いです。

対象読者

スクラムマスターが近くにいるけどありがたみを感じたことがない人

学習成果 (Learning Outcome)

記事の内容に関連する悩みについて、近くのスクラムマスターに相談できるようになる

記事一覧

乞うご期待!

 

「第13期サイボウズ・ラボユース成果発表会」開催

サイボウズ・ラボの中谷です。 今回は2024年3月29日にサイボウズ東京オフィスで開催された第13期サイボウズ・ラボユース成果発表会の模様を紹介します。

サイボウズ・ラボユース

サイボウズ・ラボユースは日本の若手エンジニアを発掘し、育成する場を提供する制度です。インターンと似ていますが、最長で1年間という長期サポートと、テーマをサイボウズ・ラボで設定するのではなく、ラボユース生個人が作りたいものをテーマとする点が特徴です。

今回の成果発表会では、第12期から引き続き活動していた学生も含めて16名のラボユース生が研究開発に取り組んだ成果を発表してくれました。

近年はコロナウィルスの影響でこの成果発表会もオンライン開催が続いていましたが、今回は 2019年の第8期成果発表会以来、5年ぶりのリアル開催となりました。今年度は夏合宿も4年ぶりに開催できたりと、徐々に社会がもとに戻っている手応えがありますね。

また4月12日にサイボウズ・ラボユースのオンライン説明会も予定しています。ご興味のある方はご参加いただけると大変嬉しいです。

発表会レポート

www.youtube.com

今回の発表をそれぞれ簡単に紹介します。

第13期サイボウズ・ラボユース成果発表会

第一部

伊藤 愛香 「ラボユースと私のマイペースな二年間」 (メンター:生田)

伊藤さんは、12期をラボユース研究生として、13期をラボユース生として活動しました。 研究生のときは卒業研究のテーマであった、日常利用を想定したAR空間での情報配置デザインの比較検討についての活動をし、実験手法や評価についてメンターとの議論を重ねてきました。 ラボユース生になってからは、街歩きの体験価値向上のためのナビゲーションアプリの開発に取り組みました。従来の最短経路を表示するナビゲーションではなく、目的地までの街歩きが楽しくなるような体験について検討し、興味があるカテゴリのスポットをできるだけ多く通る道を選択肢として提示するシステムのプロトタイプを完成させました。

重本 玲奈 「紙への手書きとデジタルメディアの利点を融合したメディア体験の提案」 (メンター:生田)

重本さんは、紙への手書きに対するこだわりを持ち、手書きとデジタルメディアとが融合することで新たなメディア体験を創出することを目指して活動しています。 これまで開発していた、紙に手書きをしたストロークをデジタル空間で情報共有可能なシステムをベースに、新たなメディア体験の設計や機能開発をラボユース期間中に取り組んできました。メディア体験の設計に苦労しながらも根気強く取り組み続け、現在は、書籍に対する手書きメモとその情報共有による新たなメディア体験が可能なシステムの開発を進めています。 今後は、開発中のシステムを完成させ、ユーザスタディを通してメディア体験の評価を進めていく予定です。

柏木 力哉 「関数型プログラミング言語の型保存コンパイラ」 (メンター:川合)

高級言語が持つ型情報をコンパイル時にも保存して、型付きアセンブリ言語を生成できるようにします。これによりコンパイルの各段階で型情報を利用したバグの検出や最適化が可能になります。また、相互再起関数を含めた型付きクロージャ変換を考案して実装しています。

今後の課題としては、型保存コンパイルの効率や安全性をさらに改善すること、言語に実用的な機能(データ型、パターンマッチ、多相型)を導入することです。

高名 典雅 「RISC-Vのアドレス変換を可視化する」 (メンター:川合)

ラボユース期間ではRISC-Vエミュレータ「carron」をRustで書きました。rv32imacとrv64imacに対応して合計9000行ほどです。このエミュレータでLinuxを動かすという目標も達成しています。

今回の発表では、RISC-Vのアドレス変換の自作ビジュアライザを紹介しました。これはRISC-Vの低レイヤのプログラムを書いているとアドレス変換がうまくいかなくて苦労するため、設定値が期待通りの動作をしているのか確認するためのものです。このビジュアライザはRust+dioxus+trunkで書かれていてブラウザ上で動作するので、RISC-Vアドレス変換のデバッグの際はぜひ使ってみてください。

福田 優真 「スクリプト言語の開発」 (メンター:川合)

以前よりプログラミング言語の開発に興味を持っていて、ラボユース期間では2つのスクリプト言語を作りました。

一つ目の言語は、前半のモジュール定義部と後半のスクリプト記述部に分離している言語で、直感的で読みやすくなっています。関数定義はScalaライクな構文を採用しています。また関数呼び出しの際はコマンドラインシェルのように引数にカッコをつけずにスペース区切りで書くことも許されており、好きな方を選べます。

二つ目の言語は、Gleamのuse構文やKokaのwith構文に類似したusing構文を持っていて、これによりネストを深くせずに簡潔に書けるようになります。発表では「Minecraftのログを解析して入退室をDiscordに通知」する例を5行で書いて例にしました。なおどちらの言語もRustで実装されています。

今後の展望としては、この2つの言語を統合したいと考えています。

山口 雄翔 「Rustによる自作OS」 (メンター:川合)

ラボユースでは、教育用OSへのNICドライバの実装・Linuxへの移植と、Rustでの自作OSをやりました。今回の発表では、自作OSの件を紹介しました。このOSはx86(32bit)用でエミュレータ(QEMU)で動作確認しています。現在のコード規模は1200行程度で(テスト含む)、キーボード/VGA/UARTによる簡単な入出力とページングができるようになっています。

今後は割り込み処理やマルチタスク、マルチコア対応、ファイルシステム、ユーザプログラム起動などに対応していきたいです。

第二部

阿部 奏太 「自作 RISC-V CPU の高速化」 (メンター:内田)

ラボユース期間で、自作の RISC-V CPU にパイプライン処理やアウトオブオーダー実行の機能を加えました。 これらの機能は CPU の処理をとても複雑にするため、たびたびバグが発生し、その都度乗り越えました。 追加した機能によるクロック数の削減量を計測し、確かに高速化することを確認しました。

櫻井 拓馬 「NAND Only CPU の安定化と高機能化」 (メンター:内田)

NAND という基本的な論理素子のみを使って、ちゃんと動く CPU を作りました。名付けて NLP-16 です。 ラボユースでは NLP-16 の動作の安定化と命令の高機能化を図った NLP-16A を設計・実装しました。

また櫻井さんは実際にNANDゲートで構成したCPUの実機を展示・デモをしてくれました(下の写真の機械全体で1個のCPUです)。

NANDのみで構成したCPU NLP-16A

高棟 雄斗 「Merkle-CRDTを使った分散KVSの実装に向けて」 (メンター:星野)

高棟さんは、Merkle-CRDT を使って分散 KVS を作ってみたいと、まさに今回のタイトル通りのテーマを掲げてラボユースに応募してきました。 Merkle-CRDT についての一番マシだと思われる情報源は arXiv にある Preprint 論文なのですが、これが難解で、設計・実装できそうだという状況になるまでにかなり時間がかかりました。 論文を読むのに慣れていない状況で、やりたいことについて書いてあるのがこの論文だったことは、高棟さんも運が悪い……(一緒に読んだメンターも?)と思わなくもないですが、実装できるところまでこれて良かったです。 今は簡単な CRDT の実装が動くようになったところで、これから分散 KVS に取り組んでいく予定です。

多田 瑛貴 「Rust製Linuxウインドウマネージャ開発を通じた良い設計へのアプローチ」 (メンター:星野)

多田さんは、良い設計とは何かについて本などを通じて学ぶ活動と、ウィンドウマネージャという具体的なソフトウェアにおいて設計の善し悪しを考える活動に取り組んでいます。 ウィンドウマネージャは過去に作ったこともあるということでしたので設計について学ぶ良い題材だったと思います。 一方で Rust は評判が良いので使ってみたいという軽いノリだったので少し心配しましたが、コードを書き始めたら割とすぐに動くものが出来ていたので杞憂でした。 ラボユース活動を通じて、設計の善し悪しについての判断軸を得ることができたのではないかと思います。

小栗 悠太郎 「DBMS自作によるトランザクション処理の学習」 (メンター:星野)

小栗さんは大学の卒業論文に取り組む年度であるにも関わらず、10 月にラボユース活動を開始したチャレンジャーです。 卒論を無事に終えたどころか、国際会議にも論文を投稿しています。 ラボユースでは、SimpleDB という本を教材にトランザクション処理について学んでいます。 小栗さんの専門はベクトル検索ということもあり、今作っている DBMS はトランザクション処理機能に加えて、 ベクトル検索機能を搭載するのが目標だそうです。

第三部

加藤 万理子 「言語モデルは音楽データから自然言語の構文的性質を学習するか?」 (メンター:中谷)

加藤さんは、言語モデルの入力に含まれるどのような知識が単語ベクトル表現として獲得されているのかに興味を持ち、特に音楽データから言語に関する知識が獲得できるかを、実験によって調べました。実験では、LSTMモデルに対して英語、日本語、音楽データ(MAESTRO)、ランダムデータ(Zipf分布と一様分布)で事前学習を行い、英語への転移性能を評価しました。 実験結果は、音楽データの埋め込み表現に句構造などの言語情報が含まれ、これが言語データの埋め込み表現と類似の構造を持つことを示唆していました。単語ベクトル表現の可視化からも、言語や音楽から得られた表現はランダムなデータとは異なることが確認できました。 加藤さんは今後もこの研究を継続し、言語の構造を探究していくとのことです。

西本 晋平 「赤外線リング構造検出に向けた深層学習モデルの開発」 (メンター:中谷)

天文学を専攻する西本さんは、可視光以外の観測データを用いた研究を行っています。今回の発表では、NASA の Spitzer 宇宙望遠鏡で観測された赤外線データから、太陽の8倍以上の質量を持つ大質量星が誕生した際に形成される構造である Spitzer Bubble(赤外線リング構造)を検出する深層学習モデルの開発について報告しました。 SSDというモデルを使用し、観測データから赤外線リング構造の位置を出力するシステムを開発、銀経10.5度から22.5度、銀緯-1度から1度の領域で学習したモデルを用いて推論したところ、多数の赤外線リング構造を見つけることができました。さらに、このモデルをCygnus X(白鳥座の領域)などの他の銀河にも適用したところ、リング構造らしい特徴を持つ天体を検出することができました。 今後もこのモデルの開発を続け、研究を進めていく予定です。

湯谷 承将 「深層学習モデルを用いたウェーブテーブル合成における意味的な音色制御」 (メンター:中谷)

シンセサイザーの音作りは複雑で難しいという課題に着目し、ウェーブテーブル合成に深層生成モデルを適用することで、直感的な音色制御を目指しました。提案手法では、条件付き変分オートエンコーダ(CVAE)を用いて、ウェーブテーブルに対する意味的なラベル(明るさ、温かさ、豊かさ)を条件として生成を行います。 モデルの評価では、再構成精度、条件付け生成の制御性、生成時間の3点に注目しました。その結果、高品質な再構成が可能であり、条件付けラベルに沿った生成ができることを確認しました。また、高いリアルタイム性も実現できました。 今後の課題として、条件付け生成の制御性改善のためのデータ数増加やモデル構成の検討、さらなるリアルタイム性の向上などを挙げていました。 また懇親会では音色をリアルタイムに変更する実機デモも行ってくれました。

「CVAEを用いたウェーブテーブル合成の意味的な音色制御」実機デモ

青木 志穂 「天然変性タンパク質の分子動力学法シミュレーション法における高速処理方法の検討」 (メンター:光成)

天然変性タンパク質とは変性領域と呼ばれる構造的に不安定な部分で、タンパク質の性質に重要な役割を果たすのですが、計算シミュレーションが難しい部分です。 その部分を高速化できないか検討を始めました。分子動力学法シミュレーション法(MD)のコアの部分は実装し、MDに渡す準備の部分もできるだけ自動化しました。 現在、既存システムGROMACSの評価をしていて、GPUのプロファイルに苦戦してるところです。 今後は自分で実装したMDのコアの部分を高速化していく予定です。

斉藤 楽 「SRv6 L2VPNの実装」 (メンター:光成)

従来IPv6のカプセル化が機能ごとに分かれていたのをまとめて、柔軟性と効率性を高めたのがSRv6です。 当初はGoでrawソケットを使って開発を始めたのですが、プログラミングの基礎をきちんと勉強する必要が分かりました。 それで『C言語[完全]入門』をサンプルコードを動かしながら勉強を進めて最後まで終わったので、いよいよネットワークプログラミングに戻ってきたところです。 デバッグ中にパケットキャプチャし続けていたのでパケットダンプを見てある程度構造が分かるようになりました。 今後は本来のSRv6の開発に着手する予定です。

まとめ

この発表まとめからもわかる通り、サイボウズ・ラボユースの活動範囲は非常に幅広く、おのおののラボユース生が自分の好きを追求しています。 今年度もサイボウズ・ラボユースは新たな応募をお待ちしていますので、興味を惹かれましたら説明会へのご参加や応募をご検討ください。

 

Next.js 製アプリケーションの CI の実行時間削減や安定性向上のために取り組んだこと

こんにちは!DOGO プロジェクトでソフトウェアエンジニアとして活動している @nissy_dev です。

DOGO プロジェクトでは、画面刷新を進めていく中で CI の実行時間が長く不安定になってしまい、開発生産性に大きな影響が出ていました。今回の記事では、CI の課題改善のために取り組んだことを紹介します。


目次


DOGO について

DOGO は、サイボウズ Office のフロントエンドを刷新するプロジェクトです。独自のスクリプト言語で書かれた MPA(Multi-Page Application)を、Next.js の App Router を利用して画面単位で置き換えています。詳細は以下のブログや JSConf での登壇スライドを参考にしていただければと思います。

blog.cybozu.io

speakerdeck.com

CI を改善することになった背景

DOGO の CI パイプラインは GitHub Actions で実装されています。

改善前のパイプライン

改善の前は次のような課題が生じており、日々の開発生産性を悪化させていました。

  • PR での CI が完了するまでに 12 〜 14 分程度かかる
    • 特に Test の実行にかかっている時間が長く、Test は今後も増えていく予定
  • Test が flaky になっており、安心してマージできない

DOGO では Shape Up という手法をベースにして刷新を進めており、開発チームは Building と呼ばれる 6 週間と Cool-down と呼ばれる 2 週間を繰り返します。

  • Building: 提供したい価値に集中して実装する開発期間
  • Cool-down: 探求などの自由に使って良い休息期間

このとき、Building 期間中に生じた問題などは基本的に Cool-down 期間に対応する予定でした。 一方で、CI の課題については Cool-down 期間でうまく改善できていない状態が続いていました。 そこで、今回は開発チームの中から自分が集中して CI の課題改善に取り組むことにしました。

CI の改善のために取り組んだこと

ビルド時に tsc を実行しない

DOGO では、Next.js で実装したアプリケーションを Custom Server の構成で動かしています。 このため、ビルド時には Next.js のビルド (next build) だけではなく、TypeScript で実装されたサーバー側 1 のコードもビルドする必要があります。 改善する前は、サーバー側のコードをビルドする際に tsc を利用しており、ビルド全体で 1 分程度の時間がかかっていました。

tsc によるビルドは型チェックが実行されるため遅いので、今回の改善ではトランスパイルだけを行う tsup に置き換えました。 tsup はデフォルトの設定がよくできており、設定ファイルを新たに書くことなく移行することができました。

また、next build 中にもデフォルトで tsc による型チェックが実行されるようなので、次のように無効化することにしました。 無効化した型チェックは、lint などと一緒にビルドとは別の job として並列で実行させています。

module.exports = {
  typescript: {
    // build 時の tsc による型チェックを無効にする
    ignoreBuildErrors: true,
  },
};

この改善によって、ビルド時間を 30 秒程度短縮することができました。

.next/cache を除いて、artifacts にアップデートする

ビルド成果物を複数の job に共有するために、GitHub Actions の artifacts を利用しています。 改善前は、next build で生成された .next ディレクトリをそのまま artifacts にアップロードしており、アップロードとダウンロードにそれぞれ 30 秒程度の時間がかかっていました。

一方で、.next ディレクトリのサイズを確認してみると、ほとんどが .next/cache によって占められていることがわかりました。

🐧 ❯ du -h -d 1 .next
 60K  .next/types
205M  .next/cache
2.9M  .next/server
2.5M  .next/static
213M  .next

こちらの cache ディレクトリはビルドキャッシュなので、アプリケーションを起動をするには不要です。2 そこで、キャッシュを除いて .next ディレクトリをアップロードするように変更しました。

- name: Next build
  run: npm run build
- name: Upload
  uses: actions/upload-artifact@v4
  with:
    name: upload-next-build
    path: |
      !.next/cache # cache ディレクトリを対象外にする
      .next

この改善によって、アップロードとダウンロードの時間をどちらも 20 秒程度短縮することができました。 また、このビルド成果物を元に docker image を作成していたので、image サイズの削減にもつながりました。

E2E テストをより多くの shard 数で分割する

Next.js のアプリケーションにおいて React Server Component (RSC) を含めたテストを行いたい場合は、基本的に E2E で行う必要があります3。 DOGO では、CI 上でフロントエンドとバックエンドを localhost で起動し、Playwright を利用した E2E テストを実行しています。

改善前は約 300 件の E2E テストを 5 個の shard に分割して実行していましたが、それでも CI のボトルネックになっていました。 今回の改善ではさらに shard 数を増やし、 10 個の shard に分割するようにしました。 これによって、テストの実行時間を 2 分程度短縮できました。

shard 数を増やすと GitHub Actions の billable time が増加するので、なるべく無駄なワークフローが実行されないように concurrency を設定しました。連続で push したときに複数のワークフローが実行されないようにしています。

on:
  pull_request:
    branches:
      - main

# ブランチごとに1つのワークフローしか実行されないようにする
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Playwright のブラウザのインストールをキャッシュする

Playwright のテストを実行するにはブラウザをインストールする必要があり、改善前はそれぞれの shard で 30 秒程度の時間がかかっていました。 この時間を削減する方法としてまず思いつくのはキャッシュを利用することなのですが、公式サイトではブラウザのキャッシュを推奨していません

今回の改善ではテスト実行時の shard 数を増やしており、GitHub Actions の billable time への影響を減らすためにも、各 shard で共通に必要な処理の実行時間はできるかぎり短縮したいです。 そこで、issue のコメントを参考に次のようにキャッシュすることにしました。

- name: Store Playwright's Version
  shell: bash
  run: |
    PLAYWRIGHT_VERSION=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//')
    echo "PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION" >> $GITHUB_ENV
- name: Cache Playwright Browsers for Playwright's Version
  id: cache-playwright-browsers
  uses: actions/cache@v4
  with:
    path: ~/.cache/ms-playwright
    key: playwright-browsers-${{ env.PLAYWRIGHT_VERSION }}
- name: Setup Playwright
  shell: bash
  if: steps.cache-playwright-browsers.outputs.cache-hit != 'true'
  run: npx playwright install --with-deps chromium

この改善によって、ブラウザのインストールの時間を 20 秒程度短縮することができました。

PR ではコード差分に関連するテストのみを実行する

DOGO には約 300 件の E2E テストがありますが、PR のコード差分に関連するテストケースは多くの場合でその一部になっています。 そこで、次の図に示すような方針で PR の差分に関連する E2E テストのみを実行する仕組みを実装しました。

E2Eテストの差分実行の実装方針

DOGO では src/apptests/e2e 直下のディレクトリ構成を同じにしています。 つまり図のディレクトリ構成において、https://example.com/page-b/... の URL に関するテストはすべて test/e2e/page-b に含めるようにしています。

このとき、URL の第一階層ごとに依存するファイルを madge と呼ばれるツールを使って取得します。 そして、それらのファイルが Pull Request の差分に含まれているかを確認し、含まれていれば対応する E2E テストを実行するようにします。

この仕組みによって、不要な E2E テストの実行を防くことに成功し、多くの PR でテストの実行時間を 1 〜 2 分程度短縮することができました。

Hydration の挙動によってテストが flaky になっていた問題の解消

E2E テストでは、フォームのサブミットなどのボタンをクリックするような操作の一部が flaky になる現象が起きていました。 原因を調査すると、hydration が完了する前に要素に対して操作を行っているためテストが落ちていることがわかりました。4

この結果を踏まえて、画面に遷移したときに hydration の完了を待つようにする処理を実装しました。 まず、テストでうまく操作ができなかった要素に、useEffect が実行されるタイミングで data-hydrated というデータ属性が追加されるようにします。

function useHydrationState() {
  const [hydrated, setHydrated] = useState(false);

  useEffect(() => {
    setHydrated(true);
  }, []);

  return hydrated;
}

export const Button = () => {
  const hydrated = useHydrationState();
  return <button onClick={....} data-hydrated={hydrated}>ボタン</button>
};

そして、テストコードでは次のようにデータ属性が追加されるまで待つようにします。

const waitForHydration = async (locator: Locator) => {
  await expect(async () => {
    expect(await locator.getAttribute("data-hydrated")).toBe("true");
  }).toPass();
};

test("ボタンのクリック", async ({ page }) => {
  await page.goto(".......");

  // データ属性を付与したコンポーネントへの locator を取得し、hydration を待つ
  const button = page.getByRole("button", { name: "ボタン" });
  await waitForHydration(button);

  // hydration が完了後、操作を行う
  await button.click();
  ...
});

こうすることで、flaky だったテストのほとんどが安定して通るようになりました。 ちなみに、似たような対策は sveltekit のテストでも行われています

CI の改善の結果

以上に加えて、E2E テストの一部削減やジョブの並列化も行い、最終的な CI のパイプラインと実行時間は次のようになりました。

改善後のパイプライン

CI を改善している間にもテストは増えていたのですが、それでも多くの PR で 5 分以上の高速化が実現できました。

今回取り組まなかったこと

今回の改善では優先度が上がらず取り組まなかった施策がいくつかありました。 これらの施策は、再び課題が生じたときや余裕ができたときに取り組むかもしれません。 他のプロダクトの改善の際にも使える施策があると思うので紹介します。

  • ワークフローのボトルネックの可視化に actions-timeline を使う
    • 今回は複雑なワークフローではなかったのでツールを入れないで対応できた
  • Flaky test の分析基盤として Allure Report を導入する
    • 今回は flaky test の原因にあたりがついていたので不要だった
    • あたりがつくことは稀なので、時間を見つけて分析基盤は用意したい
  • Microsoft Playwright Testing を導入する
    • サービスができてから間もなく、安定性や導入コストなどを考えて見送った
    • テストの実行時間の改善を期待している

終わりに

今回の記事では、DOGO プロジェクトの CI の改善について紹介しました。 RSC のテストの方法として E2E テストを選択しましたが、このままのペースで実装し続けると再び CI の実行時間が長くなってしまいそうです。 現在はチーム内でテスト戦略の見直しやよりよいテスト手法がないかを模索している状況です。 Testing Library の RSC サポートなどにも期待しています。

DOGO では、Next.js の App Router を利用して多くのページで刷新が進んでいます!もしこの記事を読んでサイボウズや DOGO に興味を持った方がいれば、次のリンクからご応募お待ちしています!


  1. サーバーは fastify を使って実装しています。
  2. ローカル環境では、キャッシュによる next build の高速化は数秒しか確認できませんでした。そのため、CI ではビルドキャッシュを利用していません。
  3. Next.js の test modeStorybook の RSC 対応などもありますが、どれも実験的なものになっており RSC の挙動を正確にテストできる状態ではないというのが現状です。
  4. 調査にあたっては、Playwright の trace viewer が非常に便利でした。
 

Kubernetes用プロファイリングツール necoperfの紹介

はじめに

こんにちは、Necoチームの竹村です。

今回のブログ記事では、新しく開発したプロファイリングツールである necoperf について紹介します。necoperf は Kubernetes 上で動作するコンテナに対して、簡単にプロファイリングを行えるようにするツールです。 本記事では necoperf を開発した背景や特徴、使い方について紹介します。

necoperf をなぜ開発したのか

necoperf をなぜ開発したのかを簡単に紹介します。

現在の perf でプロファイリングを取る方法

サイボウズではKubernetesクラスタをマルチテナント運用しており、1つのクラスタに複数のチームがアプリケーションをデプロイしています。 NecoチームではこのKubernetesクラスタを開発・運用しています。
アプリケーション開発者がアプリケーションを運用していると、アプリケーションのパフォーマンスがでないという問題が発生することがあります。 アプリケーションのパフォーマンス解析をするためにLinuxのperfコマンドを利用するのですが、マルチテナント環境ではアプリケーション開発者はサーバーにログインする権限を持たないため、以下のいずれかの方法でperfを実行する必要がありました。

  1. プロファイリング対象のコンテナ内で perf を実行する方法
    • perf のバイナリを計測対象のコンテナ内に配置して、perfを実行します
  2. プロファイリングを実行するためのサイドカーコンテナを用意する方法
    • サイドカーコンテナから計測対象のコンテナに対して perf を実行します
    • この方法では、shareProcessNamespacetrueに設定し、perf を実行するための権限をサイドカーコンテナに付与する必要があります
  3. エフェメラルコンテナからperf を実行する方法
    • この方法では、EphemeralContainers サブリソースを JSON ファイルとして作成し、Kubernetes の API サーバに対して PUT リクエストを送ることで エフェメラルコンテナを追加します。追加したエフェメラルコンテナから計測対象のコンテナに対してperfを実行します
    • kubectl debugコマンド経由ではエフェメラルコンテナを作成する際にvolumeMounts を指定することができないため、EphemeralContainers サブリソースの JSONファイル を作成して API サーバに対してリクエストを送信する必要があります

今までの運用で感じていた問題点

以前のperf を実行する方法には、以下の2つの問題がありました。
1つ目の問題としては、アプリケーション開発者に強い権限を付与する必要がある点です。 Necoチームが運用しているKubernetesクラスタではアプリケーション開発者へ付与される権限は必要最小限にされており、デフォルトの権限ではperfを実行することができません。 アプリケーション開発者がコンテナ内でperfを実行できるようになるためには、Necoチームが追加の権限を付与する必要があります。 Necoチームとしてはアプリケーション開発者へ付与する権限は最小限にしたいと考えており、perfを実行できるようにするためだけにアプリケーション開発者に強い権限を付与したくないという問題がありました。
2つ目の問題としては、perf のバイナリを用意するために非常に手間がかかる点です。 利用する perf のバージョンは ホスト OS の Linux のカーネルのバージョンにできるだけ合わせることが望ましいとされています。そのため、アプリケーション開発者はコンテナイメージを作成する際に、 パッケージ管理ツールを利用してホストOSに近いバージョンのperfを用意する必要がありました。 このパッケージを探す作業は非常に手間がかかり、perfを利用できるようになるまでのハードルを高くしていました。

これらの問題があり、Kubernetesクラスタ上でperfを実行するために非常に苦労していました。

necoperf

今までの運用で感じていた問題点を解決するために開発したのが necoperf です。necoperf は Kubernetes 上で動作するコンテナに対して、Linux の perf コマンドを簡単に使えるようにするツールです。

特徴

necoperf は以下のような特徴を持っています。

  • アプリケーション開発者に perf を実行するために必要な権限を付与する必要がなくなる
  • 1 コマンドでプロファイリングが簡単に行える
  • perf のバイナリの用意をテナントチームが行う必要がなくなる
  • コンテナの単位でプロファイリングを行うことができる

仕組み

necoperf は、各ノード上で gRPC サーバとして動作する necoperf-daemon と、necoperf-daemon に対して gRPC でリクエストを送信する necoperf-client から構成されています。
necoperf-client は necoperf-daemon に対してプロファイリングを行うためのリクエストを送信します。
necoperf-daemon はリクエストに応じて、CRI(Container Runtime Interface)を利用してコンテナ名から PID を取得します。そして、対象のPIDに対して perf を実行してプロファイリングを行います。プロファイリングの結果は necoperf-daemon から necoperf-client に返され、necoperf-client はプロファイリングの結果をファイルとして保存します。

使い方の紹介

necoperf の使い方として、mocoを使って構築したMySQLのPod に対してプロファイリングを行う方法を紹介します。 mocoに関しては以下の記事をご覧ください。

blog.cybozu.io

以下のツールが入ったUbuntu 22.04 環境を用意してください。

  • Go
  • Docker
  • kubectl
  • Kustomize
  • yq
  • FlameGraphのスクリプト
    • stackcollapse-perf.pl
    • flamegraph.pl

まず、mocoのセットアップを行ないます。

$ git clone https://github.com/cybozu-go/moco.git
$ cd moco/e2e
$ make start
$ export KUBECONFIG=$(pwd)/.kubeconfig
$ cat > mycluster.yaml <<'EOF'
apiVersion: moco.cybozu.com/v1beta2
kind: MySQLCluster
metadata:
  namespace: default
  name: test
spec:
  replicas: 3
  podTemplate:
    spec:
      containers:
      - name: mysqld
        image: ghcr.io/cybozu-go/moco/mysql:8.0.35
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi
EOF
$ kubectl apply -f mycluster.yaml

次に、necoperfのセットアップを行います。

$ git clone https://github.com/cybozu-go/necoperf
$ cd necoperf/
$ kustomize build config/namespace/ | kubectl apply -f -
$ kustomize build config/rbac/ | kubectl apply -f -
$ cd e2e
$ yq -i e '.spec.template.spec.containers[0].image = "ghcr.io/cybozu-go/necoperf-daemon:0.1.0"'  manifests/necoperf-daemonset.yaml
$ yq -i e '.spec.containers[0].image = "ghcr.io/cybozu-go/necoperf-cli:0.1.0"'  manifests/necoperf-client.yaml
$ kubectl apply -f manifests

necoperf は necoperf-cli という CLI ツールを提供しています。necoperf-cli を使うことで、簡単にプロファイリングを行えます。 mocoのテストを開始してから、以下のコマンドを実行して、necoperf-cli でMySQL Podのプロファイリングを行ってみてください。

# moco/e2eのディレクトリに移動する
$ make test
# 別ターミナルを開き、プロファイリングを行う
$ kubectl exec -it necoperf-client -- bash
$ necoperf-cli profile moco-test-0

プロファイリングが正常にできていたら、necoperf-clientのPod内に/tmp/moco-test-0.scriptというファイルが作成されているはずです。このファイルはFlameGraphのスクリプトを使うことで、以下のようなフレームグラフを作成することができます。

$ kubectl cp necoperf-client:/tmp/moco-test-0.script /tmp/moco-test-0.script
$ cat /tmp/moco-test-0.script | ./stackcollapse-perf.pl | ./flamegraph.pl > moco-test-0.svg

flamegraph

まとめと今後の予定

Kubernetes 環境でプロファイリングを簡単に行えるようにするために、necoperf を開発しました。今後は、necoperf をユーザに利用してもらい、改善していきたいと考えています。また、necoperf を運用していく中で、以下のような課題が見つかっています。

  • necoperf が利用する perf のバージョンとホストOSのカーネルのバージョンが一致するようにアップデートを行なっていくのが難しい
  • プロファイリング対象のコンテナが子プロセスを起動している場合に、プロファイリングがうまく行えない
  • pidを取得するためにCRI(Container Runtime Interface) APIを利用しており、コンテナランタイムのsocketをマウントする必要がある

今後もnecoperfを積極的に開発し、これらの課題を解決していきたいと考えています。 ぜひ使ってみてください!

github.com