TECH PLAY

株式会社ラクス

株式会社ラクス の技術ブログ

932

はじめに 皆さん!初めまして! 楽楽請求新卒エンジニアの kaihatsuda です。 本記事では、Kotlin のテスト フレームワーク Kotest に使われている Kotlin の特徴的な記法や技術を紐解いていきます! (本記事は Kotest の 使い方解説 ではなく、その背後にある Kotlin の 技術 を理解することに焦点を当てていますので、ご了承ください。) 私たちが開発する 楽楽請求 では、サーバーサイドの実装に Kotlin を採用しています。テストコードの記述も開発サイクルに欠かせない重要な工程の一部として位置づけられており、品質を担保するための必須要素になっています。特に、テストコードの 簡潔さ や 可読性の高さ は、効率的な開発において非常に重要です。 楽楽請求では、 単体テスト を記述するために Kotest を採用しています。私自身、学生時代に 単体テスト を書く経験がほとんどなく不安でしたが、配属後に初めて触れた Kotest で以下のようなテストコードに触れたとき、その直感的な書き方に感動しました。 初めて触れた Kotest のコードのイメージ class MySpec : FunSpec({ context( "四則演算の正常系" ) { test( "正しく加算できる" ) { ( 2 + 2 ) shouldBe 4 } test( "正しく減算できる" ) { ( 9 - 2 ) shouldBe 7 } } context( "四則演算の異常系" ) { test( "0で除算すると算術エラーが投げられる" ) { shouldThrow< ArithmeticException > { val result = 1 / 0 } } } }) このコードを見たとき、私はこう思いました。 「なんだこの shouldBe とか context とかいう書き方... でも、めちゃくちゃ可読性が高いし直感的に書けてすごい!」 一見すると、まるで普通の日本語の 箇条書き のように見えませんか? context("四則演算の正常系") の中に、 test("正しく加算できる") がある (2 + 2) shouldBe 4 は「2+2は4であるべき」という意味のテストである 正常系と異常系を文脈で分けて記述している Kotlin を知らなくても、何をテストしているのか直感的に理解できそうです。 でも疑問も浮かびます 「 shouldBe はまるで 演算子 みたいにスペース区切りで呼べるのは何故?」 「なんで波括弧 {} の中に context と test が並んでるの? なぜ呼び出せるの?」 「 shouldThrow<ArithmeticException> はどうやって型をチェックしているの?」 こうした Kotest の "魔法のような記法" を支えているのは、Kotlin の infix 、 拡張関数 、 レシーバ付き ラムダ式 、 inline & reified といった特徴的な機能です。(その他にも様々な技術が使われていますが、今回はここまでに留めます) はじめに 本記事のゴール 本記事で読み解くコード例 それぞれの記法の解説 1. infix 2. 拡張関数 3. レシーバー付きラムダ式 4. inline & reified 4.1 inline 4.2 reified まとめ 本記事のゴール 本記事では、 Kotest の「魔法のような記法」に着目し、その裏側で活用されている Kotlin の特 有機 能を解説します。具体的には、 infix によるスペース区切りのメソッド呼び出し 拡張関数 による既存クラスへの振る舞い追加 レシーバ付き ラムダ式 を使ったスコープ風の記述 inline & reified を活用した ジェネリクス の型情報保持 といった機能が、Kotest の柔軟で読みやすい記法を支えている仕掛けです。 そこで本記事では、Kotest を参考に作成した 簡易ライブラリ を例に取り、これらの機能が実際にどのように使われているかをコード例を示しながらひとつひとつを紐解きます。 本記事の最終的なゴールは、以下の2点です。 Kotlin の各機能(infix, 拡張関数, レシーバ付き ラムダ式 , inline & reified)の役割と使い方を理解する それぞれの機能が「なぜ必要か」「どんな恩恵があるか」を知り、自分のコードでも応用できるようにする。 Kotest の DSL が「不思議」に見える仕組みを納得する Kotest の ソースコード に実際にどう活かされているかをイメージできるようになり、より深いレベルで Kotest を使いこなせるようになる。 これにより 「Kotest の使い方」ではなく、「Kotest が成立する背景の Kotlin 技術」 を学びます。もしご自身のプロジェクトでテスト DSL (Domain-Specific Language) を拡張したり、別の場面で同様の記法を取り入れたくなったときに、本記事の内容がヒントになれば幸いです。 本記事で読み解くコード例 本記事では、解説のためKotestライクな簡易版のテストライブラリを用意しました。シンプルですが、使用例の通り Kotest っぽいテストの記述を実現しています。今後本記事では、以下のライブラリを 簡易ライブラリ と呼びます。 // レシーバ付きラムダ式で DSL を定義 fun miniSpec(block: MiniSpec.() -> Unit ): MiniSpec { return MiniSpec().apply { block() } } class MiniSpec { fun context(description: String , block: MiniSpec.() -> Unit ) { println( "Context: $description " ) block() } fun test(description: String , block: () -> Unit ) { println( "Test: $description " ) block() } } // infix & 拡張関数によるアサーション (型安全化) infix fun <T, U : T> T.shouldBe(expected: U?) { if ( this != expected) { throw AssertionError ( "Expected $expected but got $this " ) } } // inline & reified を用いた例外検証 inline fun < reified E : Throwable > shouldThrow(block: () -> Any ?): E { val thrown = try { block() null } catch (e: Throwable ) { e } return when { thrown == null -> throw AssertionError ( "Expected exception ${ E :: class .simpleName } but none was thrown." ) thrown is E -> thrown else -> throw AssertionError ( "Expected exception ${ E :: class .simpleName } but ${ thrown :: class .simpleName } was thrown." ) } } // 使用例 fun main() { miniSpec { context( "四則演算の正常系" ) { test( "正しく加算できる" ) { ( 2 + 2 ) shouldBe 4 } test( "正しく減算できる" ) { ( 9 - 2 ) shouldBe 7 } } context( "四則演算の異常系" ) { test( "0で除算すると算術エラーが投げられる" ) { shouldThrow< ArithmeticException > { val result = 1 / 0 } } } } } それぞれの記法の解説 1. infix 簡易ライブラリの main 関数には以下のような記述があります。 test( "正しく加算できる" ) { ( 2 + 2 ) shouldBe 4 } 「 (2 + 2) shouldBe 4 」と書かれている部分は、Kotest ユーザーにはおなじみの書き方ですが、初めて見ると違和感があります。まるで shouldBe が 演算子 であるかのように見えますよね。 これは、infix 関数 と呼ばれる Kotlin の機能を使うことで「 . と () を省略」しているだけです。 infixの概要 Kotlin には、特定の条件を満たした拡張関数やメンバー関数に infix キーワードを付けると、 obj.methodName(arg) という呼び方を obj methodName arg のように 演算子 風 に書ける仕組みがあります。 公式ドキュメントでも「 infix notation (omitting the dot and the parentheses) 」と紹介されており、次の3つの要件を満たせば使用できます。 メンバー関数または拡張関数であること パラメータが1つだけであること 可変長引数やデフォルト引数を持たないこと 簡易ライブラリでは、 infix は以下のように利用されていました。 infix fun <T, U : T> T.shouldBe(expected: U?) { if ( this != expected) { throw AssertionError ( "Expected $expected but got $this " ) } } 簡易ライブラリの通り、不思議に感じていた shouldBe は単なる関数であることが分かります(厳密には後述する拡張関数が利用されています)。そのため、 (2 + 2).shouldBe(4) として実行することも可能です。スペースを使った関数呼び出しが出来る infix によって、英語のように直感的に読めるテストコードが記述出来ていた、というわけですね。 2. 拡張関数 先ほど紹介した shouldBe は infix 関数であると同時に、 拡張関数 として実装されています。拡張関数を利用することで、既存のクラスに対して新しい振る舞いを追加できます。 拡張関数を定義するには、レシーバ型(拡張したい型)を関数名の前に付け加えます。以下は、 MutableList<Int> に要素を入れ替えるための swap 関数を追加する例です。 fun MutableList < Int >.swap(index1: Int , index2: Int ) { val tmp = this [index1] // 'this' corresponds to the list this [index1] = this [index2] this [index2] = tmp } ここで、拡張関数内の this は、関数が呼び出された対象の インスタンス を指します。上記の swap 関数は、 MutableList<Int> に対して次のように呼び出すことができます。 val list = mutableListOf( 1 , 2 , 3 ) list.swap( 0 , 2 ) println(list) // 出力: [3, 2, 1] 以上が拡張関数の簡単な紹介です。但し「クラス内部に実際に新たに関数を追加しているのではなく、あくまで振る舞いを拡張するのみ」という点に注意して下さい[ 参考 ]。 Extensions do not actually modify the classes they extend. By defining an extension, you are not inserting new members into a class, only making new functions callable with the dot-notation on variables of this type. では、改めて簡易ライブラリにおける拡張関数の利用例を見てみます。 infix fun <T, U : T> T.shouldBe(expected: U?) { if ( this != expected) { throw AssertionError ( "Expected $expected but got $this " ) } } shouldBe 関数は、 ジェネリクス 型 T を拡張する形で定義しています。そのため、Kotlin のすべての型に対して呼び出し可能です。さらに型制約 U : T とすることで、 expected 引数が T と互換性のある型であることを保証しています。これにより、型安全性を保ちながら直感的な アサーション を実現しています。 3. レシーバー付き ラムダ式 次は「 miniSpec { ... } や context { ... } の波括弧がどうなっているのか?」という部分です。 以下のような階層的なテスト構造が Kotlin 特有の機能を活用して実現されています。 miniSpec { context( "四則演算の正常系" ) { test( "正しく加算できる" ) { ( 2 + 2 ) shouldBe 4 } test( "正しく減算できる" ) { ( 9 - 2 ) shouldBe 7 } } .... } 見た目としては「波括弧 {} を多重に使った単純な 入れ子 構造」のように見えますが、実際には Kotlin 特有の仕組みである レシーバ付き ラムダ式 を活用しています。 ラムダ式 を引数に取る関数で「 (レシーバ型).() -> R の形」を受け取った場合、その ラムダ式 の中ではレシーバを this (省略可)で扱うことができます。(先ほど説明した拡張関数においては、拡張したい既存クラスがレシーバー型に相当します) 言葉だけでは分かりにくいので、通常の ラムダ式 と、レシーバ付き ラムダ式 を具体例から比較してみます。イメージのしやすさのため、スコープ関数の also と apply を比較します。 通常の ラムダ式 (例: also) public inline fun <T> T.also(block: (T) -> Unit ): T { contract { callsInPlace(block, InvocationKind .EXACTLY_ONCE) } block( this ) return this } // `also`: 通常のラムダ式を使用 val user2 = User( "Alice" , 25 ).also { it.name = "Bob" // レシーバではなく、`it` で明示的に参照 it.age = 30 } レシーバ付き ラムダ式 (例: apply) public inline fun <T> T.apply(block: T.() -> Unit ): T { contract { callsInPlace(block, InvocationKind .EXACTLY_ONCE) } block() return this } val user1 = User( "Alice" , 25 ).apply { name = "Bob" // レシーバとしての `this` が省略可能 age = 30 } also と apply を比較すると、次のような違いが分かります。 also : 通常の ラムダ式 を使用し、 it で対象オブジェクトを明示的に参照。 apply : レシーバ付き ラムダ式 を使用するため、 this が暗黙的に参照され、プロパティを簡潔に操作可能。 このように、レシーバ付き ラムダ式 によって、渡す側の ラムダ式 で this を暗黙的に使う(省略する) ことが出来ます。 簡易ライブラリにおけるレシーバ付き ラムダ式 の使い方を見てみます。 fun miniSpec(block: MiniSpec.() -> Unit ): MiniSpec { return MiniSpec().apply { block() } } class MiniSpec { fun context(description: String , block: MiniSpec.() -> Unit ) { println( "Context: $description " ) block() } fun test(description: String , block: () -> Unit ) { println( "Test: $description " ) block() } } レシーバ付き ラムダ式 の観点から解説すると以下のようになります。 miniSpec(block: MiniSpec.() -> Unit) : MiniSpec をレシーバとする ラムダ式 ( MiniSpec.() -> Unit )を引数として受け取り、そのスコープ内でテストを定義可能にする。 apply を使って ラムダ式 を実行し、 MiniSpec のメソッド( context() , test() )を直接呼び出せるようにする。 context(description: String, block: MiniSpec.() -> Unit) : context() で、 ラムダ式 を MiniSpec.() -> Unit を受け取ることで、さらにネストされた test("...") を同じレシーバ (= MiniSpec) で呼び出せる このような仕掛けのおかげで、「 context の中でさらに test 、その中で アサーション 」など、自然な階層構造を作れる DSL 的な書き方になっています。 4. inline & reified 最後に紹介するのは、例外検証を簡潔に行うために用いた inline と reified です。 Kotest の shouldThrow<T>() と同じように、簡易ライブラリでは次のように記述できます。 shouldThrow< ArithmeticException > { val result = 1 / 0 } この 型を指定して例外を検証する 部分が inline & reified で実現されています。 4.1 inline Kotlinでは、 コンパイラ が状況に応じて匿名クラスに変換するケースがあります。 ラムダ式 はその一例ですが、これにより、関数呼び出しに関わるオーバーヘッドが発生します。 一方、 inline キーワードを使うと、 コンパイラ が関数の呼び出しを呼び出し元のコードにインライン展開(埋め込み)するよう最適化します。つまり、わざわざ匿名クラスを作成して実行するのではなく、元のコードに直接 ラムダ式 の処理を書き込むイメージです( 参考 )。 公式ドキュメント( Inline functions )の冒頭でも「インライン関数は呼び出しのオーバーヘッドを削減してくれる」ことが紹介されています。 これにより: ラムダ式 呼び出しに伴うオブジェクト生成のオーバーヘッドが消える 後述する reified が使用可能になる というメリットが得られます。 4.2 reified Kotlin では、 Java と同じく 型消去(type erasure) の概念があります。 ジェネリック 型パラメータ( T や E など)は コンパイル 時にチェックされるだけで、実行時にその型情報が失われます。 例えば以下のようなコードで、実行時には「 List<Int> と List<String> の違いが分からない」などの状況が起こります。 fun checkList(list: List < Any >) { // 実行時点では list の型パラメータが消えているので // どんな要素型か判別できない... } しかし インライン関数の型パラメータ に reified を付与すると、 コンパイラ が「型情報を削除せずに持ち回る」ようにコードを生成します。その結果、実行時に thrown is E -> thrown のように型情報を参照できるようになるわけです。(ここを深く追いかけると記事が膨大になりそうなので、別の機会に回します。ご了承ください) 先ほどの簡易ライブラリ内の shouldThrow 関数をもう一度見てみましょう。 inline fun < reified E : Throwable > shouldThrow(block: () -> Any ?): E { val thrown = try { block() null } catch (e: Throwable ) { e } return when { thrown == null -> throw AssertionError ( "Expected exception ${ E :: class .simpleName } but none was thrown." ) thrown is E -> thrown else -> throw AssertionError ( "Expected exception ${ E :: class .simpleName } but ${ thrown :: class .simpleName } was thrown." ) } } inline によって、 ラムダ式 をインライン展開 する(=> オーバーヘッド削減 + reified 利用が可能) reified E によって、実行時に「 thrown is E -> thrown 」で「期待していた例外クラスか」を判定できる。 つまり shouldThrow<ArithmeticException> のように書くだけで、「もし投げられた例外が ArithmeticException と違ったら?」とか「そもそも例外が投げられなかったら?」というケースを簡潔に検証できるわけです。 まとめ Kotest で出てくる "不思議な記法" は、実は Kotlin 標準の機能を巧みに組み合わせた結果でした。 本記事では簡易版テストライブラリを自作してみることで、以下の機能を一通り確認しました。 infix 「 object methodName arg 」のように . と () を省略できる技術 拡張関数 「既存クラスに関数を生やせる」機能で、Kotest での アサーション ( shouldBe ) などが直感的に書ける レシーバ付き ラムダ式 ラムダ式 の中で this を特定のオブジェクトに紐づけ、 DSL のような階層的構文を実現できる inline & reified ラムダ式 の呼び出しオーバーヘッドを抑えつつ、型情報を実行時にも参照できるので、 shouldThrow のように「型を明示して例外チェック」を簡潔に実装可能 こうした言語機能を知ると、Kotest の公式 リポジトリ を眺める際も「あ、ここは拡張関数で実現してるのか」と発見が得られるかもしれません。(mockkの returns や every など) 実際に Kotest のコードを追ってみると、もっと洗練された書き方や工夫された実装が散りばめられていますので、興味を持たれた方はぜひ覗いてみてください。 最後までお読みいただきありがとうございました。
アバター
目次 はじめに Prometheusとは ハンズオン環境を構築しよう Prometheusを触ってみよう Prometheusによる監視の全体像をつかもう まとめ はじめに このブログの目的 (と、ごあいさつ) こんにちは。SREの gumamon です! 最近、 Kubernetes を使う現場がどんどん増えてきました。 Kubernetes は自律的にいろいろ動いてくれる分、「今なにが起きているのか」を把握するのが意外と難しいです。 特に、構成が動的に変わる Kubernetes では、 サービスディスカバリ機能 のある監視ツールが欠かせません。 そんな中で、 Kubernetes のメトリクス監視といえば、今や Prometheusが デファクトスタンダード です。 このブログでは、Prometheusがどのように Kubernetes の情報を集めているのかを、ハンズオン形式で体験しながら理解していきます。 「とりあえず動かして理解したい!」という方は、ぜひ一緒に試してみましょう! 対象読者と前提知識 本記事は、 Prometheusの初学者 を対象としています。 「これからPrometheusを触ってみたい」「仕組みを理解しながら学びたい」といった方に向けて、基本的な概念や構成をハンズオン形式で解説していきます。 ハンズオンの内容を円滑に進めるために、以下の知識・環境を前提としています: Kubernetes の基本的な コンポーネント (Pod、Service、Nodeなど)についての理解 本記事では詳細な Kubernetes 解説は行いませんが、基礎的な用語を把握していることが望ましいです。 Kubernetes自体を構成するコンポーネント についても概要を把握できていると理解がスムーズです。 Dockerの実行環境が手元にあること(必須) ハンズオンでは kind (kubernetes in docker) を用いて仮想的な kubernetes 環境を構築します。 実行環境は Linux が推奨ですが、 Mac やWSL( Windows Subsystem for Linux )でも問題ありません。 Prometheusとは Prometheusの概要 Prometheusは、CNCF(Cloud Native Computing Foundation)によってホストされている、 オープンソース のモニタリングツールです。 もともとは SoundCloud 社によって開発され、現在では クラウド ネイティブな監視基盤として広く採用されています。 主な用途は、 メトリクス(数値データ)ベースの監視 です。対象となるシステムからメトリクスを定期的に収集し、それを時系列データとして保存・可視化・アラートの発火などに活用します。 特徴的なのは、Prometheus自身が監視対象に プル型でアクセス し、HTTPエンドポイントからメトリクスを取得する点です。この仕組みにより、監視対象を柔軟に検出・更新できるため、構成が頻繁に変化する Kubernetes との相性が非常に良くなっています。 また、Prometheusは専用のクエリ言語「PromQL」を備えており、柔軟な集計・フィルタ・可視化が可能です。Grafanaなどの可視化ツールと組み合わせることで、強力な監視 ダッシュ ボードを構築することができます。 参考: Prometheus/OVERVIEW/What is Prometheus? Prometheusの特徴と強み Prometheusには、 Kubernetes をはじめとする クラウド ネイティブな環境に適したさまざまな特徴があります。以下に、代表的な機能や強みを紹介します。 プル型によるメトリクス収集 監視対象に対してPrometheusが定期的にアクセスし、HTTPエンドポイントからメトリクスを取得します。これにより、構成変更に強く、柔軟な監視が可能になります。 サービスディスカバリとの統合 Kubernetes やConsulなどと連携し、監視対象を自動で検出・更新することができます。 Kubernetes のように動的に変化する環境と特に相性が良いです。 シンプルなメトリクス形式(テキストベース) メトリクスは人間が読めるテキスト形式で提供されるため、開発者や運用者が直接確認・ デバッグ しやすいという利点があります。 強力なクエリ言語(PromQL) メトリクスの集計やフィルタ、演算を柔軟に記述できる専用のクエリ言語が用意されており、複雑な条件での監視や可視化も対応可能です。 豊富なExporterエコシステム OSや ミドルウェア 、 クラウド サービスなどを対象とした多数のExporterが公式・非公式に提供されており、必要な監視対象をすぐにカバーできます。 また、独自アプリケーション用のExporterも容易に作成できるため、カスタムメトリクスの取り込みも柔軟に対応できます。 Grafanaとの親和性 PrometheusのデータソースはGrafanaで広くサポートされており、グラフや ダッシュ ボードを簡単に構築できます。 Alertmanagerによるアラート機能 条件を満たすメトリクスに対してアラートを発火し、メールやSlackなどに通知することができます。アラートのグルーピングや抑制も可能です。 軽量かつ単一バイナリで動作 インストールや構成が比較的簡単で、導入のハードルが低い点も魅力です。 NOTE ハンズオンでは以下のExporterを使用します kube-state-metrics : Kubernetes リソース(Pod、Deployment、Nodeなど)の状態情報をメトリクスとして提供するExporterです。 クラスタ ー全体の構成や状態を把握するために利用されます。 node-exporter : 各ノードのCPU使用率、メモリ、ディスク、ネットワークなど、OSレベルのハードウェア/システムメトリクスを収集するExporterです。 ハンズオン環境を構築しよう ハンズオン環境の説明 本ハンズオンでは、以下の GitHub リポジトリ を使用します: gumamon/test-prometheus:v0.1.1 以下は、最終的に構築される環境の構成図です: prometheus-handson-diagram 構成図の補足 Kubernetes クラスタ は kind によりDocker上に構築されています Ingress NGINX Controller により、ローカルからのアクセスが可能です サンプルアプリケーション(go-metrics-sample)がメトリクスを提供します Prometheusは3つの コンポーネント で構成されています: prometheus-server : メトリクスの収集・保存・提供を行う本体 node-exporter : 各ノードのハードウェア/OSメトリクスを提供 kube-state-metrics : Kubernetes リソースの状態をメトリクス化 各ExporterはPrometheusにより自動的に発見・監視されます 構築手順 1) ハンズオン環境の GitHub リポジトリ をローカルにcloneしてください: git clone https://github.com/gumamon/test-prometheus.git 2) v0.1.1にcheckoutしてください git checkout v0.1.1 3) リポジトリ 内の README/Getting Started に従って構築してください: NOTE この リポジトリ は、 Kubernetes (kind)上にPrometheusとExporter、サンプルアプリケーションをデプロイし、監視の基本を体験できる構成になっています。 構築には docker, kind, helm, helmfile を使用します。 Prometheusを触ってみよう 無事にハンズオン環境は構築できましたか?早速Prometheusを触っていきましょう! PrometheusのWeb UIを開いてみる Prometheusをデプロイすると、Web UI を通じて現在の状態や収集したメトリクスを確認することができます。 ブラウザで以下のURLにアクセスしてください: http://prometheus-server.example.com NOTE この ドメイン は、ハンズオン環境に構成された Ingress によってローカルにルーティングされています。名前解決がうまくいかない場合は、 /etc/hosts にエントリを追加してください。 PrometheusのWeb UIでは、次のような情報を確認できます: ステータス情報(Targets、Configuration など) 現在のメトリクス一覧 PromQLクエリの実行結果 アラート状態(Alertmanagerと連携している場合。※今回は連携していません。) 収集したメトリクスを確認してみる Web UIが開けたら、まずは [ Query > 右上の「︙( 三点リーダ ー)」メニュー > Explore metrics ] を開いて、現在のメトリクス一覧を確認してみましょう。 prometheus-explore-metrics 以下のようにMetricのリストが出力されているはずです。 これが現在Prometheusが収集しているメトリクスの一覧になります。 prometheus-metric-list 次に、PromQLを使って実際にメトリクスをグラフ化してみましょう。 ここでは例として、 各ノードのCPU使用率 を確認してみます。 1) PrometheusのWeb UI上部にある [ Graph ] タブを開きます 2) クエリ入力欄に以下のPromQLを入力します: rate(node_cpu_seconds_total{mode="user",node="gumamon-worker"}[5m]) 3) [ Execute ] ボタンをクリックすると、下部に数値が表示されます 4) 表示形式を [ Graph ] に切り替えることで、CPU使用率の推移をグラフで確認できます prometheus-query-graph NOTE 上記のクエリは、5分間隔でCPU使用時間の変化量を示すもので、対象を以下条件で絞り込んでいます。 mode="user" によってユーザーCPU時間を対象にしています。 node="gumamon-worker" によってWorker Nodeを対象にしています。 グラフが4本あるのは cpu={0,1,2,3} があるためです。 Dockerを通じてローカル環境のCPUコア数が透過的に見えています。 このように、Prometheusではクエリを使って詳細なメトリクス分析が可能です。 必要に応じて条件を絞ったり、集計関数を組み合わせたりして、柔軟な監視が行えます。 Prometheusによる監視の全体像をつかもう Exporterが収集する情報の流れ Prometheusの概要で述べたとおり、Prometheusは監視対象に プル型でアクセス し、HTTPエンドポイントからメトリクスを取得します。 早速どのようなHTTPエンドポイントから情報を収集しているかを確認してみましょう。 収集先のエンドポイントを確認する手順 1) PrometheusのWeb UIにアクセスします: http://prometheus-server.example.com 2) 上部メニューから [ Status > Target health ] をクリックします 3) Targets画面では、現在Prometheusが監視している各エンドポイントの一覧が表示されます。 ここには以下のような情報が含まれます: Exporterのラベル名(job名) 対象のエンドポイント( IPアドレス :ポート) 現在のステータス(UP / DOWN) 最終スクレイプ時刻やレスポンスタイム prometheus-targets NOTE 本来、 Endpoint のリンクをクリックすると、実際にそのExporterが提供している 生のメトリクス(テキスト形式) をブラウザで確認することができるのですが、ハンズオン環境では閲覧することができません。 Endpoint はprometheus-serverが Kubernetes の中から観測した IPアドレス / ドメイン であり、 Kubernetes の外にあるブラウザからはアクセスができない為です。 監視対象がどう登録されているか Prometheusが監視対象をどのように把握しているか、その仕組みを理解するためには、設定ファイルの内容を確認するのが近道です。 Prometheusでは、監視対象(Target)の登録を scrape_configs というセクションで定義します。 ハンズオン環境では、以下のコマンドで prometheus-server のConfigMapを確認することができます: kubectl -n prometheus describe cm prometheus-server | less ConfigMap内には、Prometheusの設定( prometheus.yml )が格納されており、その中に scrape_configs の定義があります。 以下はその一部抜粋です: scrape_configs : - job_name : prometheus static_configs : - targets : - localhost:9090 - job_name : kubernetes-apiservers bearer_token_file : /var/run/secrets/kubernetes.io/serviceaccount/token kubernetes_sd_configs : - role : endpoints relabel_configs : - action : keep regex : default;kubernetes;https source_labels : - __meta_kubernetes_namespace - __meta_kubernetes_service_name - __meta_kubernetes_endpoint_port_name scheme : https tls_config : ca_file : /var/run/secrets/kubernetes.io/serviceaccount/ca.crt サービスディスカバリの仕組み ここで注目すべきは、 kubernetes_sd_configs という設定です。 これは Kubernetes クラスタ 内のリソース情報を自動的に取得して、監視対象(Target)を動的に登録するための仕組み です。これが、いわゆる サービスディスカバリ(Service Discovery) に該当します。 role: endpoints により、 Kubernetes 内の Service + Endpoints を対象とします relabel_configs を使って、指定条件(NamespaceやService名、Port名など)に一致したものだけをフィルタし、監視対象に登録します たとえば上記の設定では、以下の条件を満たすエンドポイントだけが登録されます: Namespaceが default Service名が kubernetes Port名が https このようにして、Prometheusは 静的なIP指定ではなく、 Kubernetes リソースに基づいて柔軟に監視対象を管理する ことができます。 構成の変化にも自動で追従できるため、 Kubernetes のような動的環境では非常に効果的なアプローチです。 参照: prometheus/configuration/#kubernetes_sd_config Kubernetes との連携ロジックの俯瞰 これまでの項目で、Prometheusが scrape_configs を通じて Kubernetes クラスタ 内の監視対象を動的に検出し、Exporterからメトリクスを収集していることを確認してきました。 ここでは、Prometheusが Kubernetes とどのように連携しているのかを、図を用いて俯瞰してみましょう。 prometheus-flow 図中の番号に沿って、Prometheusの処理フローは以下のように進みます: 1) Service Discovery の開始 Prometheusは、ConfigMapに記述された kubernetes_sd_configs に基づき、 Kubernetes API Server に対して対象リソース(この場合は Endpoints)の情報を問い合わせます。 2) 監視対象エンドポイントのフィルタリング API から取得した情報に対し、 relabel_configs の条件を適用し、 実際に監視する対象 (IP/ポート)を選別します。 3) Exporterへのメトリクス収集リク エス ト 対象が決定すると、Prometheusは定期的にそのエンドポイントに対してHTTPでアクセスし、Exporterが提供する メトリクスをPull型で収集 します。 4) 収集したメトリクスの保存と可視化 取得したメトリクスはPrometheusサーバー内で時系列データとして保存され、Web UIやGrafanaを通じて可視化・分析されます。 prometheus-query-sample このように、Prometheusは Kubernetes の API を活用することで、 クラスタ の構成変化に自動的に追従し、動的な監視を実現しています。 設定は一度行えば済み、Podの追加や再起動にも自動で対応できる点が大きな利点です。 PodのEndpointをディスカバリする仕組みについての補足 先ほど例に挙げた go-metrics-sample の監視は以下の job:kubernetes-pods を利用しています。 prometheus-target- kubernetes - pods kubernetes - pods の scrape_configs は以下の条件に一致したPodのIP/PortをEndpointに追加します。 PodのAnnotationに以下があること prometheus.io/scrape: "true" #Scrape対象に含める (trueの場合対象) prometheus.io/port: <ANY> #Scrape対象のPort prometheus.io/path: <ANY> #Scrape対象のPath このため、以下のようにManifestに記載することで、Podをデプロイする担当者が任意にPrometheusのEndpointを公開することができます。 # Source: go-metrics-sample/templates/deployment.yaml apiVersion : apps/v1 kind : Deployment metadata : name : go-metrics-sample labels : app : go-metrics-sample spec : replicas : 2 selector : matchLabels : app : go-metrics-sample template : metadata : labels : app : go-metrics-sample annotations : prometheus.io/scrape : "true" prometheus.io/port : "8000" prometheus.io/path : /metrics まとめ 今回は、Prometheusを使って Kubernetes 上のメトリクスを収集・可視化する仕組みを、ハンズオン形式で確認してきました。 Kubernetes の構成は日々変化するため、「どこで何が動いているのか」を把握するには、サービスディスカバリのような仕組みが欠かせません。 Prometheusはその点でとても相性が良く、Exporterやクエリ言語(PromQL)などを組み合わせることで、柔軟な監視を実現できます。 この記事が、Prometheusの基本的な動作や Kubernetes との連携のしくみを知るきっかけになっていれば嬉しいです。 NOTE メトリクスの可視化については、Prometheus単体ではなくGrafana等を使う構成が一般的かと思います。 Prometheusは 冗長化 を前提とした設計が為されていないという問題もあります。 参考: prometheus/overview/#when-does-it-not-fit Prometheusと各種サービスの連携については以下のブログにまとめています。 OSSでオブザーバビリティを実現する (Grafana Stack x OpenTelemetry on Kubernetes) もし興味がありましたら、合わせてご一読いただければと思います。 以上、最後までお読み頂きありがとうございました!
アバター
はじめまして、楽楽販売新卒エンジニアのomegumiです。 少し前に、社内で「脳に収まるコードの書き方」の輪読会が開催されました。 (輪読会とは、複数の人で同じ本を読み、その内容について意見を交わす読書会です) 初学者視点でもたくさんの学びがあったので、 コーディング経験に関係なく大事そうだと感じた学びと個人的に業務で取り入れたことについて 書いていきたいと思います。この記事を通して、私同様に「 プログラマ って何から勉強していけばいいんだ」「技術書とか設計って怖い…」と思っている方を少しでも後押しできれば幸いです。 読んだ書籍「脳に収まるコードの書き方」 今回、社内の輪読会で読んだ書籍はこちらです。 www.oreilly.co.jp 読んだ書籍「脳に収まるコードの書き方」 プログラマに求められる仕事と経験 「脳に収まるコードの書き方」から気づけた、プログラマとして目指すべき姿 まず"ソフトウェアエンジニアリング"って何? ではソフトウェアエンジニアリングの”技”とは何か? 小まとめ(結局何をやったらいい?) 「脳に収まるコード」とは? 関心事をどうまとめるか Before(関心事がまとまっていない例) After(関心事を分離し、整理した例) 小まとめ(脳に収まるコードを書くには?) 最後に プログラマ に求められる仕事と経験 本書を読んだことで得られた学びに、「 プログラマ の本当の仕事とは何か」「何をどう勉強していけばいいのか」があります。 結論から言うと「どういう状況で、どんな手法を選択すべきか判断する/できるようになること」だと私は思ったのですが、なぜ本書からそういう考えに至ったかを説明します。 「脳に収まるコードの書き方」から気づけた、 プログラマ として目指すべき姿 私は ラク スに入社するまで、チーム開発やビジネス目的の開発経験が希薄でした。そのため「 プログラマ として求められる振る舞いはどんな内容か、ビジネス組織における プログラマ の職責とは何か」を入社後によく考えるようになりました。 この書籍を読み始めるまでは、以下のような漠然とした目標で止まっていたと感じます。 可読性の高いコードを書く 変更しやすいコード設計を考える バグや想定外の挙動がない機能を作る 技術面の ボトルネック や問題解決を行う もちろん、良いコードを知り、ソフトウェアに求められる機能を早く実装できるようになることは重要だと思います。むしろ、当初の私はその手法や理想系を知るために勉強していました。 が、途中からこのような課題に直面し始めました。 ====================== 学んだ内容を業務で活かそうと考えていても、実装後や先輩に指摘をもらうまで「本にあった手法がハマるケース」だったことに気づけない ====================== この原因こそが、次で説明する「ソフトウェアエンジニアリングの”技”」が身に付いていないことであり、本書で「 プログラマ の職責」とされていた組織的な開発の鍵でした。 まず"ソフトウェアエンジニアリング"って何? みなさん、コードを書くときにソフトウェアエンジニアリングを意識したことはあるでしょうか?私は学んだことすら(さわりしか)ありません。書籍に出てきた説明をまとめると、次のように言えそうです。 ソフトウェア開発を純粋なアートから方法論へと転換しよう *1 という試み 最終的には、「仮説検証に基づいた 決定論 的なプロセス」を目指す理論 難しそうに聞こえますが、著者は 「 決定論 的なプロセス(=この状況なら絶対にこの手法!と断言できる方法)」は、現実の開発現場には存在しない と述べていました。つまり、ソフトウェアエンジニアリングだけを極めても、実際の プログラマ の仕事として十分とは言えない、ということです。 では プログラマ として何が必要なのか、本書からの学びを私なりにまとめたものが以下になります。 決定論 的なプロセスを見つける(完璧なソフトウェアエンジニアリングを体現する)以上に、 さまざまな技法を知り、ビジネスや状況に応じた適切な選択ができるようになる ことが重要。 だからこそ、 「ソフトウェアエンジニアリングの”技”」の部分で悩み、力を発揮することが プログラマ の職責。 ではソフトウェアエンジニアリングの”技”とは何か? 結論から言うと、ソフトウェアエンジニアリングの”技”とは「状況を見て判断、経験で判断」することです。個人の経験や判断頼りは組織的な開発に良くないという話だったのに、一体どういうことなのか。 キーとなる著者の考えを以下に抜粋します。 エンジニアリングが完全に 決定論 的プロセスなら、人間は必要なくなります。コンピューターと産業ロボットがあれば十分でしょう。 (出典:脳に収まるコードの書き方p252 [原書:Code That Fits in Your Head]) 適用できる山ほどの方法論があります。それでも、自分の脳を使う責任は無くなりません。スキルと適切なプロセス、経験則、技術を組み合わせることが仕事です。 (出典:脳に収まるコードの書き方p253 [原書:Code That Fits in Your Head]) 上記も踏まえて、自分なりの解釈を含めてまとめるとこんな感じです。 現状、状況ごとに「固定の理想/ルール」となる手法はない でも適用できる手法(方法論)は、山ほどある だからこそ、 「どういう論理に基づいて、その手法を選択するのか?」という思考プロセスを学ぶ ことが重要で、 プログラマ として得るべき経験とは「状況判断と手法選択のための 判断経験 」 プログラマ の職責とは、「どういう技術課題に、何の手法が最適かを判断する」こと(それにより、プロダクトの品質を担保する) つまり、「自身の脳を使い判断する」、「その判断を責任もってコード及びプロダクトに反映させる」ことが、 プログラマ の職責であるということです。 ここから考えると、課題だった「学んだ内容を実際のコードに活かせない状態」は、手法は学べている(=知識はある)が、それを採択するに至るために必要な思考プロセスが備わっていない(=経験が伴っていない)状態と捉えることができます。一度、「こういう時に使うのか!」という経験をしたり、「どういった理由・状況で採択される手法なのか?」という視点・思考プロセスを学ぶ必要があるということです。 小まとめ(結局何をやったらいい?) 方法論はどんどん勉強していい (例: カプセル化 ,関心ごとの分離手法,〇〇駆動開発など。) 定量 的視点での評価手法を学ぶ (例:テストによる0/1,チェックリスト使用,複雑度計測など。書籍に詳しい紹介がありますので、ぜひ読んでみてほしいです) 成果物をビジネスや目的に沿ったものにする開発手法も学ぶ (例:自動化,Gitの使い方など。こちらも書籍に詳しい紹介があります。ぜひ読んでみてください) 書籍では「組織として開発」のスタンスで話が進んだため、「 ステークホルダー の納得を得やすくする」ことの重要性・方法についても各所で説かれていました。コードや技術課題に対する判断力だけでなく、 ステークホルダー やビジネスサイドを踏まえた「職業 プログラマ としての判断力」も身に着けていくということです。以下は、特に「 プログラマ の職責」とされそうな部分を、自分なりにまとめたものになります。 ビジネスサイドの状況も鑑みて「技術的に譲れない・実装すべき部分はどこか」を判断する 技術的に譲れない=技術者視点でのMUSTについて、説明・納得・合意のための道筋を考える クリティカルな操作/機能を最速で動くようにする ⇒成果物の形を最速で業務目的に近づける(動く大枠から作る) ビジネスとコード双方のMUSTを担保できるラインの見極め 「脳に収まるコード」とは? 書籍「脳に収まるコードの書き方」では、組織的な開発の鍵として「人間の脳に優しい、読みやすいコード」すなわち「脳に収まるコード」を書く手法が主題となっていました。そこで、ここからは「脳に収まるコード」とはどうすれば書けるのかについて、自分のような初学者でも実践できた内容をまとめます。 関心事をどうまとめるか 読んですぐに実践できた内容として、コードを書く場所を決める際の判断手法があります。 コーディングにおける関心事とは、関心を払わなければならない変数/オブジェクトの詳細や、コードが目的とする処理タスクや機能が当てはまります。つまり、「脳の関心を惹くことと」と言い換えられます。人間の脳に優しいコードを書くためには、役割ごとに整理する手法を知ることが重要ということです。 私が書籍から学び取った、重要な判断基準は以下2つです。 どんなものも、与える役割は「専用の機能1つ」だけ 人間の脳は7つまでしか覚えられない ※著者によると、人間が一度に覚えておける容量の限界が「7」までであり、それ以上に処理や情報が増えた場合は、詳細すぎるとして切り分けを考えた方が良いとしていました。 まとめると、コードの一塊についてそれぞれシンプルな日本語で説明できる状態を保ち、一塊に含まれる詳細は7つまでに抑えることが重要ということです。これは、メソッドやクラス、 API 、プルリク エス トの文章などの様々な関心事に適用できるため、業務で実践できたことをデフォルメ化してお伝えしたいと思います。 例えば以下のように、チョコレート生成についてのクラスがあった場合、どのように修正すべきなのか考えてみました。 Before(関心事がまとまっていない例) class ChocolateFactory { public function makeChocolate($cacaoBeans) { // 準備:カカオ豆の種類ごとに担当パティシエを決定 // 加工:カカオ豆を焙煎、チョコに成型する処理 // 確認:できたチョコの品質検査を行う処理 return $chocolates; } } // チョコレートの作成・出力 $maker = new ChocolateFactory(); $chocolates = $maker- > makeChocolate(); このコードには、以下のような問題があります。 1つのことを理解したい場合でも、全てを読むことになる これは、1つのメソッド内で複数の工程について、関心事の詳細が全部入っているためです。例えばチョコ作りで通る工程をざっくり理解したい場合でも、「ここは準備の処理で、ここは確認の処理っぽい…」と順に全てを読むことになり、非常に大変です。ここから、すでに複数の役割を持たせてしまっていることがわかります。 各処理の再利用・変更がしにくい 例えば、焙煎処理だけコーヒー生成でも使いたい場合や、特殊な成型処理を追加したい場合です。 makeChocolate() 内ですべてくっついているため、新しい処理もここに書くしか無くなり、どんどん本筋の「チョコ作り」とは関係ない処理が入り込むことになります。コードの役割がどんどん増えていく原因になるということです。 何をしているのか理解しにくい 現在は単純なコメントのみのため、少ない事柄を扱っているように見えます。しかし実際、DBからカカオ豆を取得したりもみ殻と分けたりといったチョコ生成処理を書いていくと、理解すべき変数・分岐条件・使用する関数などの数はかなり多くなることが予想できます。これは、書籍の「7つまで」に反しています。 そこで、今回書籍から学べた手法を使って、関心事の観点で リファクタリング してみたコード例が以下です。 After(関心事を分離し、整理した例) class PrepareMakeChocolate { // 準備:カカオ豆の種類ごとに、担当パティシエへ振り分けるメソッド } class Patissier { // 加工:カカオ豆を焙煎、チョコに成型するメソッド } class ChocolateFactory { // チョコ作りの工程(準備・加工)書いたメソッド public function makeChocolate(): Chocolates{} // 確認:在庫に入ったチョコレートの品質を参照するメソッド public function checkChocolateQuality($id): boolean {} } // チョコを作る $chocolateFactory = new ChocolateFactory(); $chocolates = $chocolateFactory- > makeChocolate(); // 品質次第でチョコレートを提供 $chocolateFactory- > checkChocolateQuality($chocolates['id']); この変更は、以下のように可読性・再利用性の向上に貢献できます。 各クラスが1つの役割に集中している(準備,加工,確認) 各工程の影響範囲が独立している ChocolateFactory では工場としての役割「チョコを作って提供する」を考えて「チョコ作りの工程」「作ったチョコの確認」の2つをメソッドとして用意しました。管理や品質検証の工程は、厳密には「チョコを作る」工程ではないので、「作る」から分離しました。 もし焙煎や成型の処理に特殊な工程を組み込むパターンが発生したら、インタフェース化を検討しても良さそうです。上記コード例を書くにあたり、役割以外の判断基準として書籍から得た他の手法も実践しています。以下は、書籍でも紹介されていたかなり有名なコード設計の理論について、自分なりの解釈でまとめたものです。 関心事を分けるための要点2つ: コマンドとクエリを分離する システムの挙動を以下2つに大別するという考え方です。 コマンド:他のオブジェクトの状態を変更する副作用がある クエリ:状態を照会する(ので、返り値がある) 上記の例で言うと、 checkChocolateQuality メソッドは作ったチョコの状態を確認して、結果を取得するだけの「クエリ」です。 一方、 PrepareMakeChocolate 内の担当パティシエへ振り分けを行うメソッドは、他のオブジェクトに担当パティシエの情報を当てがう副作用があるため「コマンド」と考えられそうです。この2つを分ける意識は、各クラス・メソッドの役割をより明確化することに役立ちます。 やりとりする型と状態を保証すべき範囲を明確にし、 保証されたオブジェクト同士で付き合う 各クラスやメソッドに出入りするデータ・オブジェクトについて、「どのオブジェクトが、どんな状態を、どこまで保証するべきか」を決めておく必要があります。例えば PrepareMakeChocolate クラスを経由してエラーが出なかった(= PrepareMakeChocolate 内の検証に引っかからなかった)場合は、「担当パティシエが確定している状態」が保証されるべきです。 このように、関心事によってコードを書く場所の場所を判断する手法は、実際の業務でもメソッド抽出や変数の配置場所を決める時に役立ちました。 小まとめ(脳に収まるコードを書くには?) 関心事という観点で、クラスやメソッドを分ける 詳細すぎる情報は、 カプセル化 で「安全に」切り分ける コードの一部を切り出すときには、関心事単位でまとめるだけでなく以下3点を保証することが重要です。 外部から送られた「データの状態」が内部ルールと沿うか検証する 外部に返す「データの有無・状態」を明確にする 外部と内部を分けるため、公開範囲の制限を行う 覚えておきたい考え方: カプセル化 で実装の詳細を隠蔽する (出典:脳に収まるコードの書き方_p128 [原書:Code That Fits in Your Head]) 最後に 今回の輪読会参加で自分にとって最もプラスだったのは、下の2つです。 今後の業務/勉強から、どんな学びを得ていくべきかが具体化されたこと コード設計を取り巻く、様々な手法まで興味が広がったこと 「ソフトウェアエンジニアリングとか テスト駆動開発 って、よく聞くけど専門的すぎて自分にはまだ難しそう…」「設計や上流の知識が全然ない!そもそも何から勉強していけば良いの?」と感じていた私にとって、コーディング面のテクニックだけでなく「何をどう勉強すべきか」を考え直すきっかけとなった当書籍による輪読会への参加は、非常に大きな意義があったと感じています。 書籍に「初学者向けではない」とあるだけに、輪読会への参加前はためらいもありました(実際難しかったです)が、なんとか読み切った時には達成感もひとしおでした。今後、難しい書籍への挑戦や勉強会に参加する 心理的 ハードルも下がったと感じます。 ちなみに、私は本書がきっかけで「 テスト駆動開発 」に興味を持つに至りました。理由としては以下です。 切り分けたクラス/メソッドの検証に深いつながりがある プログラマ が担うべき品質担保へ直結する 書籍で、開発の進め方における”あるべき”とされていた「小さく書き進め、常に動く状態を保つ」に大きな効力を発揮する プログラマ の職責である「判断」での引き出しを増やしていくためにも、輪読会で下げてもらったハードルを飛び越え「 テスト駆動開発 Kent Beck   (著), 和田 卓人  (翻訳)」を読むなどしていきたいと思います。 また今回は盛り込めませんでしたが、「脳に収まるコードの書き方」には他にも多くのコーディングテクニックや開発改善の手法も書かれています。もちろんベテランの方にもおすすめの書籍だと思われますが、私のような初学者の方でも大量の学びがあると思いますので、ぜひ本書に挑戦してみていただきたいです。 本記事が、少しでもなんらかの興味・学びのきっかけとして届けば幸いです。 *1 : 出典:脳に収まるコードの書き方p37 [原書:Code That Fits in Your Head]
アバター
私たちは創業当初から「顧客志向」を徹底して重視し、2017年からは開発組織として「顧客をカスタマーサクセスに導く、圧倒的に使いやすい SaaS を創り提供する」というミッションを掲げてきました。 その結果、多くのお客様にプロダクトが支持され、国内 SaaS 市場でARR No.1を達成できました。 ラク スは、 特定の業務領域に特化して顧客志向でプロダクトを徹底的に磨きこみ、圧倒的に優れた顧客課題解決を目指す「ベスト・オブ・ブリード型製品開発戦略(以下ベスト・オブ・ブリード戦略)」 でプロダクト開発を行い、お客様の声を最優先にする方針をとってきました。 「ベスト・オブ・ブリード」は元々IT製品調達の用語で、業務領域ごとに最適な製品を組み合わせてシステムを構築することを指します。 ベスト・オブ・ブリード戦略はこの概念を用いて再定義したものです。 ラク スがなぜベスト・オブ・ブリード戦略を選び、どのようにお客様の声を製品へ反映しているのか。開発本部長の公手に、その背景や経緯、成功要因を聞きました。 原点は「お客様に喜ばれるものを作りたい」思い 重視したのは「お客様のリアルな姿」 マルチプロダクトを高成長させる戦略 絞り込んだターゲットから圧倒的支持を獲得 戦略成功には組織の「熱伝導性」を高めることが重要 「誰かの役に立っている」実感こそ開発の醍醐味 おわりに 原点は「お客様に喜ばれるものを作りたい」思い ラク スがベスト・オブ・ブリード戦略をとるようになった背景を教えてください。 正直、創業当初から「これがベスト・オブ・ブリード戦略だ」と意識していたわけではないですね。 「メールディーラー」を開発していたころは2000年代前半です。まだベスト・オブ・ブリードという言葉はなかったんじゃないですかね。 「お客様に喜ばれるものを作りたい」 という気持ちは強かったですね。 ただ、そういう気持ちが強いのとお客様に価値あるものが提供できるというのはまた別の話です。 ただ、「お客様に喜ばれるものを作りたい」という想いはめちゃくちゃ重要だと思います。最初の頃はお客様の話は聞いていましたが、解像度が高くありませんでした。なので、結構想像でこういうものが役立つだろうという感じで開発していたと思います。 私が「メールディーラー」の開発に参画したときは「メールディーラー」は十数社に導入されているぐらいのかなり小さなサービスでした。その後、私の後に参画された当時の役員のHIさんが PMF (Product Market Fit)を目指し奮闘していました。ECショップのメール問い合わせという領域に焦点を合わせることで、すごい勢いで売れるようになりました。 当初はお客様に役立つものを想像で開発していたとのことですが、売れ始めてから課題は出てきませんでしたか? まだまだ製品完成度も低かったことから、お客様からの要望が山のように来ました。 自分たちが想像していなかったような要望がきたり、自分たちの開発したものがお客様のイメージやHIさんのイメージと微妙にズレがあることもありました。 そこでHIさんともっとお客様の声をリアルに直接聞いて、現場を見て顧客解像度を上げていこう、ということになったわけです。開発だけでなく、営業もCSもお客様から聞いた声をどんどんデータベースに蓄積していきました。「こんな機能があると助かる」「ここが不便だから直してほしい」と具体的な要望がどんどん出てきましたね。 お客様の声を徹底的に聞き、マーケットインでお客様の声を実現していく うちに、自然と機能が豊富になり、お客様のかゆいところに手が届くシステムになり、より多くのお客様からの支持を得られるようになりました。 お客様の声をリアルに聞いたことで、ベスト・オブ・ブリード戦略に至る成功体験が得られたんですね。 はい、この成功体験がその後の ラク スのプロダクト開発の基本姿勢となり、結果としてベスト・オブ・ブリード戦略をとっていたということになるかと思っています。 あ、我々ってベスト・オブ・ブリード戦略なんだって気が付いたのは2011年とか2012年ぐらいだったと思いますね(笑)。 「ベスト・オブ・ブリード」という言葉は2004年ぐらいからあるみたいですけど。 お客様の声を徹底的に聞く方針について、具体的にはどんなことをしていたのでしょうか? 仕様一つとっても、 開発・営業・CS・ マーケティング が膝を突き合わせながら議論 していました。 先ほども述べましたが、営業やCSが商談やサポートで拾った要望、つまり「お客様からこういう声が出てる」「こんなところに不満を感じている」という情報を集めて、当社のWebデータベースでもある「楽楽販売」に情報集約しています。 それを開発と営業、CS、 マーケティング のメンバーで「どれを優先的に解決するか」「何が一番お客様にとって価値があるのか」を話し合う「製品力会議」という場を作っていましたね。 楽楽シリーズを開発する頃には 「顧客志向」のカルチャーが根付いていたため、自然と「お客様の声を最優先に意思決定する」ということができるようになっていた と思います。 重視したのは「お客様のリアルな姿」 お客様にとって価値あるものを知るため、エンジニアはどんな取り組みをしていましたか? エンジニアやUIデザイナーは、お客様先を訪問して実際の利用シーンを見たり、電話でインタビューしたりというのはやっていましたね。 業務の詳しい内容を完全に理解できなくても、直接会ってやり取りすると 「こんな人が使ってるんだ」というイメージ がつかめます。それが開発者にとっては大事なんです。 お客様が使うイメージをつかむことで、どんな変化があるのでしょうか。 開発者自身が少しでも「お客様のリアルな姿」を知っていると、いざ仕様を考えたり改善案を作ったりする時に発想が変わるんです。 自分が書いているコードが誰かの課題解決に繋がっていると思えると、モチベーションも上がりますし、本当に使う人の役に立ちたいという視点でよりよい実装ア イデア が出せるようになります。 そういった一つ一つの小さな差分が積み重なって最終的なプロダクトの使い勝手に大きな差として現れてきます。 結局、 SaaS というビジネスも人と人のつながりで成り立っているので、コードや技術の先にある、お客様という人を開発者が意識することが重要です。 マルチプロダクトを高成長させる戦略 ベスト・オブ・ブリード戦略を意識し始めた経緯を教えてください。 ベスト・オブ・ブリード戦略を自然ととっていたというのが僕の感覚ですね。 記憶はあやふやですが、 ラク スのプロダクト戦略の強みをベスト・オブ・ブリードという言葉で認識したのは先ほど述べた通り2011年とか2012年頃だと思います。 それ からし ばらくたって、各サービスをよりスピード感をもって成長させるために、サービス別組織として営業、マーケ、CSなどビジネス側の部門を 事業部制 としてまとめましたね。そのほうが自分たちのターゲットのみを意識して素早い意思決定ができます。 この頃には 全社員がベスト・オブ・ブリード戦略が当社の強みと明確に認識していた と思いますね。 ラク スは各プロダクトの技術選定にも裁量がありますが、これもベスト・オブ・ブリード戦略の考え方が活かされているのでしょうか? 技術選定自体はベスト・オブ・ブリード戦略と直接の関係はなく、開発スピードや長期的な保守性などのいくつもの要素を勘案して決めています。 各開発サービスのエンジニアが決定します。特にこれを使わないといけないという縛りはなく、一番のポイントは「そのときにベストと思った技術を選ぶ」ということです。 ただ、社内のノウハウから、データベースは PostgreSQL 、言語は Java 、 PHP がほぼ選択されます。 例えば、初期のプロダクトでは短期リリース実現のためにやや古めではあるけど再利用性などのメリットを生かすために既存の技術スタックを流用するということもやってました。 今は組織が拡大し、リソースにも余裕があるためそのような縛りなく新しい技術スタックや アーキテクチャ を選択しています。 いずれにしても 「お客様に必要な価値を、できるだけ早く届けるにはどうするか?」が根本。 技術選定はその手段にすぎない、という考え方です。 絞り込んだターゲットから圧倒的支持を獲得 ベスト・オブ・ブリード戦略によって、複数プロダクトの PMF を達成できた要因は何だと思いますか? それぞれのプロダクトで、しっかりとターゲットを絞ったこと も大きな要因です。 広く汎用的にすべてのお客様のニーズをカバーしようとしないで、「まずはこの層に刺さる機能を徹底的に磨く」ですかね。 たとえば「メールディーラー」なら先ほどとお伝えした通り、ターゲットをECショップに定め「まずはこの層に圧倒的に支持される」ことを狙っていました。 なので在庫管理システムとの連携なんかも実装しましたね。 そういった積み重ねで結果的に「これがあるとめちゃくちゃ便利」「ほかに代替があまりない」という域に達することができる。 当たり前と言えば当たり前なのですがベスト・オブ・ブリード戦略の真髄だと思っています。 ベスト・オブ・ブリード戦略では、複数プロダクト間の連携についてどのように考えているのでしょうか? 複数プロダクトを開発していると、どうしてもクロスセルのために共通機能やデータ連携という話が出てきます。 また効率性の観点から共通モジュールの開発、共通データ基盤という技術側の発想も出てきます。 ただ、当社はいったんはそれらの優先度を落とし、 ターゲット顧客が求める機能を最優先に実現してきた というのは成功要因の一つだと思いますね。 ここ最近の コンパウンド スタートアップとは真逆のやり方ですね。 データ連携や共通機能を実装してしまうとそれらに引きずられてしまい、足かせとなり、ビジネスとしての速度低下や使い勝手の低下を招いてしまいます。 戦略成功には組織の「熱伝導性」を高めることが重要 一般的に組織規模が拡大すると分業が進み、開発組織にお客様の声は届きづらくなります。 ラク スの開発組織としては、どのような対応が重要と考えていますか? 組織規模が大きくなると開発とお客様の距離は遠くなりますので、 「相手を知ろうとする」 マインドセット を持ち続けてもらうことが重要。 ここで言う相手とは、お客様と、お客様と接点を持つ社内の他部署の両方です。 お客様を知るには、可能なら 直接会いに行く・現場を見に行く のが一番です。 リアルで訪問すると会社の雰囲気や業務の流れがより具体的にわかります。 それに、何社か回ると「この業界のお客様はこういう課題を抱えているんだな」といった共通点や傾向も見えてきます。 開発はCSほど深く顧客業務を理解できるわけではないですが、それでも実際に一度触れるかどうかで、見える景色がまったく違ってくるんです。 社内の他部署との連携についてはどうでしょうか。 各部署同士がお互いの役割や温度感を知ることが大事です。 開発であれば「CS・営業がどんなやり取りをしているのか」を知るだけでも、認識が変わります。 たとえば、営業の日報を見たり、CSの対応履歴を確認したりするだけで、「こんなふうに使われているんだな」「ここに不満を持たれているんだな」と具体的なイメージがつかめます。 創業時に自然と行っていたことを、今は仕組化して意識的に行っていく必要がありますね。 はい、昔は開発、営業、CSの距離が近かったので、お客様の声やビジネスのリアルな状況(熱量)がダイレクトに伝わっていました。 でも、組織が大きくなると「お客様の声が届くまでの経路」が長くなり、熱量が薄まってしまう。 開発側・ビジネス側相互が自分から情報を取りに行き、組織の「熱伝導性」を高める必要があります。 単にビジネス責任者が熱量を持って伝えてくれるのを待つだけではなく、開発側からも「何が求められているのかを知りたい」と積極的に動くことが大事です。 そうしないと、規模が大きくなるにつれてお客様のリアルな声が遠のいてしまうんですよね。 つまり情報が自動的に伝わってくるのを待つのではなく、能動的にアクセスすることが必要になってくる、と。 そうですね。結局、どんなに規模が大きくなっても、 「顧客志向で製品を作る」ためには、開発もお客様を知る努力をし続けなければならない んです。 そのためには、お客様と直接話す機会をつくること、そして社内の他部署との情報共有を深めることが欠かせません。 昔は規模が小さかったから自然とやれていたことも、いまは能動的に取り組まないと情報が届かない。だからこそ、「どうやったら熱量を伝え合えるか」を組織として工夫する必要がありますね。 昔のような距離感は戻せなくても、 マインドセット や仕組みを作れば、お客様とのつながりを維持できるんですね。これからもその姿勢を重視していきたいですね。 「誰かの役に立っている」実感こそ開発の醍醐味 最後に、エンジニアが ラク スで顧客志向で開発することの一番の醍醐味は何でしょうか? やっぱり 「誰かの役に立っている」という実感を得られる ことですね。 技術を追う楽しさもありますが、最終的には「自分が作ったものが誰かの課題を解決している」とわかるのが一番の喜びだと思います。 当社は長年にわたってお客様の声をしっかり聞く取り組みを続け、それがベスト・オブ・ブリード戦略を支えてきました。 結果として、業界トッ プレベ ルシェアのプロダクトも複数あります。つまり、 自分たちが開発したものが「本当に世の中で使われて、誰かの役に立ち、評価されている」という手応えを得やすい んです。 あと、10年以上提供し続けているサービスも多数ありますが、古臭い技術スタックのサービスだけではありません。 既存の アーキテクチャ を刷新したりする機会もありますし、新規プロダクトの立ち上げもあります。 常に新しいことに挑戦する機会がある かと思います。 お客様の課題を意識して開発していくので、ダイレクトにお客様へ貢献できる。そういう面でも「人の役に立つものを開発したい」という想いの強いエンジニアにとってはかなりやりがいが大きいかと思います。 おわりに 今回は ラク スが大事にしてきたベスト・オブ・ブリード戦略の背景から経緯、成功要因までをお伺いしました。マルチプロダクトを PMF に導いてきた戦略の根底には、 ラク スの「顧客志向」が一貫しているのですね! 顧客志向で、お客様の声を起点にプロダクトを磨き続ける姿勢は変わりません。 組織規模が大きくなると一筋縄ではいかないところも出てきますが、これからも自分たちなりに工夫を重ねながらベスト・オブ・ブリード戦略を追求し、より多くのお客様に「 ラク スのプロダクトを使ってよかった」と言ってもらえるように頑張っていきたいですね。
アバター
こんにちは、デザインマネージャーの清水です。 私たち「プロダクトデザイン課」は、お客様の業務課題を解決するため、全プロダクトのUI/UXデザインを担っています。 あらゆるプロダクトにとって、最良のUXを目指すことは必然だと思います。 私たちもまた、お客様にとってより使いやすいプロダクトを提供するため、継続的にUX改善に取り組んでいます。 先日のブログ「 ラク スのプロダクトデザイン組織紹介― 顧客価値を高める新たな挑戦」では、 継続的なUX改善の一環である「UI刷新」プロジェクト についてご紹介しました。 ここでの「刷新」とは、既存UX(お客様の使い勝手に大きく影響する配置や導線)を急激に変更するという意味ではなく、UXの維持向上を重視しつつ、よりわかりやすいUIデザインにするイメージにとらえていただければ幸いです。 tech-blog.rakus.co.jp 今回は「UI刷新」の目的や背景、どのような価値を目指しているのかを詳しくお話ししたいと思います。 UI刷新プロジェクトとは 進め方と目指す姿 UI刷新プロジェクトとは ラク スのプロダクトが対象とする中でも、特に経費精算や勤怠管理といったバックオフィス業務は複雑で、多くの法令が関係します。そこで、ユーザに対応いただく操作も煩雑になりやすい側面があります。 ユーザに業務をスムーズに進めていただくためには、機能の充実はもちろん、直感的で迷わない操作性も欠かせません。そのため、より良いUXを提供する一環で「UI刷新」プロジェクトを立ち上げました。 今回のUI刷新は、 ラク スのバックオフィス向けプロダクトである楽楽シリーズ(楽楽精算、楽楽明細、楽楽販売、楽楽勤怠、楽楽電子保存、楽楽請求)が対象となります。 UI刷新の目的 今回の刷新には、大きく2つの目的があります。 1. UI統一による一貫した体験の提供 現在、各プロダクトのUIはそれぞれ独自の設計がされており、操作性もすべて異なっています。これは ラク スが採用している 「ベスト・オブ・ブリード製品開発戦略」 のためです。 「ベスト・オブ・ブリード」は、 業務領域ごとに最適化された“最良”の製品を、必要に応じて組み合わせて導入するというIT製品導入の用語 です。 ラク スはこれにならい、 業務領域に特化したUIや機能を作りこみ 、お客様の課題をいち早く解決できるようにしてきました。 その結果、どの領域においても最適なUI・機能を提供できるようになり、 ラク スの強みの一つとなっています。 しかし、最近は SaaS を複数組み合わせて利用する企業が増えてきました。楽楽シリーズを複数導入している場合、プロダクトごとに異なるUIに慣れる負担が生じます。そこで今回の刷新では、 プロダクトをまたいでも「同じシリーズだ」とわかるようにUIを統一し、より一貫した体験をお届けする ことを目指しています。 2. UIの最新化による使いやすさの向上 UI刷新の方向性を決める際、もう一つ大切にしたのが 「UIの最新化」 でした。 これまで ラク スのプロダクトは、お客様の業務課題を確実に解決するために、機能を優先して充実させる戦略をとってきました。おかげで多くのお客様のニーズに応え、選んでいただいています。 一方で、機能優先で開発を進めてきた結果、古いデザインが残っているプロダクトもあり、新たなUIへ進化させるタイミングを模索してきました。プロダクトを横断した「UI統一」は、その大きなチャンスだと考えています。 新しいUI コンポーネント は、視認性や可読性、 アクセシビリティ にも配慮し、どのユーザーにとっても直感的な操作ができるよう設計しています。 実際の導入にあたっては、 お客様の業務に合わせて最適化してきた既存のUXと調和を図るプロセス をとっており、後述いたします。 進め方と目指す姿 UI刷新とはいえ、既存のお客様が慣れ親しんだ操作感を一気に変えるわけにはいきません。 業務 ドメイン に特化した コンポーネント 配置を維持しつつ、画面のレイアウトやデザインをより洗練させる のが今回の方針です。 UI刷新前 UI刷新後 将来的には、プロダクトを横断して複数の楽楽シリーズを併用しても違和感を感じない、一貫した操作体験を提供し、視認性の向上や アクセシビリティ への配慮など、あらゆるユーザーにとって使いやすいUIデザインの提供が目標ですが、まずはその第一歩としてユーザーのプロダクト遷移時の違和感の解消を目指しています。 そのために、プロダクト統一の共通UI コンポーネント とデザイン ガイドライン を整備し、各プロダクトを担当するデザイナーで意識統一を図りました。楽楽シリーズは、それぞれ異なるカラースキームや コンポーネント を持っていますが、 共通UI コンポーネント とデザイン ガイドライン でデザイン基盤を整えることで、ユーザーに統一感のあるプロダクト体験を提供できる ようになります。 各プロダクトには、利用ユーザーに合わせた特有の機能や表現があるため、全てのデザインを共通UI コンポーネント で作成することは現実的ではありませんが、プロダクトを併用するユーザーに将来に渡って一貫した操作体験を提供していくため、何を統一し、何を独立させて最適化するかといった視点にこだわってデザイン ガイドライン を作り、更新し続けています。 デザインシステムの取り組み 共通UI コンポーネント やデザイン ガイドライン を発展させて、フロントエンド開発メンバーと共通の理念を持ってプロダクトの システム開発 を進めていくために、デザインシステムの導入にも取り組んでいます。 ラク スでは複数のプロダクトが独自のUIデザインを持っているため、今回のUI刷新後も各プロダクトで多くの独自 コンポーネント を使ったデザイン表現がされています。 その中で、デザイン制作目線でも システム開発 目線においても”共通のデザイン表現”とは何なのかを関係者が認識して、各プロダクトのデザイン、開発をできていることが重要だと考えています。 もっとも、これだけでUX改善まで実現できるわけではありません。 今回の刷新は、まずレガシーな画面をアップデートし、プロダクト間の共 通化 を図るもので、いわばスタートラインに立ったに過ぎません。 今後のUX改善のためには、デザイン組織としてより顧客を理解し、エンジニアやビジネスサイドとの連携を強めていくことも欠かせません。 私たちは、 このUI刷新をきっかけに、より大きなUX改善へと踏み込んでいきたい と願っています。 お客様にとって本当に使いやすいプロダクトとは何かを追求し、継続的にアップデートを重ねることで、 ラク スのプロダクトが「業務を効率化するだけでなく、使うのが楽しみになる」存在へと育っていくことを目指しています。 デザインチーム一丸となって取り組んでまいりますので、どうぞご期待ください。
アバター
ラク スが開発環境を進化させ続ける理由 こんにちは。エンジニア リングマ ネージャーの iketomo(いけとも) と申します。 多くの開発組織にとって、開発力を高め、エンジニアが活躍できる環境を整えることは重要な課題です。 開発環境は単なる「働く場所」ではなく、エンジニアが最大限の力を発揮し開発生産性を高めることが、顧客に価値を届けるための基盤だからです。 ソフトウェア開発の世界は日々進化し、新しい技術が次々と生まれています。また、市場の変化も加速しています。 この変化に対応するためには、開発力と組織の魅力を高め続ける必要があり、そのためには開発環境整備が不可欠と考えています。 ラク スも例外ではなく、開発生産性を高め競争力を向上するための開発環境整備に取り組んでいます。 今回の記事では、 ラク スの開発環境整備の基本方針、整備対象の選定・プロセスと、具体的な事例をご紹介します。 私たちの取り組みにかける姿勢をお伝えできれば幸いです。 ラクスが開発環境を進化させ続ける理由 開発環境整備の対象と基本方針 開発環境整備の選定・検証プロセス 開発生産性向上のための取り組み GitHub Copilotの導入 43インチモニタの導入 Devinの検証 まとめ 開発環境整備の対象と基本方針 整備の検討対象は、開発生産性向上につながるあらゆる環境・ツールです。 ただし単に最新のツールを何でも導入するわけではありません。 私たちは検討にあたって以下の基本方針を掲げており、 最終的には 「より高品質なソフトウェアを効率的に開発し、顧客に最大の価値を提供する」 ことを目指しています。 費用対効果を重視した選定 整備対象を選ぶとき、最も大切にするのは 費用対効果 です。 開発環境への投資は単なるコストではなく、開発生産性を高めるための戦略的な判断の一環です。 適切なツールや設備に投資することで、エンジニアの作業効率が向上し、開発のスピードと品質が向上します。 しかし、すべてのツールや技術が無制限に導入できるわけではありません。 費用対効果を考慮する際には、次のポイントから多面的に評価しています。 直接的な効果: 開発速度の向上、バグの削減、レビューやテスト、デプロイの効率化など、 開発プロセス での直接的なコスト改善が見込めるかどうか。 間接的な効果: 業務上のコミュニケーションしやすさ、ストレスの軽減、学習コストの削減など、 開発プロセス 以外でもポジティブな影響を与えるかどうか。 価値の持続性: 一時的な流行に乗るのではなく、将来的にも価値を発揮するかどうか。短期間で廃れてしまうものではなく、継続的に活用できるか。 特に、開発生産性を最大化するためには、投資することによってエンジニアにとってストレスの少ない環境を実現することが重要です。 開発者が作業に集中できる環境を整えることが、長期的に見て開発組織の競争力向上につながると考えています。 開発環境整備の選定・検証プロセス 現場の裁量で発案し、小さく試す ツールや新しい技術を導入する際には、単なる流行や市場の動向だけに頼るのではなく、実際の開発業務での有効性を確認することが不可欠です。 そのため私たちは、 開発チームが裁量を持ち、現場ニーズから提案してもらう検証プロセス を確立しています。 私たちの選定プロセスは次のステップで進められます。 開発チームからの提案: 開発環境を改善するための新しいツールや技術に関するア イデア を、各エンジニアが裁量を持って提案できる仕組みを整えています。 パイロ ットチームによる小規模検証: 提案されたツールは、まず少人数の パイロ ットチームによって試験的に導入され、実際の業務にどのような影響を与えそうか確認します。 パイロ ットチームは各開発チーム内で組成することもあれば、開発組織横断になることもあります。 評価とフィードバック : パイロ ットチームによる定性・ 定量 の結果をもとに、効果や課題を整理し、正式導入に向けた判断を行います。 本格導入と最適化 : 費用対効果が見込める場合、開発組織全体に展開し、必要に応じてカスタマイズや最適化を実施します。 このようなプロセスを取ることで、リスクを最小限に抑えつつ、最適な開発環境を実現することができます。 継続的な検証 開発環境は一度整えれば終わりではなく、技術の進化や組織の成長に応じて常に最適化し続ける必要があります。 そのため、私たちは 定期的に開発環境の見直しを行い、必要に応じてアップデート を実施しています。 継続的な改善を行うために、次の取り組みを実施しています。 定期的なレビュー: 半年に一度、社内の技術検証チームが現在の環境に対してフィードバックを行い、改善点を洗い出します。 社内ナレッジの共有: 新しく導入したツールやベストプ ラク ティスについて、社内勉強会やドキュメント、社内チャットを通じて情報を共有し、全員が活用できる状態を整えます。 技術トレンドのキャッチアップ: 最新の技術動向を常に把握し、必要に応じて新しい技術を試験導入することで、開発環境の陳腐化を防ぎます。 これらの取り組みを継続することで、エンジニアが常に最適な環境で開発に取り組めるようにし、結果的に開発効率と品質の向上につなげています。 開発生産性向上のための取り組み ここからは、具体的な取り組みについて紹介します。 GitHub Copilotの導入 目的 コーディング時間短縮、業務品質向上、学習や調査時間の短縮を通じた開発生産性向上を目的に、2023年6月から導入しています。 ソースコード の自動補完や提案機能のほか、コードレビュー、コード解説、テストデータ作成など、多くの局面での利用を想定しました。 経緯 まずテッ クリード 主導のプロジェクトチームで試験導入を行い、どのようなメリット・デメリットがあるのかを検証しました。コードサジェストにより削減した作業分を集計すると、同じ業務の所要時間は12%削減、一人当たり10時間/月の作業効率化につながることが明らかになりました。費用に見合う効果が確認できたため、最終的に全社展開を決定しました。 結果 90%以上のエンジニアが GitHub Copilotによって生産性が向上した との回答が得られました。時間換算で、1時間/日程度の作業が削減されたようです。 実際に使ったエンジニアからも「単純なコードの記述がとても楽になった」「サジェストが怖いくらい正確」という声が上がっています。 現在は GitHub Copilot Enterpriseも導入し、開発組織内でさらなる活用を図っています。活用事例やナレッジ共有を行い、組織全体の活用レベルをさらに高めていきたいと考えています。 43インチモニタの導入 目的 開発業務の可視性を向上させ、スクロールや画面切り替えの負担を軽減することで、作業効率を改善することが目的でした。 特に、大規模なコードベースを扱う際や、 ペアプログラミング の際に、広い視野を確保することで生産性の向上を狙いました。 経緯 従来のモニタ環境では、コードが縦に長くなり、スクロールや切り替えの回数が増えることで作業のリズムが崩れるケースが多発していました。 そこでテッ クリード 発案のもと、まず一部のチームで43インチモニタを試験導入し、可読性や作業効率への影響を検証しました。年間48時間以上の削減効果があることがわかりました。 結果 ソースコード の視認性が向上し、スクロールやウインドウ切り替えの回数が減少。結果として、 当初の想定通りコーディング・コードレビューや デバッグ 作業の時間短縮 につながりました。 特に関連する ソースコード を横に複数並べて同時に見ることで、プログラムの処理フローの把握をかなり効率的に実施することができるようになりました。 また ペアプログラミング やコードについて議論する際に、複数のエンジニアが同時に1つのモニタの前に集まることが多くなり、コミュニケーション活性化にもつながりました。 それ以外に当初想定なかった副次的効果で、縦に並んだタスク一覧を数多く表示できるので探しやすくなったり、メールや説明等の文章作成時に全体把握ができるので作成効率が上がるなど、 ソースコード 以外でも43インチモニタが活躍するシーンがあり、使用している全員が作業効率が良くなったと回答しました。現在では 開発組織の半数のエンジニアが利用 しています。 唯一のデメリットは画面が大きいので座っている人が見えなく、在席しているのかどうかを確認するのに席の横まで行かないといけないのですが、そこはご愛嬌で。 Devinの検証 目的 開発生産性向上の目的で、 AIエージェントの一つであるDevinの検証 も開始しています。 特に、各開発チーム内で適用可能性のある ユースケース や、十分な費用対効果が期待できるかを検証しています。 経緯 近年、チャット型やCopilot等のサジェスト型をはじめとした、 開発プロセス での生成AI活用が進んでいます。 しかし、AIエージェントは従来の生成AIとは違い、目的を与えることで自律的に複数の情報ソースやサービスから情報取得して操作を行うことができます。 タスクの計画立案から実行、成果物の提出まで一連のプロセスを自律的に遂行することで、飛躍的に生産性を向上させる可能性 があります。 私たちの社内においては、社内未導入の技術を調査・検証する「技術推進チーム」がAIエージェントのトレンドについて情報収集を進めてきました。 最新事例のフォローアップや社内での ユースケース 発掘に向けて、積極的に先行検証を開始しています。 現状 現在はバックエンド開発、AI機能開発、モバイル、SREなど、多岐にわたる部門・チームで検討を開始しているところです。 以下のような幅広い利用シーンが想定され、 費用対効果も良好な結果が得られそうな感触 です。Devinを検証中のエンジニアの声を一部紹介します。 PoCで チュートリアル を試すとき、必要な知識と実装が自動的に提案され、すぐ着手できた Devinの出力からベストプ ラク ティスやよりよい実装に気づける テストコード実装や リファクタリング に使ってみたい API 作成からCI/CD、PRの作成まで一括指示ができ、アウトソースしている感じがした。再現性の高い作業は委託しやすそう 日中、夜間関係なく稼働できるので効率的 人のアウトプットのダブルチェックに使える オンボーディング資料のアップデートなど、開発の付随作業の削減にも使えそう ユースケース の検証はまだ拡大継続中ですが、コスト削減だけでなく、PoCのようにDevinから新しい発見を得ることも期待できそうです。 まとめ 開発環境整備は、単なる効率化ではありません。エンジニアが力を発揮し、最高のプロダクトを生み出すための基盤です。 ラク スは 「費用対効果」を重視し、現場ニーズと継続的な改善を大切にしたプロセス で、確実に環境を整えてきました。DevinやCopilot、4Kモニタの導入はその一例です。 近年、生成AIをはじめとした技術革新により、開発環境はかつてないスピードで進化しています。 わずか半年後、1年後には、いま私たちが想像する以上の生産性を手にしている可能性すらあります。それはつまり、「未来の開発力」は、私たちの想像を常に超えてくるということです。 だからこそ、私たちは「現時点での最適な環境」に甘んじることはありません。 変化を受け身で待つのではなく、自ら変化を先取りし、常に“次の最適”をつくり出すことが必要 だと考えています。 私たちは、エンジニア一人ひとりがより自由に、より創造的に力を発揮できる環境を追求し続けます。 そして、テク ノロ ジー の進化を味方にしながら、より速く・より大きく・よりクリエイティブな価値を生み出せる開発組織へと進化していきます。
アバター
はじめに 楽楽販売開発課サポート対応チームのshimizu_sと申します。 前回、同チームにて以下の記事を掲載しました。 tech-blog.rakus.co.jp 今回は同じく、サポート対応チームの業務の内容について、どのようなことを実施しているかさらに詳しくご紹介します。 工夫した取り組みについて サポート対応の業務内容については前回の記事を参考にしてください。 さて、前回の記事で記載されたゴールは「お客様の疑問・もやもやを無くす」ですが、 ゴールにただ向かうだけではなく、お客さまにより満足していただけるようにサポート対応チームは以下の部分にも注力しています。 迅速に サポート対応を行う取り組み 正確に サポート対応を行う取り組み 明確に サポート対応を行う取り組み 今回は、三つの観点でサポート対応チームがどの様に工夫をしているかをご紹介したいと思います。 はじめに 工夫した取り組みについて 迅速にサポート対応を行う取り組み サポート依頼・対応のデータ蓄積 よくある質問ページの作成 正確にサポート対応を行う取り組み 調査観点・方法のナレッジ化 明確にサポート対応を行う取り組み 回答作成レビュー 回答作成時チェックシート 終わりに 迅速にサポート対応を行う取り組み 疑問を解消するのはもちろんですが、お客さまにとって、返事が遅いと不安になります。 最初は小さな不安の種ですが、時間が経過していくと最終的に 製品としての信頼を損なうことに繋がります。 そうした中で、お客様の疑問をいち早く解決できるように以下の取り組みを実施しています。 サポート依頼・対応のデータ蓄積 楽楽販売では、過去に受けたお客さまからの依頼に対して、社内で使用している楽楽販売の専用データベースに以下のような情報をもって蓄えています。 質問内容 回答した内容 その回答を行った理由 現在、このデータベースには600~700件/年のデータが登録されています。 サポート対応の蓄積 これにより、同様・類似の質問が来た際、過去依頼を検索・参照することで、素早い解決ができるように取り組んでいます。 よくある質問ページの作成 カスタマーサポートチームがサポート対応チームに確認しなくても対応ができるように、 以下の観点を考慮した「よくある質問ページ」というものを作成して、社内ネットワーク内でカスタマーサポートチームへ共有しています。 質問の頻度が多い 確認する箇所が明確である よくある質問ページ こちらのページについては、 カスタマーサポートチーム側からの意見を募集しており、内容を鑑みて反映をしていく運用となります。 正確にサポート対応を行う取り組み 調査観点・方法のナレッジ化 楽楽販売は柔軟なカスタマイズ性が強みの製品です。 豊富な機能を備えているため、お客様から多岐にわたる質問がやってきます。 そのため、サポート対応チーム内でも『どのように調査をしていけばよいか?』と悩むことがあります。 そうしたなかで、様々な質問に対して正確に調査し、適切に回答ができるように、 オープンソース ソフトウェアであるナレッジ共有ツール *1 を使用して、以下のようなナレッジを共有しています。 サポート依頼の調査観点 ログ調査方法 実際に機能を確認できる端末の使用方法 どのような質問が来ても、正しく内容を把握し、適切な回答を提供できるように努めております。 明確にサポート対応を行う取り組み サポート対応チームの取り組みの一つとして、"回答をいかに分かりやすく伝えるか" もあります。 なぜなら、エンジニアであるサポート対応チームと顧客対応を中心とするカスタマーサポートチームでお互いの専門としている分野が異なります。 さらに、文字ベースでのやり取りを基本としているため、曖昧な表現や一般的ではない用語を利用すると正しい内容が伝わらず、やり取りが増えるだけでなく、 お客様に誤った内容が伝わってしまう可能性がある からです。 そうしたことを防ぐため、サポート対応チームに配属後、わかりやすい回答ができるように以下の取り組みが実施されます。 回答作成レビュー 回答作成時チェックシート 詳細について説明します。 回答作成レビュー カスタマーサポートチームへ回答を伝える前に、 サポート依頼の担当者が作成した回答を一度ベテランの方に見てもらいます。 文章としておかしくないか 表現に揺らぎがないか 誤解を与えるような強い言葉(例えば『絶対』)を使っていないか 一般的ではないIT用語を使っていないか 色々な観点でチェックが入ります。 作成した回答のレビュー依頼 ベテランのチェック あらかじめ設定した基準をクリアし簡潔な回答ができるようになれば、ベテランのレビューがなくなります。 余談ですが、ここで筆者の文章力がグッと鍛えられた気がします。 回答作成時チェックシート また、サポート対応チーム内で、上記レビューであった指摘点について普遍的な内容の場合は、回答作成時のチェックシートに追加されます。 回答作成チェックシート つまり、ここに記載されているものに当てはまらないようにすることで、わかりやすく誤解を与えにくい回答になります。 (副次的にベテランのチェック時間も減るといった効果もあります) こうした文章表現のナレッジ化も取り組んでいます。 終わりに サポート対応チームでは、さまざまな工夫を行っております。 冒頭でもお伝えしたように、 「お客様の疑問・もやもやを無くす」がゴールではありますが、お客さまに、「より満足していただける」ように心がけています。 こうした取り組みは一朝一夕で成し遂げられるものではなく、すぐに成果が見えるわけではありません。 しかし、日々ユーザーが増えていく楽楽販売の商品の強みの一つとして「サポートの品質が高い」となるように、 サポート対応チームは、お客さまと実際にやり取りしていただいているカスタマーサポートチームと力を合わせて、 高品質なサポートを提供できるよう色々な工夫を行い努めていきたいと思います。 *1 : GROWI
アバター
はじめに:経費精算業務の現状と課題 当社は、経費精算業務を効率化するプロダクト「楽楽精算」を提供しています。 現在、多くの企業が紙や Excel で経費精算業務を行っており、申請から承認までに膨大な時間を要しています。 特に手作業による申請チェックや書類不備の差し戻しが、 経理 担当者の業務負担を増大させる要因となっています。 このような状況では、 経理 担当者が業績管理や予算策定といったコア業務に集中することが難しくなります。 こうした問題を解決するため、 「楽楽精算」ではプロダクトでのAI活用を一層推進する こととしました。 プロダクト開発に関わるエンジニアの皆さんにとっても、AIを活用したプロダクトの提供価値向上は大きな関心事ではないでしょうか。 一方で、AI導入の際には インパク トの見積もりや、不確実性の扱い方について悩む場面もあると思います。 そこで本記事では 経費精算業務の効率化に向けた、AIを活用した製品戦略と開発ロードマップ をご紹介します。 ロードマップ策定の目的やプロセス、不確実性への対処、期待されるアウトカムにも触れていますので、皆様のご参考になれば幸いです。 はじめに:経費精算業務の現状と課題 なぜAIが必要なのか 変化の激しいAI技術の不確実性をどう扱うか AI開発ロードマップの策定目的 どう実現していくのか AI開発ロードマップの概要 どんなアウトカムを目指すのか プロダクトでのAI活用を成功させるために なぜAIが必要なのか 少子高齢化 による労働力不足が進んでおり、企業はより少人数で高い付加価値を生み出すことが急務 です。 経理 業務をはじめバックオフィス業務も例外ではなく、AIを活用したさらなる効率化が求められます。 またAI活用により、既存プロセスのシステム化を超えた、経費精算のDX(業務の抜本改革)が可能になります。 業務の最適化を通じて、ガバナンス向上と 経理 担当者がコア業務に専念できる状態が期待できます。 変化の激しいAI技術の不確実性をどう扱うか AIは変化が激しく、AI活用によりプロダクトの提供価値がどれほど向上するかは不確実性がついてまわります。 計画を立てても、実際に運用してみると想定外の課題が発生することも少なくありません。 そのため、PoC(概念検証)を行い、最小実用プロダクト(MVP)を経て実運用に至る段階的なアプローチが重要になります。 最初から大きな範囲に適用するのではなく、 最も重要な機能からスモールスタートで進める ことが成功の鍵となると考えています。 AI開発ロードマップの策定目的 ラク スが「楽楽精算」のAI開発ロードマップを策定する主な目的は、 経理 担当者がより戦略的、創造的なコア業務に専念できる状態を創出する道筋を描く ことです。 冒頭で述べた通り、多くの 経理 担当者は経費精算などのノンコア業務に多くの時間を費やしており、企業の売上や利益に直接貢献する業務に注力できていないという課題があります。 AI技術を活用して経費精算業務を効率化し、 経理 担当者の負担を軽減することで、彼らが本来注力すべきコア業務に時間とリソースを割けるようにすることを目指しています。 どう実現していくのか ラク スは、AI開発ロードマップに沿って、以下のステップでAI機能を実装し、上記の目的を実現していきます。 出典: 当社プレスリリース   ステップ1:申請者・承認者向け機能のAI活用 最初のステップとして、 使用頻度が高く、利用ユーザー数が多い「申請者・承認者向け」の機能 からAIの導入を開始します。 申請者が経費申請を行う際の入力ミスを削減し、それに伴う差し戻しを減らすことで、承認者や 経理 担当者のチェック作業を軽減します。 今後リリース予定の「勘定科目の入力補助機能」は、このステップにおける重要な取り組みの一つで、現在PoCを進めているものです。 この機能は、AI- OCR によって領収書や請求書のデータを読み込み、過去の申請履歴や添付された領収書データを基に、AIが適切な勘定科目の入力を補助します。 これにより、申請者がどの勘定科目に分類すべきか迷うことが減り、経費申請時のミスや差し戻しを大幅に削減することが期待されています。 ステップ2: 経理 担当者向け機能のAI活用 次のステップとして、 より専門性が求められる「 経理 担当者向け」の機能 にAIを活用していきます。 具体的には、企業のガバナンス向上のための経費使用規定違反や利用目的が不明瞭な経費申請を検知するAI機能や、 経理 担当者の業務負荷を大きく削減するためのAIによる通常経費申請の自動承認・否認機能などの開発を目指します。 AI開発ロードマップの概要 ラク スは、2025年3月期から2029年3月期までのAI開発ロードマップを策定し、段階的にAI活用を進めることで、 経理 業務全体の生産性向上を目指しています。 出典: 当社プレスリリース     フェーズ1(2025年3月期~2026年3月期): 経理 担当者のチェック作業軽減 経費申請の入力補助機能 申請前の不備をチェックする機能 経費申請内容のチェック機能 承認経路を適切化する機能 フェーズ2(2027年3月期~2029年3月期): 経理 担当者がコア業務に専念できる状態の創出 経費使用規定違反や利用目的が不明瞭な経費申請を検知する機能 AIによる通常経費申請の自動承認・否認機能の開発 開発ロードマップの詳細はプレスリリースにも掲載しておりますので、併せてご覧ください。 https://www.rakus.co.jp/news/2024/1212.html どんなアウトカムを目指すのか ラク スは、これらのAI機能を実装することで、 1社あたりの経費精算作業時間を大幅に削減する ことを目指しています。 現在の 楽楽精算にAI機能を導入することにより、60%以上の作業時間短縮が可能になる と期待されています。 さらに、このロードマップは経費精算領域だけでなく、 他の楽楽シリーズのサービスにもAI活用を視野に入れ、機能アップデートを継続する ことも視野に入れています。 ラク スは、顧客基盤の強みを生かし、バックオフィスサービスへのAI活用を早期に展開することで、顧客企業全体の業務効率化、生産性向上に貢献しようとしています。 そして、 ラク スは バックオフィス業務支援領域で最も頼りにされる存在 を目指しています。 AI技術を駆使して、顧客の要望や法改正などの外部環境の変化に柔軟に対応しながら、常に進化し続けることで、バックオフィス業務のDXを包括的に支援し、業界をリードする存在となることを目指しています。 プロダクトでのAI活用を成功させるために AIを活用してプロダクトを成長させるためには、ユーザー視点でその有効性を評価し、不確実性を考慮しながら現実的な計画を立てることが重要です。 そのために、AIの導入目的を明確にし、単なる技術導入ではなく、 課題解決を中心に据える ことが求められます。 また、PoCからMVP、最適化といった段階を踏みながら、 柔軟に計画を修正していく ことも不可欠です。 そうすることで、AIの力を最大限に活かし、プロダクトの価値を高めることができると考えています。 本記事が、AI活用によるプロダクト成長のヒントとなれば幸いです。
アバター
はじめに こんにちは。楽楽精算の サポートエンジニア を担当している梅田です。 サポートエンジニアの役割は、楽楽精算の仕様や技術を理解するだけでなく、問い合わせ対応時に適切な調査を行い、開発チームやインフラチームと連携しながら迅速に解決策を導くことです。そのため、テスト技法や業務知識を学び、最新の技術動向を把握することが大切です。 ラク スでは「 学習し成長し続ける 」ことを行動指針のひとつとして掲げており、 私たちサポートエンジニアもこの指針を大切にしています 。 お客様により良いサポートを提供するためには、製品知識に加えて、技術や法制度の変化を理解し、 常に最新の情報に対応していくことが不可欠 です。 そこで、実務に役立つスキルを学び、 サポートエンジニアチーム内や所属している課の単位で 勉強会を開催・実施しています。本記事では、 これらの勉強会の取り組みを通じて得られた成果 をご紹介します。 はじめに なぜ勉強会が必要なのか? 学習の必要性を実感したお問い合わせエピソード 学習文化の醸成「勉強会のはじまりと進化」 2022年度:1人勉強会(自己学習から)のミニマムスタート 2023年度:チームの関心が高まり、継続的な学習文化へ 2024年度:業務時間内の勉強会が本格化 スキルアップとチームの成長を目指す「勉強会の目的と取り組み」 目的1. 実務に活かせるスキルの向上 工夫:実務に直結するテーマを選定 目的2. キャリアの方向性を明確にする 工夫:スキルの可視化とフィードバックの仕組み 目的3. チーム内の知識共有を促進する 工夫:持ち回り発表者制によるアクティブラーニングの実践 持ち回り発表者制のメリット・デメリットとサポート体制 メリット デメリット サポート体制 「3種の神器」でレベルアップ!サポートエンジニアの資格取得戦略 資格取得の推奨 「学習し成長し続ける」ために     なぜ勉強会が必要なのか? サポートエンジニアの業務では、技術調査や問い合わせ対応に加え、製品仕様や法制度の理解が欠かせません。しかし、新機能の追加や法改正のたびに、日々の業務だけで知識をすべて習得するのが難しいのが現実です。 学習の必要性を実感したお問い合わせエピソード 定時実行で生成されるファイルに、実行時間直前に作成したデータが含まれていないというお問い合わせがありました。 当初はデータが消失したのではないかと懸念しましたが、調査の結果、次回の定時実行には問題なく出力されることが確認できました。これは、システムの負荷を分散させるために処理タイミングを調整している設計によるもので、仕様どおりの動作でした。 もしシステムの設計を深く理解していれば、落ち着いて対応し、迅速に説明できたと感じました。 この経験を通じ、機能の仕様だけでなく、処理の仕組みや技術的な制約を深く理解し、お客様に的確に伝える力を磨く必要があると改めて認識しました。 学習文化の醸成「勉強会のはじまりと進化」 2022年度:1人勉強会(自己学習から)のミニマムスタート サポートエンジニアチームの学習文化は、私自身の1人勉強会(自主学習)から始まりました。当時の私は、 システムエンジニア としての経験はあったものの、テスト設計や品質保証の視点が不足しており、 お客様からの問い合わせに対し、システムのどの部分を検証すべきか、影響範囲をどのように調査すべきかを効率的に判断することが難しい状況でした。 そこで、 上司からの勧めもあり 、 業務の幅を広げるためにテスト設計とソフトウェア品質の評価、 ITサービスマネジメント という視点を 体系的に学習 することを決意しました。 JSTQB(ソフトウェアテスト技術者資格) を通じて、テスト設計の基礎やソフトウェアの品質を評価する視点を学びました。特に、「どのような観点で検証すれば、システムの品質を維持できるか」という考え方を学んだことで、問い合わせ対応の精度が向上しました。学んだ知識を活用することで、ログ分析や再現手順を整理し、調査をより明確に進めることができました。 また、 ITIL(ITサービス運用資格 ) を学ぶことで、問い合わせ対応を単発の業務として終わらせず、継続的な改善につなげるという視点も得ることができました。学習の結果、問い合わせ内容を蓄積し、対応フローの最適化やドキュメント整備を進めることで、よりスムーズな対応ができるようになりました。 2023年度:チームの関心が高まり、継続的な学習文化へ 私が取り組んでいた自主学習に興味を持つメンバーが増え、 有志のメンバーが集まり、少人数での勉強会が始まりました 。この勉強会では、 チームとして JSTQB 、 ITIL 、 日商簿記(財務会計資格) 、 OSS-DB(データベース資格) 、 LinuC(Linux資格) などの取得を目的として幅広い分野を学習しました 。 JSTQB ・ ITIL の学習を通して、 品質保証 と ITサービスマネジメント に関する知識を深め、 問い合わせ対応の精度向上 に貢献しました。 日商簿記 の学習では、 経理 業務に関する理解を深め、 運用に沿ったご案内 や 会計に関する適切なご説明 ができるようになり、 サポート品質の向上 に繋がりました。 OSS-DB ・ LinuC の学習では、 データベースやサーバー に関する知識を習得し、 開発・インフラチームとの連携 を円滑化し、 技術的な問い合わせへの対応力 を高めました。特に、 OSS-DB の勉強会では、梅田以外の メンバーが発表者 を務めることで、 「教え合う」文化 が生まれました。発表者を担当したメンバーは、 人に伝えることで知識を深め 、参加者も 積極的に質問 することで、 相互理解を深めました 。 2023年を通して、 個人の学び が チーム全体の成長 に繋がる 学習環境 が整い、 資格取得 のみならず、 業務に直結する知識 を学び、 サポートの質を高める文化 が根付きました。 2024年度:業務時間内の勉強会が本格化 2023年度までの取り組みが評価され、2024年度からは 業務時間内での勉強会開催が本格化 しました。これにより、 資格取得 を目的とした学習から、 実務 や 自分たちのスキル成長 に直結する勉強会へと発展しました。 勉強会開催の推移 この年に新たに取り組んだ勉強会のテーマには、次のようなものがあります。 なぜなぜ分析勉強会 : 問い合わせ対応時の問題を深掘りし、問題の本質に迫るなぜなぜ分析の手法を学びました。この勉強会を通じて、 分析視点が身に付き、問題の本質を捉えた対応と問い合わせの迅速な回答、業務効率化 に繋げることができました。 生成AIの活用事例紹介 :生成AIを活用した問い合わせ対応の効率化や業務改善への応用方法を紹介しました。この勉強会を通じて、 問い合わせ対応の効率化 や 業務改善 に繋がる 具体的なア イデア が得られました。( 前回のブログ記事 で活用事例として紹介 しました) 経理 実務勉強会 :決算業務の流れを理解し、経費精算システム(楽楽精算)や会計システムとの連携について学習しました。この勉強会を通じて、 経理 業務全体の流れ と、 各システムがどのように連携しているか をメンバー全員が理解できました。 こうした実践的な学びを通じて、 業務の効率化や品質向上につながる知識を身につける機会が増えました。 スキルアップ とチームの成長を目指す「勉強会の目的と取り組み」 サポートエンジニアチームでは、 メンバーの スキルアップ と業務の質向上を目的に勉強会を実施 しています。単なる知識習得にとどまらず、 実務に活かし、キャリアにつなげ、チーム全体を成長させる ことを目指し、次の 3つの目的 を掲げています。 目的1. 実務に活かせるスキルの向上 サポート業務では、 製品の検証や運用、品質保証の知識に加え、テスト設計や自動化、影響調査などのスキルが必要です 。これらを実践的に学び、日々の業務に活かせるようにすることが勉強会の第一の目的です。 工夫:実務に直結するテーマを選定 メンバーが「知識を得るだけでなく、すぐに活用できる」よう、勉強会のテーマを業務課題と結びつけています。 たとえば、 Playwright 勉強会 では、E2E(エンドツーエンド)テストの自動化手法を ハンズオン形式 で学びました。参加者はすぐに業務へ応用できました。実際に、勉強会の翌週には 自動化テストの導入を進めるメンバーが現れ 、成果につながりました。 目的2. キャリアの方向性を明確にする サポートエンジニアとしての専門性を高めるだけでなく、品質保証やシステム運用、開発との連携など、多様なキャリアの選択肢を意識できるようにすることを目的としています。そのため、 ITスキル標準 ( IT 分野で働く人がどんなスキルを持っているかを客観的に評価するための基準) を活用してスキルの棚卸しを行い、各メンバーが習得すべきスキルを明確にしています。 習得すべきスキルと達成度を可視化 工夫:スキルの可視化とフィードバックの仕組み ITスキル標準 を基に、サポートエンジニアに必要なスキルを整理し、それに沿った勉強会のロードマップを作成しました。スキル習得の進捗を確認しながら、次に学ぶべき内容を計画し、定期的な1on1で上司と話し合う機会を設けています。 参考:2024年度勉強会のロードマップ 目的3. チーム内の知識共有を促進する 勉強会を通じて、 チーム全体のスキルを向上させ、属人化を防ぐ ことも大きな目的です。知識が特定のメンバーに偏らないようにすることで、 誰もが安心して業務に取り組める環境 を整えています。 工夫:持ち回り発表者制によるアクティブラーニングの実践 サポートエンジニアチームでは、「自分が知りたいことを学び、それを共有することで理解を深める」という アクティブラーニング の考え方を取り入れ、メンバーが交代で発表を担当する 持ち回り発表者制 を導入しています。 勉強会のロードマップは、運営者(梅田)が作成し、各メンバーが必要なスキルを明確にできるよう調整しています。発表者は、このロードマップに沿って準備を進め、知識を深めながらアウトプットの機会を得ることで、実務に直結するスキルを身につけています。 また、「分からなければ聞く」「確認しやすい」というサポートエンジニアチームの文化を活かし、発表後のディスカッションを活発に行うことで、相互理解を深めています。 持ち回り発表者制のメリット・デメリットとサポート体制 持ち回り発表者制は、チーム全員が主体的に学ぶ環境をつくるために導入しました。 しかし、発表者にとっては学びの機会となる一方で、準備の負担や進行の難しさといった課題もあります。そのため、発表者制のメリットとデメリットを整理し、より円滑に進めるためのサポート体制を整えています。 メリット 参加者が受け身にならず、主体的に学ぶ機会が増える 発表者を担当することで、理解を深めるだけでなく、伝える力も身につく 特定の人に負担が偏らず、チーム全体で スキルアップ できる デメリット 初めて発表者を担当するメンバーにとっては、準備に時間がかかる 人によって説明のレベルや進行のスムーズさに差が出る サポート体制 こうした課題を解決するために、 発表者向けのサポート体制 を整えています。初めて発表者を担当するメンバーには、過去の資料を共有し、必要に応じて経験者がフォローすることで、負担を軽減しています。 「3種の神器」でレベルアップ!サポートエンジニアの資格取得戦略 資格取得の推奨 サポートエンジニアチームでは、業務に直結する資格の取得を推奨しており、特に JSTQB ・ITILv4・ 日商簿記 3級 は「3種の神器」として定着しています。これらに加えて、運用管理・技術インフラ・品質保証の知識を深めるため、さまざまな資格取得にも取り組んでいます。 資格取得は スキルの可視化 や キャリア形成の指標 となるだけでなく、実務での適用を意識した学習につながっています。 以下に、取得を推奨している資格を 「品質保証」「運用・管理」「技術インフラ」「業務知識」 のカテゴリに分けて整理しました。 サポートエンジアチーム推奨資格一覧 カテゴリ 資格名 推奨度 説明 品質保証 JSTQB ◎ ソフトウェアテスト に関する国際的な資格認定制度で、品質保証に必要な知識を体系的に学ぶことができる。 品質保証 JCSQE ○ ソフトウェア品質保証に関する知識を体系的に学べる資格。 開発プロセス や品質管理の基礎を理解し、実務での品質向上に役立てることができる。 運用・管理 ITIL ◎ ITサービスマネジメント に関するベストプ ラク ティスを体系化した フレームワーク 。ITサービスの品質向上や効率化を体系的に学ぶことができる。 技術インフラ LinuC ○ Linux のシステム管理や運用に関する知識を証明する資格。サーバー管理やインフラ構築の基礎を体系的に学ぶことができる。 技術インフラ OSS-DB ○ オープンソース のデータベースに関する資格認定制度。特に PostgreSQL の管理や運用に必要な知識を体系的に学ぶことができる。 技術インフラ AWS(SAA) ○ クラウド サービス「 AWS 」に関する技術知識や運用スキルを証明する資格。インフラ設計やセキュリティ対策の理解を深め、 クラウド 環境の適切な活用に役立てることができる。 業務知識 日商簿記 ◎ 企業の運営や会計に関する基礎知識を体系的に学ぶことができる。 サポートエンジニアチーム全体のスキル向上を目的に、資格取得を積極的に推進した結果、 資格取得者は年々増加 しており、以下のような推移を示しています。 資格取得者数の推移 「学習し成長し続ける」ために サポートエンジニアチームの勉強会は、2024年度から本格的に始まり、まだ発展の途中です。現状、発表者が業務多忙や急遽の休みなどで勉強会がスキップになることがあります。 勉強会を停滞させずに継続するには、発表者の負担を分散し、進行を柔軟に調整できる仕組みを整えることが重要です。 今後も、勉強会の着実な継続を続けて、自分たちの成長につなげるために、勉強会運営の工夫を続けていきます。 また、これまでチーム内での学習が中心でしたが、他部署でも専門的な勉強会( リファクタリング 最新技法など)が開催されていることが分かりました。今後は、 社内の知見を活かし、他部署の発表者を招いたり、資料提供を受けたりすることで、学習の幅を広げていきます 。 私自身も、勉強会の質を向上させるために、 LT会 やテックブログへの参加を継続し、資料作成やプレゼンテーションのスキルを磨いていきます。さらに、チーム内で関心の高いコンテンツ(最近であればコンテナ技術やNotebookLMを活用した業務改善等)についても学び、実務への応用を進める予定です。 「 学習し成長し続ける 」 という姿勢を大切にし、新しい知識を積極的に吸収しながら他業界の優れた事例も取り入れ、個人とチームの成長につなげていきます。勉強会を実践的な学びの場へと進化させ、さらに成長を加速させるため、今後も改善を続けていきます。 const ctx = document.getElementById('studyChart').getContext('2d'); const studyChart = new Chart(ctx, { type: 'bar', data: { labels: ['2022年度', '2023年度', '2024年度'], datasets: [ { label: '資格取得向け', data: [1, 3, 0], // 各年の資格取得向け勉強会数 backgroundColor: 'rgba(54, 162, 235, 0.6)' }, { label: '業務知識向け', data: [0, 0, 7], // 各年の業務知識向け勉強会数 backgroundColor: 'rgba(255, 206, 86, 0.6)' }, { label: '技術向け', data: [0, 0, 5], // 各年の技術向け勉強会数 backgroundColor: 'rgba(75, 192, 192, 0.6)' }, { label: '最新技術&ツール', data: [0, 0, 3], // 各年の最新技術系勉強会数 backgroundColor: 'rgba(255, 99, 132, 0.6)' } ] }, options: { responsive: true, scales: { x: { stacked: true }, y: { stacked: true, beginAtZero: true } } } }); const ctx2 = document.getElementById('radarChart').getContext('2d'); const data = { labels: ['実務/テスト', '実務/プロダクトサポート', '知識/商材理解', '知識/IT共通', '知識/専門知識'], datasets: [{ label: '達成度', data: [10, 59, 67, 50, 82], borderColor: 'rgba(54, 162, 235, 1)', backgroundColor: 'rgba(54, 162, 235, 0.2)', pointBackgroundColor: 'rgba(54, 162, 235, 1)', pointBorderColor: '#fff', pointHoverBackgroundColor: '#fff', pointHoverBorderColor: 'rgba(54, 162, 235, 1)' }] }; const options = { responsive: true, maintainAspectRatio: false, aspectRatio: 1, // チャートの縦横比を調整(小さく) layout: { padding: 0 // 余白を最小限に }, plugins: { legend: { display: false // 凡例を非表示 } }, scales: { r: { suggestedMin: 0, suggestedMax: 100, ticks: { stepSize: 25, callback: function(value) { return value + '%'; } }, pointLabels: { font: { size: 10 // 軸ラベルのフォントサイズを小さく } }, grid: { lineWidth: 0.5 // グリッドの線を細くして余白を減らす } } } }; new Chart(ctx2, { type: 'radar', data: data, options: options }); const ctx3 = document.getElementById('qualificationChart2').getContext('2d'); const data2 = { labels: ['2022年度', '2023年度', '2024年度'], datasets: [ { label: '資格保有者数(累計)', data: [3, 17, 23], // 各年度の資格保有者数(累計) backgroundColor: 'rgba(0, 115, 207, 0.2)' // 青色 } ] }; const qualificationChart2 = new Chart(ctx3, { type: 'bar', data: data2, options: { responsive: true, plugins: { legend: { display: false } // 凡例を非表示 }, scales: { y: { beginAtZero: true, title: { display: true, text: '資格保有者数' }, ticks: { precision: 0 } // 小数点を表示しない } } } });
アバター
こんにちは!株式会社 ラク スで技術広報を担当している川東と申します! 2025年2月27日に開催された 『EMConf JP』 は、エンジニア リングマ ネジメントを実践するEMにとって刺激的なイベントでした! 公式サイト( fortee.jp/emconf-2025 )にもある通り、今回のテーマは 「増幅」 と 「触媒」 。すなわち、EMたちが持つ熱意や知見をより大きく広げ、組織全体に好影響を与えるセッションが盛りだくさんでした。熱気あふれる会場には多くのEMやエンジニアが集まり、リアルな知見や体験を共有する貴重な場となりました。 弊社からはPdMチームのEMである稲垣が登壇しております! 本レポートでは、ホールAとホールBの両方で行われたセッションを、筆者の独断と偏見で学びを感じたポイントに絞って振り返っていきたいと思います! (実際にイベントに参加して技術広報2名で全セッション視聴してきました!) セッションレビュー キーノート エンジニア リングマ ネージャという働き方と知識体系のロードマップ(広木大地/レクター) EMとしての役割は「エンジニアの管理職」から進化し、【4つのP】―人材育成、技術戦略、プロジェクト管理、プロダクト連携―が求められるようになったという視点が印象的でした。また、フェ イルファ ストの精神や 心理的 安全性の確保など、現場で実践すべきポイントを明確に示してくれました。 hirokidaichi.github.io n=1の経験が紡ぐエンジニア リングマ ネジメントの可能性(岩瀬義昌/ NTTコム 、fukabori.fm) 個人的に一番印象に残ったセッションです。実践に基づくリアルな体験談と、EMとしての具体的なノウハウ(「ポエムドリブンマネジメント」など)が盛り込まれており、すぐにでも現場で活かしたい内容ばかりでした。 speakerdeck.com 【ホールA】 Potential EM制度を始めた理由、そして2年後にやめた理由(青木啓剛/estie) 期間限定でEMとしての素養を見極める制度の試み。その中で、EMに求める最重要要件や、常に組織を変革していく必要性が浮き彫りにされました。 speakerdeck.com エンジニアリング価値を黒字化する、バリューベース戦略を用いた技術戦略策定の道のり( Kazuki Maeda/atama plus) 経営戦略と技術戦略の橋渡しとして、バリューベース戦略を活用する方法が紹介され、経営層と対等な目線で議論を進めるための具体的なアプローチが印象に残りました。 speakerdeck.com 1行のコードから社会課題の解決へ:EMの探究、事業・技術・組織を紡ぐ実践知(熊谷遼平/レディーフォー) 事業、技術、組織の3軸からEMの役割を探究している点が印象的でした。特に、学習文化形成のために業務時間の3割を本業以外に充てる施策は、メンバーの成長と組織の活性化に寄与すると感じました。 speakerdeck.com Two Blades, One Journey: Engineering While Managing(ohbarye/スマートバンク) EMとICの両立が可能であるという実体験からの提案。EMとしての柔軟な働き方や、必要に応じた役割転換のヒントが、参加者にとって大いに参考になるだろうと思わせるセッションでした。 speakerdeck.com 「共創型エンジニア リングマ ネジメント」の挑戦と実践(うっしー/kubell) 組織拡大に伴う局所最適やサイロ化の問題に対し、EMグループ全体で課題解決に取り組む姿勢は、共創の精神そのものでした。全員が責任を共有するという考え方は、今後の組織運営においても非常に魅力的です。参考にしたいです! speakerdeck.com サバイバルモード下でのエンジニア リングマ ネジメント(こにふぁー/Kyash) 売上減少や人材流出といった厳しい現場での実践例は、危機時における判断の難しさを改めて認識させられる内容でした。​会場は立ち見が出るほどに盛況でした! speakerdeck.com 【ホールB】 プロダクト部門のマネージャー全員でマネジメントポリシーを宣言した記録(piro takahara/スタンバイ) 部門全体で共通のポリシーを作ることで、情報共有と成功体験のすり合わせが図られ、組織拡大の中での一体感を醸成する重要な取り組みとして参考になりました。 speakerdeck.com 急成長する企業で作った、エンジニアが輝ける制度(池ノ上倫士/SHIFT) EMがいない状況下で導入されたスキルツリーや評価制度が、給与上昇率向上や 離職率 の低下につながった具体例が紹介され、エンジニアが輝ける土台作りの重要性を再認識させられました。 speakerdeck.com 楽しいぞEM拡張パズル!課題と共に役割を広げちゃおう!(笹健太/クリエーションライン) 「EMって何をすればいいのか?」という疑問を、PMトライアングルの視点から整理。完璧なスー パーマン ではなく、得意分野を生かしながら成長する姿勢は、多くのEMにも共感できるセッションだったと思います。 speakerdeck.com 大規模 アジャイル から学ぶ、エンジニア リングマ ネジメントの本質(Kittaka shun/TOKIUM) 複雑化する組織で、 アジャイル 手法を応用した意思決定と役割分担の工夫が紹介され、EMとしての自信とポジティブな取り組み方を後押しする内容でした。 speakerdeck.com わたしがEMとして入社した「最初の100日」の過ごし方(daiksy/ はてな ) パラシュート人事としてのスタートに伴う期待値調整や、既存の文化へのリスペクトの重要性を具体的に語っており、新任EMにとっては必聴のアド バイス となりました。 speakerdeck.com プレイングマネージャー は本当に悪なのか? - 令和時代の プレイングマネージャー を戦略的にハックする(すずけん/カミナシ) 技術力とマネジメントを同時に発揮する プレイングマネージャー のメリットと課題を、実践的な視点で議論。意思決定と実行の迅速さ、評価への納得感が強調されました。 speakerdeck.com EMの仕事がLVアップした3つの視点 ~これまでに出会ったEMから学んだこと~(shinden (新田 智啓)/Datachain) 「WHY(価値を知る)」「WHAT(理想と課題設定)」「HOW(体系的な学び)」という3つの視点で、EMとしてのレベルアップを図る方法がシンプルかつ説得力をもって伝えられました。 speakerdeck.com 多様なマネジメント経験から導き出した、事業成長を支えるEMの4つの コンピテンシー (稲垣剛之/ ラク ス) 弊社(株式会社 ラク ス)からはPdMチームを統括するEMの稲垣が登壇しました。今回、大変多くのCfPの中から弊社EMのCfPを採択していただけたのはとても光栄でした! セッションの内容としては、論理的かつ具体的に、EMが事業成長にどう寄与できるかを弊社の評価軸である コンピテンシー という形で整理しました。実践的なヒントをお届けできていれば幸いです。 © 2025 EMConf JP 2025 実行委員会 speakerdeck.com 「Govtechという巨大な山に挑む」エンジニアが活躍する、世界最強の行政DXチームを目指す(井原正博/廣瀬幸帆/GovTech東京) 新しい公共 価値の創出に向け、内製化と組織文化の再構築を目指す壮大なビジョンが示され、行政領域でのチャレンジとして非常に刺激的な内容でした。 まとめ EMConf JPはオフライン限定のイベントでしたが、会場の熱量にすさまじいものを感じました! 各セッションを通じて感じたのは、確かにEMとしての業務は容易ではないものの、失敗を恐れずチャレンジすることで得られる学びや、組織全体に与えられる インパク トの大きさです。 今後もこのようなイベントが開催されることで、各々の知見や経験を共有しながら、より多くのEMが組織の成長に貢献していく未来を期待せずにはいられません。 EMに興味のある皆さん!ぜひ、この熱い波に乗って、EMとしてのキャリアをさらに進化させてみてはいかがでしょうか?
アバター
はじめに エンジニア2年目のTKDSです! 今回は GitHub のデ バイス フローを利用したユーザー認証の方法についてご紹介します。 デ バイス フローはブラウザに直接アクセスできない CLI ツールでも GitHub 側に認証を依頼できる機能です。 今回はデ バイス フローによる認証を経て発行されるアクセス トーク ンを使って、認証・認可を要求したユーザー名を取得し、ユーザー確認に利用するところまでやっていきます。 はじめに GitHub Appsによるデバイスフロー 実際にやってみる GitHub Appsの準備 アプリの準備 実行 まとめ 参考文献 GitHub Appsによるデ バイス フロー 認証リク エス ト時に返ってくるURL・ユーザーコードを使用し、ブラウザで認証を行うとアプリケーション側でアクセス トーク ンの発行を行えます。 アクセス トーク ンのリク エス トには初回の認証リク エス ト時に返ってくるデ バイス コードが必要なため、アクセス トーク ンのリク エス トを行うエンドポイントを単純に叩いてアクセス トーク ンを横取りされる危険性は低いと考えられます。 この方法を利用するメリットとしては、 - ユーザーはURLを開いて、ユーザーコードを入力するだけでいい - ユーザーの認証機能を直接提供せずに、 GitHub に認証を依頼できる - ユーザーに認証させたあと取得できるユーザー トーク ンを使いユーザー情報を取得できるので、認証依頼時点でユーザーの情報を知ってる必要がなく、エンドポイントから返ってきたユーザー トーク ンを使ってはじめてユーザーの特定ができる - GitHub Appsで権限を絞れば、ほとんどの情報取得をシャットアウトできる→ユーザー トーク ンはもちろんもらさない前提で安全性も高い 等があります。 ユーザー側の利便性、アプリ自体はURLとユーザーコードを提供するだけの利便性がいいと思って今回採用してみました。 では次に実装例を示していきます。 実際にやってみる 今回やりたかったのは、ユーザー認証と認証したユーザーのアカウント名の取得です。 まず、今回の処理の流れを以下に示します。 ユーザーがまずアプリを通して、デ バイス フローを GitHub に依頼します。 そして、返ってきた値のうち、URLとuser_codeをユーザーに提示します。 ユーザーが認証するまでの間、アプリ側は GitHub の トーク ン払い出しのエンドポイントをたたきつづけます。 ユーザーは表示されたURLにアクセスし、ログイン後、アクセス トーク ンの入力・権限の認可を行います。 ユーザーによる認可が完了した以降で、アクセス トーク ン払い出しURLにアクセスすると、ユーザーコードを使って認証・認可をしたユーザーに紐付いたアクセス トーク ンを取得できます。 この時点でアプリはユーザーが誰かわからないので、アクセス トーク ンを使ってユーザーの情報を取得します。   最終的にレスポンスのloginキーに含まれてるユーザー名を確認することで、どのユーザーがアクセス トーク ンを払い出したかわかります。 では、実際に準備していきます。 GitHub Appsの準備 今回のデ バイス フロー認証には GitHub Appが必要なため用意します。 New GitHub Appで GitHub Appsの作成をしてください。 設定画面に遷移するので、 Enable Device Flow にチェックをいれてください。 次に、 GitHub Appによって発行された トーク ンになんの操作を許すか設定します。 今回は個人のメールアドレスだけですが、ここで設定を変えれば、所属してるオーガナイゼーションの取得なども可能になります。 今回は一番弱い権限かつ取得しても問題なさそうな情報にしたいので、Email adressesの項目をRead-Onlyで設定します。 これで GitHub Appの設定は完了です。 client_id を控えておいてください、あとで使います。 アプリの準備 CLI アプリを準備していきます。 CLI アプリ側でやりたいことは、デ バイス フローのリク エス ト、ユーザーへのURLとuser_codeの提示、アクセス トーク ン払い出しのポーリング、取得したアクセス トーク ンによるユーザー情報の取得です。 Goによるコードを書きました。 サードパーティ のライブラリ使ってないため、そのまま動くと思います。 今回は先にgo mod initしてしまっていたので、go.modがある状態前提です。 package main import ( "context" "encoding/json" "fmt" "io" "log" "net/http" "net/url" "os" "strings" "time" ) const ( // clientID = "" // GitHub AppのClient ID deviceAuthURL = "https://github.com/login/device/code" accessTokenURL = "https://github.com/login/oauth/access_token" githubUserURL = "https://api.github.com/user" pollingInterval = 5 * time.Second // intervalより長くしない、とりあえず5秒 ) // デバイスコード type deviceCodeResponse struct { DeviceCode string `json:"device_code"` UserCode string `json:"user_code"` VerificationURI string `json:"verification_uri"` ExpiresIn int `json:"expires_in"` Interval int `json:"interval"` } // アクセストークン type accessTokenResponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` Scope string `json:"scope"` Error string `json:"error,omitempty"` } var clientID string func main() { clientID = os.Getenv( "CLIENT_ID" ) // 1. デバイスコードを取得 deviceCodeRes, err := requestDeviceFlow() if err != nil { log.Fatal(err) } fmt.Printf( "verification_uri: %s \n " , deviceCodeRes.VerificationURI) fmt.Printf( "user_code: %s \n " , deviceCodeRes.UserCode) // 2. アクセストークンを取得(ポーリング) timeout := 3 * time.Minute ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() var accessTokenResponse accessTokenResponse loop: for { select { case <-ctx.Done(): default : accessTokenRes, err := requestAccessToken(deviceCodeRes.DeviceCode) if err != nil { log.Fatal(err) } if accessTokenRes.AccessToken != "" { accessTokenResponse = *accessTokenRes break loop } switch accessTokenRes.Error { case "authorization_pending" : fmt.Println( "ユーザー認証待ち" ) case "slow_down" : fmt.Println( "ポーリング感覚短すぎ" ) case "access_denied" : log.Fatal( "認証失敗" ) case "expired_token" : log.Fatal( "デバイスコードの有効期限が切れた" ) default : log.Fatal(accessTokenResponse.Error) } // time.Sleep(pollingInterval) } // time.Sleep(time.Duration(deviceCodeRes.Interval) * time.Second) time.Sleep(pollingInterval) } fmt.Println(accessTokenResponse) userInfo, err := getGitHubUserInfo(accessTokenResponse.AccessToken) if err != nil { log.Fatal(err) } fmt.Println(userInfo) } func requestDeviceFlow() (*deviceCodeResponse, error ) { client := http.Client{} sendData := url.Values{} sendData.Set( "client_id" , clientID) sendData.Set( "scope" , "repo,user" ) req, err := http.NewRequest( "POST" , deviceAuthURL, strings.NewReader(sendData.Encode())) if err != nil { return nil , err } req.Header.Set( "Accept" , "application/json" ) req.Header.Set( "Content-Type" , "application/x-www-form-urlencoded" ) dCodeResp, err := client.Do(req) if err != nil { log.Fatal(err) } defer dCodeResp.Body.Close() var deviceCodeRes deviceCodeResponse err = json.NewDecoder(dCodeResp.Body).Decode(&deviceCodeRes) if err != nil { return nil , err } return &deviceCodeRes, nil } func requestAccessToken(deviceCode string ) (*accessTokenResponse, error ) { client := &http.Client{} data := url.Values{} data.Set( "client_id" , clientID) data.Set( "device_code" , deviceCode) data.Set( "grant_type" , "urn:ietf:params:oauth:grant-type:device_code" ) req, err := http.NewRequest( "POST" , accessTokenURL, strings.NewReader(data.Encode())) if err != nil { return nil , err } req.Header.Set( "Accept" , "application/json" ) req.Header.Set( "Content-Type" , "application/x-www-form-urlencoded" ) tokenResp, err := client.Do(req) if err != nil { return nil , err } defer tokenResp.Body.Close() var tokenRes accessTokenResponse err = json.NewDecoder(tokenResp.Body).Decode(&tokenRes) if err != nil { return nil , err } return &tokenRes, nil } func getGitHubUserInfo(accessToken string ) ( string , error ) { req, err := http.NewRequest( "GET" , githubUserURL, nil ) if err != nil { return "" , err } req.Header.Set( "Authorization" , "Bearer " +accessToken) req.Header.Set( "Accept" , "application/vnd.github.v3+json" ) client := &http.Client{} resp, err := client.Do(req) if err != nil { return "" , err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return "" , err } return string (body), nil } 実行 漏れたら困る値もあるのでぼかしつつ実際の流れを載せていきます。 1 . アプリ起動 環境変数 からclient_idを取得するようにしてあるので、 コマンドライン で指定して起動します。 client_idには、先ほど GitHub Appを作ったときに控えておいたclient_idを指定してください。 CLIENT_ID="<控えておいたclient_id>" go run main.go verification_uri: https://github.com/login/device user_code: xxxx-xxxx 2 . ブラウザでユーザーコードを入力 表示されたURLにアクセスし、user_codeを入力してください。 ユーザーコードの認証が成功すると、権限委譲の画面に移行するので、緑のボタンを押してください。 成功すると以下画面が表示されます。 これでユーザー側操作は完了です。 3 . アクセス トーク ンの取得とユーザー情報の取得 アプリ側は2の間ずっと トーク ン払い出しのエンドポイントをポーリングしています。 これには事前に払い出したclient_idと認証リク エス ト時にuser_codeと一緒に返ってくるdevice_codeを使用します。 取得できたアクセス トーク ンをもとにユーザー情報を取得します。 ここで取得したユーザー情報をもとに、アクセスしていいユーザーなのか判別などします。 ※ここの画面のスクショはアクセス トーク ンやユーザー情報が出てしまっているので割愛します。 以上で、デ バイス フローによるユーザー認証ができました。 まとめ 今回はデ バイス フローを利用して、 ユーザー認証を行いました。 今回用意したアプリではclient_idをユーザー側が入力しましたが、サーバー側で行うようにすることでより堅牢にユーザー認証できるのではないかと考えられます。 GitHub 側に認証を任せて簡単にユーザー認証ができるのは非常に便利です。 ここまで読んでいただきありがとうございました! 参考文献 https://docs.github.com/ja/enterprise-cloud@latest/apps/creating-github-apps/writing-code-for-a-github-app/building-a-cli-with-a-github-app https://docs.github.com/ja/enterprise-cloud@latest/apps/creating-github-apps/writing-code-for-a-github-app/building-a-login-with-github-button-with-a-github-app https://docs.github.com/ja/enterprise-cloud@latest/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app#using-the-device-flow-to-generate-a-user-access-token https://www.authlete.com/ja/developers/device_flow/ https://tex2e.github.io/rfc-translater/html/rfc8628.html?utm_source=chatgpt.com
アバター
はじめに ラク スのプロダクトデザイン組織マネージャーの小林です。 私たちの所属する 「プロダクトデザイン課」は、お客様の業務課題を解決すべく、全プロダクトのUI/UXデザインを担うチーム です。 2025年、プロダクトデザイン課はお客様への価値提供をより一層高めるため、新たな挑戦に踏み出します。 そこで改めて組織紹介も兼ね、ミッション・ビジョン、これまでの歩み、現在取り組んでいること、今後の挑戦についてお話したいと思います。 はじめに プロダクトデザイン課のミッション・ビジョン ミッション・ビジョン策定に至る経緯 顧客理解を高めるデザイナーの取り組み 顧客ニーズを踏まえ主体的な提案も増える UI刷新への挑戦 顧客価値を高める今後の取り組み プロダクトデザイン課のミッション・ビジョン 私たちデザイナーに共通するお客様の課題解決への思いと、 その実現へ向けてあるべきチームの状態を宣言しました。 ミッション プロダクト開発の中心となり、顧客課題を解決する優れたUXを生み出す ビジョン 日本を代表するBtoB SaaS の優れたUXを生み出すプロフェッショナルチーム ミッション・ビジョン策定に至る経緯 ラク スのデザインチームは2012年にゼロから立ち上げました。 私は開発マネージャーを務めていましたが、より課題解決につながるプロダクト作りへの思いと共に、 UI設計や ユーザビリティ への熱意も高まり、デザイン専門チームを作ることにしたのです。 立ち上げ期はデザイナーも少なく、 開発プロセス への関わり方、採用・育成方法、UIルールも手探りしながら進めていきました。 プロダクト規模の拡大とともに担当するUI設計規模も拡大しましたが、デザイナーの上流工程への関わり方はまだまだ限定的でした。 開発チームでほぼ決定した要件・仕様・期日に対し、きれいなUIを調整して提供するだけの時期もありました。 より主体的に顧客価値を提供するため、より深く 開発プロセス に関わりたいという思いが強くなっていました。 そのための課題が、 顧客業務や製品仕様の理解を高めること でした。 ラク スのプロダクトは業務 ドメイン にあわせ、個別に深く課題解決に踏み込んでいます。 その分、製品仕様の複雑性や難易度は高くなるとも言えます。さらに運用歴が長いプロダクトは開発経緯への理解も必要となります。 私たちも改めて、顧客課題解決に踏み込んでいくことを決意し、ミッション・ビジョンを策定することにしました。そこでの思いは、主に下記の二つに集約されます。 ・表面上のUIでなく、顧客業務の課題をしっかり理解し、課題解決できるUI/UXをつくることがデザイナーの一番の役割 ・ビジネスサイド、プロダクトマネージャー、エンジニアなど、すべての部署とコミュニケーションをとり、要求を満たすUIをデザイナーが主体的に設計していきたい まとめると、かつてデザイナーは、開発チームが決めた要件・仕様をもとにUIを整える立場でした。 しかし、それでは本質的な課題解決ができないため、 「表面的なデザインではなく、顧客課題を解決するUX設計」 を重視する組織とするために、現在のミッション・ビジョンを定めました。 ただ顧客課題は日々進化しますので、顧客理解に終わりはありません。現在でも上流工程への関わり方についてまだまだ取り組むべきことは多いと考えています。 顧客理解を高めるデザイナーの取り組み 当社のデザイナーは顧客理解を深めるため、以下のような取り組みにも力をいれています。 ・商談動画の視聴 営業やカスタマーサクセスとお客様との会話を 追体験 し、リアルな業務課題を理解する取り組みです。 ・業務見学、 ヒアリ ング 実際に業務現場を把握し、より実用的なUI設計などに活かします。 ・資格取得 ラク スのプロダクトが手掛ける領域には、電子帳票保存法や インボイス 、 労働基準法 など、満たすべき法要件があります。デザイナーもUI設計を行う上で、必ず理解しておく必要があります。基礎知識として、簿記など関連資格の取得に取り組んでいます。 ・勉強会 幅広い顧客業務を体系的に理解するため、 経理 など分野別の勉強会も行っています。 業務の必要に応じ参加するほか、入社オンボーディングプログラム内でも動画視聴できるようにしています。 顧客ニーズを踏まえ主体的な提案も増える これらの取り組みを通じ、顧客ニーズを踏まえて主体的な提案を行えることも増え、より多くのお客様にご満足いただけるようになりました。 ここでは、 メールディーラー「メール対応画面のUI刷新」事例 をご紹介します。 メールディーラーはメール共有管理ツールです。 www.maildealer.jp ビジネスサイドが持つ顧客の声をデザイナーと密に連携し、既存・新規両方のお客様に満足いただけるUIを提供できた事例です。 メールディーラーは運用歴が20年以上の老舗プロダクトで、当時の インストーラ ー型メールUIを参考にしていました。 使い慣れているお客様も非常に多い一方、Web メーラー の時代にあわせたUI/UXのアップデートも必要でした。 そこでデザイナーを中心にプロトタイプを制作し、ビジネスサイドに提案しました。 営業、製品企画、カスタマーサクセスと目指す方向性を合意したのち、製品企画が既存顧客の業務運用に支障がないかを確認、営業が商談での反応を伺いながら顧客の声を集約し、UIUXの設計を実施しました。 さらに、フロントエンドエンジニア、サーバサイドエンジニアともシステム上の実現可能性をすり合わせたのち、無事に新たなUIをリリースすることができました。 UI刷新への挑戦 ラク スは 中長期を見据えた継続的なUX改善 を目指しています。 この一環で、 ラク スのプロダクトを横断するUI刷新 がスタートしました。 背景は、 SaaS の導入が一般的となり、複数の製品を検討する企業が増えてきたことです。 ラク スのプロダクトは 顧客の業務 ドメイン にあわせ、深く課題解決に踏み込むベスト・オブ・ブリード型の開発 を行っています。 そのため各プロダクトのUIも、各業務 ドメイン ごとに個別性が高いものとなっています。 今後はお客様が複数の SaaS を利用することも想定し、 どのプロダクトを利用しても共通体験を提供できるようにしたい と考えています。 現在、UI刷新のゴールや、どのように顧客体験を改善すべきか、役員陣も交えて議論しています。 具体的なUI刷新の進め方については、デザイナーも積極的に発案や取りまとめを行っており、デザイン、開発、ビジネスサイド間で活発に議論している最中です。 顧客課題解決と複数 ドメイン をまたぐ共通体験、両方を提供できるようしっかりと役割を果たしていきたいと思います。 ここでは、刷新後に予定されているUIを少しだけお見せします。 顧客価値を高める今後の取り組み UI刷新の一環で、デザインシステムの構築・導入にも取り組んでいきます。 この詳細は次回の記事でご紹介いたします。 UX向上を実現するためには、さらに深い顧客理解が欠かせません。 より積極的に顧客 ヒアリ ングやユーザーテストにも取り組んでいきたいと考えています。
アバター
はじめに プロダクトをつくる私たちエンジニアや組織は 「本当に顧客のために開発できているだろうか?」 と、一度は自問したことがあるのではないでしょうか。 事業成長し、組織が大きくなるにつれ、エンジニアと顧客の距離は遠くなりがちです。 かつては直接届いていた「この機能、助かりました」「ここが使いづらい」といった顧客の生の声も届きづらくなります。 複数チームでの分業や、多くの ステークホルダー が関わる場合、このように感じる方もいるのではないでしょうか。 こうした環境下では、  「リリースした機能は、本当に役に立ったのだろうか?」  「顧客はどんな機能をよく使い、どんな課題に直面しているのだろうか?」  「この対応の優先度は、本当に正しいのだろうか?」 といった疑問がよぎることもあるのではないでしょうか? 私たち ラク スも例外ではありません。 近年、開発メンバーが増え、組織が大きくなる中で、顧客の声がエンジニアに届きづらくなってきました。 しかし、 私たちは創業当初から「顧客志向」を徹底して大切にし 開発組織としては 「顧客をカスタマーサクセスに導く圧倒的に使いやすい SaaS を創り提供する」 というミッションを掲げ、「顧客志向」での開発に取り組み続けてきました。 結果としてプロダクトが顧客に支持され、 国内 SaaS 市場でARR No.1 を達成することもできました。 今後も顧客に選ばれ続けるプロダクト開発をするため、 私たち開発組織は「顧客志向」を徹底し大切にしていく事が重要 と考えています。 今回は「顧客志向の SaaS 開発組織」であり続けるために 私たちがどんな取り組みを行っているのか その一端を、技術広報からご紹介します。 はじめに 開発組織の「顧客志向」を強化する取り組み 管理職全員でのワークショップ 開発組織全メンバー向けワークショップ 顧客理解を深めるための情報を集約 顧客志向表彰 「顧客志向」を日々実践し、これからも徹底 開発組織の「顧客志向」を強化する取り組み 昨年、当社における「顧客志向」の浸透や日々の実践について 社内調査したところ、組織や人それぞれで大きくバラつきがあることが明らかになりました。 このような現状を管理職たちで議論した結果、 開発組織全体で「顧客志向」の重要性を再認識する 必要があるという事で一致しました。 そのため、まずは管理職から「顧客志向」の重視性を再認識する目的でワークショップを開催することになりました。 管理職全員でのワークショップ このワークショップでは、ゴールを 「顧客志向」の定義と実践方針を 言語化 し、共通認識化すること に設定しました。議論は以下のステップで進めました。 「顧客志向」の重要性の再認識:各自が考える「顧客志向」の重要性と、業務への影響をチーム内で共有・議論し、共通見解をまとめました。 顧客志向の定義策定:顧客志向の浸透にあたり、「顧客志向」をどのように定義するか、共通見解をまとめました。 具体的アクションの検討・実践方針の策定:顧客志向を強化するために、日々の業務や組織全体で実施すべき具体的なアクションと実践方針を議論し、共通見解をまとめました。 ワークショップを通じて管理職が考える顧客志向の定義と実践方針は以下に決まりました。 「顧客志向」の定義 ・顧客のニーズや課題を深く理解し、価値のあるソリューションを提供する ・また、変化するニーズやフィードバックに対して迅速対応と継続改善に取り組むこと 組織全体の実践方針 開発本部内の全社員が顧客がいる事を常に意識し ・顧客理解を深める   ex)利用者と同じ体験をする、一次情報(VoC等)に触れる ・顧客への提供価値を自分の言葉で説明できる このワークショップを通じて管理職たちの中で「顧客志向」の重要性を再認識することができ、 また、浸透や実践を強化していこうという共通認識を持つことができました。 また、組織全体で「顧客志向」をさらに浸透させることで一致し、全メンバーを対象にしたワークショップも開催することになりました。 社内にも議論の内容を共有しました 開発組織全メンバー向けワークショップ メンバー向けのワークショップの目的も 「顧客志向」の重要性に気付きを得る こととにしました。 加えて 担当プロダクトの顧客理解と提供価値を自分たちの言葉で説明できるようになる ことも目指して実施しました。 ワークショップの流れは以下 顧客志向の重要性を考えるワーク 顧客理解と製品理解を深めるディスカッション 課題解決とアクションプラン作成 最終発表 各チームは各チームはPdM、デザイナー、フロントエンド、バックエンド、インフラ、SREの混成で、各チーム4〜5名の異なる役割間でコミュニケーションが生まれるようにしました。 このワークショップを通じて、 参加メンバーたちは自分自身の言葉で議論することで、顧客ニーズを積極的に取りに行く必要性や、顧客業務の理解の重要さ、 顧客接点を持つビジネスサイドやプロダクトマネージャーとの連携強化などの気づきを得ていました。 またワークショップ後半では、顧客理解を深めるアクションのア イデア も出るようになり、市場調査やインタビュー、VoC活用、 ユーザビリティ テスト実施などの具体的な施策の議論も活発になりました。 参加後アンケートと ヒアリ ングから 参加者の9割以上 が「顧客志向」の重要性を再認識し、実践していこうと思ったとの回答を得られました。 メンバーについても「顧客志向」の重要性を再認識することができ、 また、実践を強化していこうと共通認識を持つことができました。 顧客理解を深めるための情報を集約 ワークショップを通じて課題としてあがった、 継続して顧客理解を深めるための仕組み も整えはじめました。 事業成長と組織が拡大するにつれて、顧客理解に必要な情報は分散している状況でした。 最新資料を探すことが難しく、資料の重要性も伝わりにくい状況であったほか、顧客や製品に関する基礎知識も、開発組織向けにまとまったものはありませんでした。 そこで、以下のような情報を開発組織の社内 ポータルサイト へ集約しました。 顧客・製品特性などの基礎情報 ※オンボーディング資料 商談動画 顧客満足度 調査 顧客要望の情報 などなど これにより開発メンバーが顧客理解を深められる情報にアクセスしやすくしました。 集約・公開後は「非常に役立った」「定期的にメンテナンスしていってほしい」などの声が多く寄せられるほか、ここからの情報をベースに製品の重要機能について学び合う勉強会企画も動き出しました。 顧客志向表彰 さらに、顧客志向を体現した個人の行動を称える「顧客志向表彰」も新設しました。 「顧客志向」を高める取り組みを共有し、その価値を認め合う ことで日々の挑戦を後押ししていきます。 「顧客志向」を日々実践し、これからも徹底 「顧客志向」の浸透と実践の強化を図るべく、ご紹介した直近の取り組みを通じて、 組織全体で「顧客志向」の重要性を再認識することができ、また、浸透や実践を強化していこうという共通認識を一気に高めることができました。 しかし、「顧客志向」の実践にゴールはありません。 日々業務の中で意識し、行動し続けることが重要と当社開発組織では考えています。 「顧客志向の SaaS 開発組織」としてあり続けるため 「顧客志向」を徹底して大切にし実践し続けていくため そのための仕組み作りと支援を、技術広報も取り組んでいこうと考えています。
アバター
インフラ開発部でテッ クリード を務めております上畑です。 みなさんはAnsibleコードを修正した後に そのAnsibleコードを本番環境へ適用する際、 ドキドキ していませんでしょうか? 前回、 Ansibleをバージョンアップする記事 を執筆し、大量のコード修正が必要になりました。 この記事では、 ラク スがどのようにしてAnsibleコードを ドキドキ せずに本番に適用しているか、その仕組みを紹介します。 目次 目次 1. はじめに 2. DockerによるAnsible自動実行CIシステム 3. その他、CI環境の工夫 3-1. 本番環境Dockerイメージの最新化 3-2. CI実行時のエラー調査 3-3. CI/CDの並列実行を実現するコード化 3-4. 定期Dockerイメージの構築 4. 最後に 1. はじめに 一般的に、Ansibleコードを修正してマージする際には、以下のような事前チェックを行うことが多いのではないでしょうか? Lint(構文)チェック 複数人によるコードレビュー 本番環境へのAnsible Dry-Run実行 ステージングや開発環境でのAnsible実行 特にAnsibleコードの修正で厄介なのは、 AnsibleのDry-Runでは確認できず、実際に実行しなければ挙動が確認できないコードがある という点ではないでしょうか。(例えばcronモジュールなど) そのため、多くの方が以下のような対策を取っているかもしれません。 本番環境の複製やステージング環境でAnsibleを実行し、問題を検証する しかし、これには次のような課題があります。 修正を繰り返すたびに実行が必要 環境準備が大変 作業後の環境復元でミスが発生する可能性 2. DockerによるAnsible自動実行CIシステム ラク スでは上記の対策に加え、次の方法を採用しています。 本番環境に近いDockerイメージを用意し、そのDockerイメージ対してAnsibleを実行する CI環境を構築しました。 これにより、Dry-Runではなく実行時の変更差異をAnsibleコードの修正を行いながら何度でも確認可能です。 Docker環境のため、一部実行できないコードや挙動の違いはありますが、実際にAnsibleを実行できるというのは大きなメリットです。 具体的なCI環境の処理フローは以下の通りです。 AnsibleコードをGitサーバにPush Gitサーバから親JenkinsのCI用ジョブへWebhook通知 CI用ジョブが、Lintチェックジョブを立ち上げてLintチェック(Ansible Lint、 YAML Lint)を実行 CI用ジョブが、Ansibleチェックジョブを立ち上げて、指定した本番相当のDockerイメージを起動し、Ansibleを実行 本番環境に対してAnsibleのDry-Runを実行 3. その他、CI環境の工夫 3-1. 本番環境Dockerイメージの最新化 上記CIを完了後にmainブランチへマージして本番環境へAnsibleの適用を行いますが、 その際、本番相当のDockerイメージへもAnsibleを適用してDockerイメージの更新を行います。この作業も自動化されています。 3-2. CI実行時のエラー調査 Ansibleを実行した際にエラーが発生して調査を行いたいことがありますが、CI環境では実行後にDockerコンテナを停止する為、エラーになった状態を維持することができません。 その為、CI実行後にDockerコンテナを停止しないオプションを用意し、Dockerコンテナにアクセスして調査できるようにしています。 3-3. CI/CDの並列実行を実現するコード化 弊社では、配配メールのサービスだけでも10種類以上のサーバがあり、1つのAnsible リポジトリ でこれらの環境を維持しています。 共通利用のコードに変更を行う際、すべての環境に対して手動でジョブを実行するのは非効率なため、 以下の様な YAML ファイルを用意して並列実行を自動化しています。 CDも同じ仕組みで自動デプロイを行っています。 # Docker CIジョブ ANSIBLE_DOCKER_RUN: - IMAGE: 'serverA:latest' PLAYBOOK: 'serverA.yml' stage: 'production' DOCKER_KILL: false - IMAGE: 'serverB:latest' PLAYBOOK: 'serverB.yml' stage: 'production' DOCKER_KILL: false # 本番Dry-Runジョブ ANSIBLE_HONBAN_DRY_RUN - PLAYBOOK: 'serverA.yml' stage: 'production' - PLAYBOOK: 'serverB.yml' stage: 'production' 3-4. 定期Dockerイメージの構築 Ansibleコードは当然ながら新規から構築を行っても同一の環境である必要があります。 よくあるコード修正の失敗として既存環境に対して適用することはできるが、新規に構築した環境では実行できないコードを作成してしまうことがあります。 例えば、以下のように実行順が逆になっている場合など # 新規に追加したコード - name: Install Package ansible.builtin.dnf: name: "A" enablerepo: "epel" # 修正前からあるコード - name: Enable Epel Repo ansible.buitin.file: src: "epel.repo" dest: "/etc/yum.repos.d/epel.repo" owner: "root" group: "root" これは既存の環境に適用した際はepel.repoが存在する為、エラーになることはありませんが新規に構築した際にはエラーになります。 実際にこんな単純なミスはないでしょうが、例えばrole単位に分かれていてplaybook内でrole名の指定順が逆であったため発生するなど 以外と発生するケースはあります。 これは上記DockerCIでも発見できない為、定期的(毎日)素のOSのイメージからAnsibleを実行して新規から構築することが可能か確認を行っています。 4. 最後に ラク スではAnsibleによるサーバの構成管理の環境を維持する為、その安全性を高める継続的な改善を行っています。 これ以外にもbehaveやmoleculeによる振る舞いテストの追加など強化を行っており、継続的改善を行っていきます。 以上、ありがとうございました!
アバター
インフラ開発部でテッ クリード をしております上畑です。 ラク スで利用しているAnsibleコードについて、Ansibleのバージョンアップを行った内容を記事にしました。 この記事が同じような境遇のどなたかの助力になれば幸いです。 1. 背景 2. Ansibleバージョンアップ 2-1. AnsibleとPythonの関係調査 2-2. 各OSの標準Pythonバージョン一覧調査 2-3. Porting Guideによる仕様変更の確認 2-4. バージョンアップ戦略 2-5. Ansibleコード修正内容 [修正対応内容] ansible-2.9.27 to ansible-8.7.0 ansible-8.7.0 to ansible-9.12.0 3. コード修正にはAnsible-Lintの自動修正(autofix)機能を使う 3-1. 実行方法 オプションの使い方 ルール一覧 4. Ansible-Lintバージョンアップ 4-1. Ansible-Lintバージョン推移 4-2. Ansible-Lint各バージョンへの対応 v4.7.0 to v5.4.0 var_naming unnamed-task v5.4.0 to v6.14.0 missing document start "---" (document-start) missing starting space in comment (comments) too few spaces before comment (yaml[comments]) deprecated-module name[casing] ※autofix利用可能 fqcn[action-core]※autofix利用可能 fqcn[action] no-changed-when: Commands should not change things if nothing needs doing. yaml[truthy]: Truthy value should be one of v6.14.0 to v6.22.2 var-naming[no-role-prefix] risky-shell-pipe 5. まとめ 1. 背景 2019 - 2020年に私が作った共通Ansibleテンプレートはansible-2.9.x(旧型Ansible)のコード体系をベースとして構築しており、現在も各サービス環境の構築や設定変更等業務で利用しています。 (記事) ラクスサービスを管理するAnsibleコードの共通テンプレートを作った話 - RAKUS Developers Blog | ラクス エンジニアブログ また、CI/CD環境としてAnsibleの構文チェック(Lint)やAnsible実行環境をJenkinsと連携して整備しており、業務にがっつり組み込まれていることからAnsibleのバージョンアップに伴うサービス影響を鑑みて見送って(半ば放置)しておりました。 しかし、定期的なJenkins(Dockerコンテナ)のバージョンアップを進めていくにあたって、Jenkinsイメージ内のOS更新に伴う Python サポートバージョン変更が上記環境の維持に影響が出たことが今回Ansibleバージョンアップを決心した背景となります。 2. Ansibleバージョンアップ 2-1. Ansibleと Python の関係調査 Ansibleはご承知の通り2.9から2.10に伴いansibleとansible-core(or ansible-base)に分かれましたので現在(2024年11月18日)までの状況をまとめました。 下記の表にもある通り、Ansible実行環境及び実行先の Python のサポートバージョンの範囲がある為、これに伴う影響を確認していきます。 出典: ansible-coreサポートマトリックス Ansible Version base/core Version EOL Ansible実行環境 Ansible実行先 ansible-2.9.x - EOL(2022/05/23) 2.7, 3.5-3.8 2.6-2.7 , 3.5-3.8 ansible-2.10.x ansible-base 2.10.x EOL(2022/05/23) 2.7, 3.5-3.9 2.6-2.7 , 3.5-3.9 ansible-3.x ansible-base 2.10.x EOL(2022/05/23) 2.7, 3.5-3.9 2.6-2.7 , 3.5-3.9 ansible-4.x ansible-core 2.11.x EOL(2022/11/07) 2.7, 3.5-3.9 2.6-2.7 , 3.5-3.9 ansible-5.x ansible-core 2.12.x EOL(2023/05/22) 3.8-3.10 2.6-2.7 , 3.5-3.10 ansible-6.x ansible-core 2.13.x EOL(2023/11/06) 3.8-3.10 2.6-2.7 , 3.5-3.10 ansible-7.x ansible-core 2.14.x EOL(2024/05/20) 3.9-3.11 2.7 , 3.5-3.11 ansible-8.x ansible-core 2.15.x EOL(2024/11/01) 3.9-3.11 2.7 , 3.5-3.11 ansible-9.x ansible-core 2.16.x 2025/05/01 3.10-3.12 2.7 , 3.6 -3.12 ansible-10.x ansible-core 2.17.x 2025/11/01 3.10-3.12 3.7-3.12 ansible-11.x ansible-core 2.18.x 2026/05/01 3.11-3.13 3.8-3.13 2-2. 各OSの標準 Python バージョン一覧調査 いくつか抜粋しますが、各OS標準の python のバージョンを確認しました。 上記の表ではansible-9.xからansible-10.xへ移行する際に、Ansible実行先として python -2.xのサポートを終了しています。その為、 RHEL / CentOS の7系への影響があります。 また、 Python -3.6のサポートも終了している為、ここではAlmaLinux/RockyLinux/ RHEL の8系の影響にも考慮が必要となることが考えられます。 OS OS Version Python Version RHEL / CentOS 7 2.7 AlmaLinux/RockyLinux/ RHEL 8 3.6 AlmaLinux/RockyLinux/ RHEL 9 3.9 Ubuntu 20.04 3.8 Ubuntu 22.04 3.10 Ubuntu 24.04 3.12 ラク スでも社内にあるいくつかの検証用の環境の一部がCentOS7の環境があり、現在リプレイス作業中でした。 その為、影響解決策として以下のいずれかの対応を検討しました。 python -2.7から python -3.xへの変更 OSのリプレイス完了まで待つ 並行で作業を行う為、まずは CentOS7環境のリプレイスが完了するまではansible-10.xへの変更は行わずに、ansible-9.12.0まで対応していくこと としました。 2-3. Porting Guideによる仕様変更の確認 Ansible公式サイトにはPorting Guide(移植ガイド)がありますので、利用しているAnsibleのモジュールにおいて仕様変更等がないか各バージョン毎に仕様変更への影響確認を行います。 その際に各モジュール単位で確認を行いますが、 ラク スのAnsibleコードではAnsible標準の機能による実装が中心となっているため、community.general. , ansible.builtin. などに限定することができ、比較的確認箇所が少なく済みました。 公式 Ansible Porting Guide 2-4. バージョンアップ戦略 一番留意が必要なのは、 「サービスへ影響をしないこと」 が重要である為、ansibleのバージョンアップは段階的に実施していきます。 以下ansible アップデート順になります。 From Version To Version base/core Version target Python Verison 2.9.x 2.9.27(2.9最終バージョン) --- 2.6-2.7, 3.5-3.8 2.9.27 2.10.7 ansible-base-2.10.17 2.6-2.7, 3.5-3.9 2.10.17 3.4 ansible-base-2.10.17 2.6-2.7, 3.5-3.9 3.4 8.7.0 ansible-core 2.15.13 2.7, 3.5-3.11 8.7.0 9.12.0 ansible-core 2.16.13 2.7, 3.6-3.12 一旦ココまで ---- ---- ---- 9.12.0 10.6.0 ansible-core 2.17.x 3.7-3.12 2-5. Ansibleコード修正内容 以下、各Ansibleバージョン間において対応した内容を記載しておきます。 弊社環境では以下2種類の修正のみで、ansible-9.12.0までの修正において、実行時の動作は問題ありませんでした。 [修正対応内容] ansible-2.9.27 to ansible-8.7.0 未定義の変数の使用について、厳格化されました。実際には[]内は文字列の予定でしたが、変数として評価されていた為対応しました。 The task includes an option with an undefined variable. --- - name: "{{ package_name }}" | default([httpd]) + name: "{{ package_name }}" | default(['httpd']) ansible-8.7.0 to ansible-9.12.0 Ansible 2.12(ansible 5.X)からDeprecatedとなっていたinclude:, import:の表記は[Ansible 9系から廃止]]( https://docs.ansible.com/ansible/latest/porting_guides/porting_guide_9.html#id65 )されました。 Removed include which has been deprecated in Ansible 2.12. Use include_tasks or import_tasks instead. --- - - include: setup.yml + - include_tasks: setup.yml 3. コード修正にはAnsible-Lintの自動修正(autofix)機能を使う ansible-lintにはv6.15.0から autofix機能が実装 されました。 この自動修正機能を利用することでAnsibleコードの修正の9割に対応することができましたので紹介します。 3-1. 実行方法 自動修正機能付きで実行するにはansible-lint実行時に--fixを付与するだけです。 渡したplaybookに記載されているroleに対して、引っかかったルールに対して修正が行われます。 ※このオプションは、v6.20.0から追加されておりますので利用時はこれ以降のバージョンを使用してください。 ansible-lint [playbook].yml --fix 実際に修正が行われるとModified * files.と修正されたファイル数が表示されます。 diff機能はない為、git等で差分を確認しましょう。 # ansible-lint ansible-lint.yml --fix --- INFO Identified /etc/ansible as project root due .git directory. INFO Set ANSIBLE_LIBRARY=/etc/ansible/.cache/ansible-compat/0cb87f/modules:/usr/share/ansible/plugins/modules INFO Set ANSIBLE_ROLES_PATH=/etc/ansible/.cache/ansible-compat/0cb87f/roles:roles:/etc/ansible/roles INFO Executing syntax check on playbook ansible-lint.yml (0.77s) Modified 6 files. Passed: 0 failure(s), 0 warning(s) on 39 files. Last profile that met the validation criteria was 'production'. オプションの使い方 --fix ... --fix=allと同等、自動修正できる全てのルールを修正 --fix=,--fix name, fqcn , yaml ... 指定したルールのみ修正 ルール一覧 全てのルールに対して自動修正が行われるわけではなく、以下のルール一覧に記載されているものに対して修正が行われます。 対応しているautofixルール一覧 ざっと紹介すると以下の修正が可能です。 * command-instead-of-shell ... shellをcommandに変換 * deprecated-local-action ... local_actionの代わりに delegate _toを使う * fqcn ... モジュール名を fqcn 形式に変換(ansible.builtin.など一部のみ) * jinja ... jinja表記のformat修正 * key-order ... blockにwhenをつける場合の記載修正 * name ... 先頭が小文字から始まっている場合、大文字に変換 * no-free-form ... one-linerの記載を複数行記載形式に修正 * no-jinja-when ... when文のjinja記載を修正 * no-log-password ... no-logの記載を修正 * partial-become ... becomeやbecome_userの記載を修正 * yaml ... yaml 表記を修正 4. Ansible-Lintバージョンアップ 続いてAnsible-Lint環境についてもアップデートを検討しました。 4-1. Ansible-Lintバージョン推移 現行のAnsible-Lintはv4.3.7を利用しておりましたので、現時点での最新バージョンであるv24.10.0まで段階的にアップデートをおこない影響を確認しながら修正を実施しました。 以下バージョン推移 v4.3.7 > v5.4.0 > v6.14.0 > v6.22.2 > v24.10.0 4-2. Ansible-Lint各バージョンへの対応 ここからはAnsible-Lintのバージョンアップにおいて、各バージョン間における実際に修正が必要だった内容を記載しておきます。 v4.7.0 to v5.4.0 var_naming 変数名として使用できる文字の制限 影響が大きい為、一旦修正を行わず例外追加で対応(大文字を許可) .ansible-lint --- var_naming_pattern: "^[A-Za-z_][A-Za-z0-9_]*$" # 大文字を許可 unnamed-task name定義のないタスクの禁止 - import_tasks: install.yml - import_tasks: setup.yml # 以下に修正 - name: Install import_tasks: install.yml - name: Setup import_tasks: setup.yml v5.4.0 to v6.14.0 missing document start "---" (document-start) yaml ファイルの先頭に---を付与 --- missing starting space in comment (comments) 先頭コメントの後にコメント文の前にスペースが必要 #-name # 以下に修正 # - name: too few spaces before comment ( yaml [comments]) コメントの前のスペースは2つ以上。またはコメントの開始後のスペースがありません command: test #noqa 503 command: test # noqa 503 deprecated-module include:, import:の禁止 - include: # 以下に修正 - name: ... include_tasks: - import: # 以下に修正 - name: ... import_tasks: name[casing] ※autofix利用可能 nameの先頭文字は大文字であること - name: test # 以下に修正 - name: Test fqcn [action-core]※autofix利用可能 module名をansible.builtin.など新しい表記に対応が必要 - name: Test command: echo 'test' # 以下に修正 - name: Test ansible.builtin.command: echo 'est' fqcn [action] module名をansible. posix .やcommunity.general.など新しい表記に対応が必要 - name: Test sysctl: name: ... - name: Timezone timezone: name: ... # 以下に修正 - name: Test ansible.posix.sysctl: name: ... - name: Timezone community.general.timezone: name: ... ただし全ての修正が終わるまでは古いansible-lintでciを動かす為、一旦対象外にしておく .ansible-lint --- skip_list: - fqcn[action] no-changed-when: Commands should not change things if nothing needs doing. command module使用時にはchanged_whenの定義が必要 # コマンド実行結果がrc: 0の場合changedになる ... ansible.builtin.command: echo 'test' register: result changed_when: result.rc == 0 # コマンド実行結果に関わらずchangedにはしない ansible.builtin.command: echo 'test' register: result changed_when: false # コマンド実行結果に関わらずchangedにする ansible.builtin.command: echo 'test' register: result changed_when: true yaml [truthy]: Truthy value should be one of yaml 構文においてboolianの値はtrue or falseである必要がある - name: Test ansible.builtin.command: echo 'test' register: result changed_when: no failed_when: True # 以下に修正 - name: Test ansible.builtin.command: echo 'test' register: result changed_when: false failed_when: true また、上記対応においては修正量が多い為、role単位での修正を行う対応として、 yaml -lintバージョンを1.26.3から1.35.1に更新しignore機能を利用して修正対象を絞り込んで順次対応を実施しています。 .yamllint --- rules: truthy: ignore: - "roles/roleA/*/*.yml" # ロール修正時に削除して対応 - "roles/roleA/*/*.yaml" # ロール修正時に削除して対応 - "roles/roleB/*/*.yml" # ロール修正時に削除して対応 - "roles/roleB//*/*.yaml" # ロール修正時に削除して対応 v6.14.0 to v6.22.2 var-naming[no-role-prefix] ロール内の変数名は role_name_ プレフィックス として使用する必要があります。 プレフィックス の前では下線が使用できます。 Ansibleドキュメントを確認しても、このルールが確認できませんでした。変更に伴う修正影響が大きい為、一旦修正対象外としました。( https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#playbooks-variables ) roles/hoge/vars/main.yml --- version: '0.1' # 以下に修正 hoge_version: '0.1' 今回はこのルールを全体的に対象外にする .ansible-lint --- skip_list: - var-naming[no-role-prefix] risky-shell-pipe shell内でpipeを使用する場合は、pipefailオプションを設定する必要がある - name: Test Shell shell: echo 'test' | tail # 以下に修正 shell: set -o pipefail && echo 'test' | tail 5. まとめ Ansibleバージョンアップ時にはAnsibleのバージョンによって Python のサポートバージョンが変わるので注意。Ansible実行環境、Ansible実行先の環境の Python のバージョンを確認すること Ansibleコードの修正にはAnsible-Lintの自動修正機能が使える。ただしv6.20.0以上を使うこと Ansibleのバージョンアップは段階的に行う Ansible-Lintも段階的にバージョンアップを行い修正範囲を極小化して段階的に行う 実際には、これ以外に弊社にはAnsibleを本番環境に適用する前に実際に実行して、安全性を確認する環境が存在しますが詳細は別記事で紹介しようと思います。 以上長くなりましたが、Ansibleバージョンアップ作業のまとめになります。 ありがとうございました。
アバター
はじめに 配配メール開発チームの id:takaram です。 2024年12月22日に、東京・蒲田で PHP Conference Japan 2024 が開催されました。 ラクス はブロンズスポンサーとして協賛させていただいたのに加え、エンジニア3名の トーク を公募で採択いただき、登壇してきました。 今回は ラクス からの参加者によるレポートを紹介させていただきます! はじめに 参加レポート PHPの今とこれから2024 PHP RMは何をする?コア開発者と兼任するメリット/裏話 終了の危機にあった15年続くWebサービスを全力で存続させる〜Twilog・Togetter統合の舞台裏〜 見えないメモリを観測する: PHP 8.4 `pg_result_memory_size()` とSQL結果のメモリ管理 20年続くレガシープロダクトに10年携わったエンジニアが思う、システム長期運用のカギ PHPerのための計算量入門 情報漏洩させないための設計 PHPConferenceへの参加を後押しするためにしていること どうして手を動かすよりもチーム内のコードレビューを優先するべきなのか ラクスからの登壇セッションのご紹介 PHP開発者が挑むDKIM導入:Googleガイドライン対応の実例と学び Rustで作るPHP拡張モジュール:PSR-7ライブラリ編 20年もののレガシープロダクトに0からPHPStanを入れるまで まとめ 参加レポート PHP の今とこれから2024 report by id:hirobex fortee.jp 廣川さんによる、定例の PHP の動向についての トーク です! PHP8.4は新機能も多く、非常に盛り沢山な内容になっていました! プロパティフックや非対称プロパティ可視性などは、使ってみたいと思っているPHPerの人も多いのではないでしょうか? PHP RMは何をする?コア開発者と兼任するメリット/裏話 report by id:hirobex fortee.jp PHP コア開発者であり、かつリリースマネージャーでもあるSaki Takamachiさんの トーク です。 リリースマネージャーが集まったSlackがあるという話や、リリースマネージャーが PHP リリースのためにどんな作業をするのかと行った、非常に貴重な話を聞くことができました! 世界的な OSS がどのように開発されているのか、その一部を見ることができます! 終了の危機にあった15年続く Webサービス を全力で存続させる〜 Twilog ・Togetter統合の舞台裏〜 report by id:hirobex speakerdeck.com イーロン・マスク に買収されたXの API 廃止に伴う、 Twilog とTogetter統合についての トーク です。 大規模なDBを、どのようにして AWS に移行したのか、そのお話を聞くことができました! SQLite と MySQL が構成図に出てきたときは、どうなることかと思いました。 見えないメモリを観測する: PHP 8.4 `pg_result_memory_size()` と SQL 結果のメモリ管理 report by id:takaram speakerdeck.com 武田憲太郎 ( @KentarouTakeda ) さんによるセッションです。武田さんは、 PHP 8.4で導入された pg_result_memory_size() の提案・実装を行った方で、その関数にも関連する「 SQL 実行時のメモリ使用量」についてお話しされました。 普段何気なく使っているPDOの prepare、execute、fetchAll の裏で何が起きているのか、 SQL の結果データがどこでどのように保持、管理されているのかを知ることができました。それと同時に、自分のコードの実装は大丈夫……?と不安になったので、改めて見直してみようと思いました! 20年続くレガシープロダクトに10年携わったエンジニアが思う、システム長期運用のカギ report by id:uemura_rks speakerdeck.com 20年続くプロダクトをモダンな環境に移行してきた取り組みについての トーク でした。 私が担当するサービスは同じような境遇でいまだにレガシーから抜け出せていない状況にあるため、 非常にためになる内容でした。 システムだけでなく開発体制の改善(開発体制に再現性を持たせる)について述べられていたことも印象的でした。 モダンへの移行はタイミングが遅くなれば遅くなるほどやることが増えるので、出来るだけ早めに実施したいものです。 PHPerのための計算量入門 report by id:hirobex speakerdeck.com アルゴリズム において非常に大事な要素になる計算量について、非常にわかりやすく解説していただいた トーク です! データ数が大きくなればなるほど計算量の考えは大事になってくるので、とても参考になりました。 また、最後にチューニングにも限界があるから、仕様変更も視野に入れようねという話も入っていたのが、非常に実務的だと思いました。 情報漏洩させないための設計 report by id:uemura_rks speakerdeck.com ビジネスロジック の実装ミスなどによる情報漏洩を防ぐための設計手法についての トーク でした。 コンテキストごとにEntityを用意することで必要最小限の情報を扱えるようにするといった工夫 分けたEntityを取り違えないための工夫 が参考になりました。 情報漏洩を題材にした トーク でしたが、紹介されていた手法は情報漏洩に限らず普段の開発に活かせる考え方でした。 deptrac という依存関係をチェックできるツールが面白そうなので使ってみたいです。 PHPConferenceへの参加を後押しするためにしていること report by id:takaram fortee.jp 下岡葉子 ( @yoko_94b ) さんによる、 PHPカンファレンス のような技術イベントに参加したことがない人の背中を押すにはどうするか、というお話でした。 行かない理由として「難しそう」「すごい人が行くところでは?」のような気後れや「休みの日の朝から……?」のような声に対し、「若い人もたくさん来てるよ」「知らない単語を知るいい機会だよ」「午後からでも大丈夫」といった声掛けを社内で行って後押しされているそうです。 トーク 中「今回初参加の方いますか?🙋」と質問されたところ、結構たくさんいらっしゃったのが印象的でした。 弊社開発部は東京と大阪に分かれていて、大阪からだと距離のハードルという問題もあったのですが、2024年は全国各地で PHP 系カンファレンスが開催されたおかげでハードルがぐっと下がりました。 2025年も続くようなので、私も参加を後押ししていきたいです! 月刊 PHPカンファレンス 2025! #phpcon #track1 pic.twitter.com/xJYvrkZzs5 — takaram (@takaram71) 2024年12月22日 どうして手を動かすよりもチーム内のコードレビューを優先するべきなのか report by id:takaram speakerdeck.com おかしょい ( @okashoi ) さんによるLTです。 コードレビューをついつい後回しにしてしまう人(私含め)には耳が痛い話ではありました。私は「レビューを優先すべき」という話は聞いていても、理由まで具体的に理解できていなかったのですが、今回そこを知ることができました。 自分のタスクを横に置いてでもレビューを優先していきましょう!💪 ラクス からの登壇セッションのご紹介 PHP 開発者が挑む DKIM 導入: Google ガイドライン 対応の実例と学び report by id:uemura_rks speakerdeck.com トラック4の一発目として、楽楽販売で Google ガイドライン に対応した実録をお話させていただきました。 カンファレンス初登壇でしたが落ち着いて話せたので良かったです。 自分が設計寄りの人間なこともあり技術の話があまり出来なかったため、次回はもう少し技術ネタを持って参加してみたいです。 Rustで作る PHP 拡張モジュール:PSR-7ライブラリ編 report by id:takaram www.docswell.com 通常 C言語 で作る PHP 拡張モジュールを、 ext-php-rs というライブラリを使ってRustで実装してみる、という話をさせていただきました。 Rust自体本格的に触ったことがない状態から始めたため、かなり苦労したのですが、初心者だからこそできる話もあったかなと思います。今回の登壇を通じて、Rustにも PHP にも詳しくなれたのではと思います! 20年もののレガシープロダクトに0からPHPStanを入れるまで report by id:hirobex speakerdeck.com 登壇しました! タイトル通り、レガシープロダクトにPHPStanを入れた話です! PHPStanのオプションや、bootstrapFilesを駆使して、PHPStanを導入した話をしました! ありがたいことに、多くの人が来てくださりました。 発表した会場が、大学の講義室みたいで、人気教授になった気分でした。 まとめ 2024年は全国各地で PHPカンファレンス が行われましたが、歴史の長い PHP Conference Japan(世界最古だそうです!)は今年の締めくくりにふさわしい盛り上がりだったように思います。 2025年は6月28日開催と、実はもう半年後に迫ってきています。来年も楽しみですね!
アバター
はじめに こんにちは、 @rs_tukki です。 この記事は、 ラクス Advent Calendar 2024 の25日目の記事です。 今回は、開発中に見つけた重いクエリを改善するための記録と、改善のために使用した見慣れない構文の紹介をしようと思います。 はじめに 開発中の出来事 パフォーマンスチューニング UNNESTとは? チューニング結果 まとめ 開発中の出来事 ある日、私はWebアプリに新しい API を追加するための実装をしていました。 既存のテーブルからデータを取得するための新しいクエリを作成して、そのクエリを呼び出した結果を返すだけの単純な API です。 実装はつつがなく完了し、その後のテストでも特に問題は見られませんでした。 ただ、新規にクエリを作ったため、最後に JMeter を使ったパフォーマンス検証を行うことに。 【図解】はじめてでもわかるJMeterの使い方 - RAKUS Developers Blog | ラクス エンジニアブログ DBにデータを大量に入れて、顧客の利用状況を元にアクセス数の 閾値 を検討し、 その数だけリク エス トを投げる操作を3回繰り返し、それぞれの平均値を取ります。 そして結果がこちら。 平均値 1回目 1,881ms 2回目 1,886ms 3回目 1,929ms 1,900ms = 1.9秒 。 ...流石にちょっと時間がかかりすぎですね。 パフォーマンスチューニング 負荷検証は通常の運用で想定した分をはるかに超えたデータ量と呼び出し回数で行ってはいましたが、 とはいえ一度の API の実行で2秒弱もかかることがあるのではとてもリリースできません。 そのため、 ボトルネック となっている SQL を特定した上でそのチューニングを行うことにします。 実装したコードから呼び出しているクエリを1つずつ コメントアウト してはパフォーマンス検証、という操作を繰り返し、極端に処理が軽くなったタイミングがあれば、その時に コメントアウト しているクエリが ボトルネック ということになります。 そして、それを何度も繰り返している内に、とうとう原因となる一つのクエリに辿り着きました。 それがこちら。 SELECT count ( 1 ) AS count FROM ( SELECT a.column1 FROM tableA a WHERE EXISTS ( SELECT 1 FROM tableB B WHERE a.column1 = b.column1 AND a.column2 = b.column3 - 1 AND b.column4 = 0 AND b.column5 = 0 AND b.column6 IN ( SELECT column7 FROM tableC WHERE column8 = ' AAA ' OR column9 = ' AAA ' OR column10 = ' AAA ' ) ) AND a.column11 = 0 AND a.column12 >= 100 LIMIT 100 ) AS " result " ; このクエリの実行が極端に重いせいで、リク エス ト全体が遅延していたというわけです。 このクエリ……何か変…… 見てもらえれば分かるとおり、このクエリでは欲しい情報を一度に取ろうとするあまり、 サブクエリにサブクエリを重ねて極端にパフォーマンスが悪いクエリになってしまっています。 そのため、サブクエリの中の最も深い部分、tableCから column7 を抽出している部分を先に実行しておき、 後から条件に加えるようにしました。 SELECT column7 FROM tableC WHERE column8 = ' AAA ' OR column9 = ' AAA ' OR column10 = ' AAA ' ; -- 「test1」と「test2」が抽出される --- SELECT count ( 1 ) AS count FROM ( SELECT a.column1 FROM tableA a WHERE EXISTS ( SELECT 1 FROM tableB B WHERE a.column1 = b.column1 AND a.column2 = b.column3 - 1 AND b.column4 = 0 AND b.column5 = 0 AND EXISTS ( SELECT column7 FROM UNNEST(ARRAY[ ' test1 ' , ' test2 ' ]) AS C(column7) WHERE b.column6 = C.column7) ) AND a.column11 = 0 AND a.column12 >= 100 LIMIT 100 ) AS " result " ; UNNESTとは? さて、改善後のクエリに UNNEST という見慣れない構文があります。 これは PostgreSQL で使用できる構文で、配列を引数として渡すと、その各要素を単一列のテーブルとして出力する、という構文になります。 www.postgresql.jp つまり、 SELECT column7 FROM UNNEST(ARRAY['test1','test2']) AS C(column7) を実行すると、 column7 test1 test2 という列を含む C テーブルが結果として得られます。 この結果はそのまま EXISTS に引き渡すことが出来るため、事前に抽出しておいた複数件の値を1つずつIN句に入れるよりも若干パフォーマンスが改善するというわけです。 チューニング結果 さて、改善後のクエリを、改善前と同条件のもとで負荷検証にかけてみたところ... 平均値_改善前 平均値_改善後 1回目 1,881ms 507ms 2回目 1,886ms 518ms 3回目 1,929ms 516ms API リク エス トの実行時間が1/3にまで改善されました! クエリを分割するだけで速度が3倍近くにもなるので、やはりチューニングは大事だと気付かされた一件でした... まとめ 今回は、開発時に発見した ボトルネック とそれを改修するまでの記録を解説しました。 テストのタイミングでは問題なく見えても、大量のデータを投入し大量のリク エス トを投げるとパフォーマンスが落ちることは往々にしてありますので、しっかり検証は行うようにしましょう。 ここまで読んでいただき、ありがとうございました!
アバター
目次 リファクタリングに着手するまでの経緯 苦労した点や学び 仕様を理解する 既存コードを読み解く ①目的や仮定を持たずに一気に全体を追ってしまう ②コメントに惑わされてしまう ③効果的な作業メモを取らない 適切な命名 ①コードリーディング時 ②実装時 まとめ リファクタリング に着手するまでの経緯 初めまして!楽楽販売新卒エンジニアのudonrmです。 本記事では、配属後初めての業務として経験した既存コードの リファクタリング について過程と学びを伝えていきます。 弊社では、3ヶ月間の新卒合同研修を経た後にそれぞれの部署に配属され、そこで配属後研修が行われます。私の所属された楽楽販売では、配属後に検証環境を使用した実践的な開発演習や商材理解を中心に2ヶ月半ほどの研修がありました。9月中旬ごろには配属後研修が終了し、そこから実業務を開始しました。 初めての業務として取り組んだタスクが、コード品質の改善を目的とした既存コードの リファクタリング です。 この リファクタリング は、既存のバリデーション アーキテクチャ の改修やクラスの責務の見直し、可読性の向上がメインテーマになります。 バリデーション アーキテクチャ 変更の経緯については、弊社過去ブログをご参照ください。 tech-blog.rakus.co.jp 苦労した点や学び ここからは、 リファクタリング に着手する際に特に苦労した点や学びのあった点を観点別に紹介していきます。 仕様を理解する まず苦労した点に、仕様理解を挙げたいと思います。 楽楽販売は自社の業務フローに合わせて表示項目や操作メニューをカスタマイズできることが特徴の一つで、長所でもあります。ただ、その分どうしても機能数が増えてしまう傾向にあり、仕様理解が難しいことが業務を進めるうえで自分の課題になっていました。 今回のタスクは、配属後研修で軽く触れた程度でほとんど知見のない機能を対象とした リファクタリング でした。そのため、当然ですが仕様の理解にも苦労がありました。 当初の考えとしては、仕様理解は リファクタリング の方針検討やレビュワーとの認識合わせを経て自然と理解していくものくらいの認識を持っていました。 実装の序盤ごろまでは仕様の理解不足による影響は特になかったのですが、時間がたつほどじわじわと仕様の理解不足による影響を感じ始めます。 詳細は省きますが、画面上で使われているとある単語の意味を若干勘違いしていたり、細かい挙動の把握漏れがあったりしました。誤った前提知識を持ったままコードリーディングを進めるので、細部を読み間違えてしまったり、最悪の場合は デグレ が起きていることに気が付かなかったりしてしまいます。 結果的に、今回のタスクでは仕様理解に対して以下の学びを得ることができました。 適切な仕様理解はコードリーディングや実装の精度向上に役立つ 「ながらの仕様理解」には限界がある 進捗がストップするのを恐れずに積極的に時間を作るべき 違和感を持った仕様は問題ないはずと思いこまずに確認する これについては、実装のことで頭がいっぱいで確認を怠ってしまった経緯があります。 正しい仕様を理解しないと実装する必要のない箇所を実装してしまったり、考慮漏れが生まれたりしてしまう可能性があるので特に強く意識していきたいと感じた部分でした。 既存コードを読み解く リファクタリング 着手に当たり、既存コードのロジック把握にもかなり苦労しました。 当然ですが、簡単にスラスラと読めるものではありません。 機能理解が不十分なことや、処理の流れが脳に収まらないことなど原因はいくつもありますが、今振り返るとコードを読む際の取り組み方に問題があったと思います。 ここからは問題点を3点に絞ってふりかえってみたいと思います。 ①目的や仮定を持たずに一気に全体を追ってしまう 業務のコードは単に量が多いだけでなく、複雑な仕様が影響してどうしても複雑になってしまいます。楽楽販売の ソースコード も同様でした。 当初は、右も左もわからず目についた処理をとりあえず追ってしまっていました。上から下に流れるように読んでいくだけなので、修正箇所の特定にも時間はかかるし、何より脳が疲弊して効率がどんどん下がっていきます。ある日、そのモヤモヤを日報に書いてみたら先輩からこんなフィードバックが来ました。 後日実際に ペアプロ をしていただき、以下の学びを得ました。 命名 から役割が自明な関数に対して仮説を立てること 例えば、以下のようなメソッドがあります。ここで 命名 と返り値だけに注目すると、「ユーザーIDを受け取ってHogeArrayを返すんだな」ということが自明だと思います。 次に、 getHogeArrayByUserid() を呼び出す際は以下のようになると思います。 命名 から役割が推測できて返ってくる値も把握できている場合には、わざわざ getHogeArrayByUserid() の中身の処理まで追いに行く必要はなさそうです。 $hogeArray の正体に対して仮説を立てる→ デバッグ を使って検証の繰り返しでコードリーディングが進みます。 当初、目についたメソッドをただひたすら読みに行ってしまっていたので、このアド バイス を受けてコードリーディングが少し楽になったのを覚えています。 こうして書き出してみると当たり前のことだなとは思いますが、当時はコードリーディングがまったく進まないことに焦り、ヒントになりそうなロジックがないかを探そうとして本来読む必要がない箇所まで読んでしまっていたので悪循環になってしまっていました。 ②コメントに惑わされてしまう 命名 と返り値以外にも、コードリーディングの理解を助ける要素としてコメントがあります。 先輩との ペアプロ から得た学びを頼りに、ロジックを深追いしない代わりにコメントを意識して積極的に読むようにしてみました。 結論としては、「仕様理解、機能理解がままならない場合はコメントを読むとかえって混乱する場合がある」という学びを得られました。 実際に経験した例では、実態に即したコメントでなかったり、表現があいまいでなにを指しているのかがわからなかったりするケースがありました。 詳細は省きますが以下のようなコメントがありました。 コメントではターゲットのリストを取得という補足がされていますが、返り値の型がない(リリース当時は型宣言がサポートされていませんでした)ことやターゲットがさす内容がロジックを追わないとわからないことがコードリーディング時の負担になっていました。 実際には中身のロジックを追っていけば取得できるリストは確認できるのですが、仕様が複雑なためターゲットの実態をうまくつかめず不安を抱えたまま読み進めていきました。仕様理解や機能理解があれば、このメソッドの出力結果が使われる場面や使われ方が具体的に想像しやすくなるはずです。 実際には、このターゲットの正体は仕様をしっかり理解していれば特に苦労することなく理解できるものでした。仕様理解や機能理解に不安がある場合は、コメントで感じた違和感を放置せずにひとつひとつ確認していくことが大切だなと感じた場面でした。 ③効果的な作業メモを取らない 既存コードを読み解くうえで、作業メモの取り方に関しても課題感を感じていました。 「①目的や仮定を持たずに一気に全体を追ってしまう」の課題と類似しますが、作業メモも自分の思考の流れを一気に書き出しているだけだったので、読み返すのが辛いだけでなく、作業の進捗状況がわかりづらいアウトプットになってしまっていました。 当時の作業メモを見返してみると問題点は以下の2つが考えられます。 事実や気づきが入り混じっている メモを書いているときは問題ないが、あとから見返す際に信頼すべき情報かどうかの判断にリソースが使われてしまう フォーマットや粒度がそろっていない メモを追記していく際にどこに追記すればいいか迷う→フォーマットや粒度がさらに崩れてどんどん読みづらくなる メモを読み返す際にどこを読めばいいか探すのに時間がかかる 当時のメモをそのまま紹介することは難しいので、動物園に行くためのメモというテーマに変えて当時のメモを再現してみました。(GPT4o生成) このメモは極端な例ですが、 事実や気づきが入り混じっている 駐車場はあるけど使うかわからない。料金高めだった気がする(曖昧)。 この書き方は、書いている最中は思考が整理されて問題はないはずですが、量が増えれば増えるほど自分のメモに信頼が持てなくなってしまいます。気づきとして書いたつもりのメモを事実として誤読してしまうと、作業が直感的に進まずもはやメモを読まないほうがスムーズに進みます。これは実際に体験したので少し痛い目を見ました。 フォーマットや粒度がそろっていない 「目的」と「見たい動物」の見出しの粒度が明らかに揃っていません。適切な階層構造になっていないとメモを読み返す際の障害になってしまいます。メモを追記していく際にどこの階層に記述していくかの判断を誤ってしまうかもしれません。実際に当時のメモを読み返してみると階層構造が全体的に統一されていないため、読むのに時間がかかってしまいました。 せっかくメモを取ったのに、メモを見ないほうが進捗がいい気がするという本末転倒な状況になってしまったのでメモの取り方を見直してみました。 改善後のメモはこんな感じです。 まずは、どんなタスクをするとしても共通のフォーマットを使用するように変更しました。タスク単位でissueを+1していけばいいだけなので追記も楽ですし、読み返す機会も圧倒的に増えました。 メモには気づきベースのメモを書き、補足事項には事実ベースのメモを書くことで情報が一気に整理されて読みやすくなったのを実感できました。 このフォーマットが最適解と言い切れるわけではありませんが、無理なくメモを取る習慣を続けられているので今のところうまくいっている感じがします。 適切な 命名 命名 に関しても、コードリーディング時と実装時双方の場面で何度も頭を悩ませました。 *1 リーダブルコードの購読や配属後の開発演習などで、可読性の低い 命名 がもたらす弊害については認識していたつもりでしたが、修正方法や考え方について ソースコード ベースで学べたのは本当にいい経験だったと思います。ここではコードリーディング時と実装時にわけて適切な 命名 について考えたいと思います。 ①コードリーディング時 今回の既存実装は、汎用的な 命名 や抽象的な 命名 をしている箇所が多くあったため、直感的にコードリーディングをするのが厳しいなと感じる場面が多くありました。 以下のコードは一例です。 どの変数も 命名 が抽象的です。例えばこのメソッドの再利用性が高く、汎用的なロジックを提供している場合は具体的な文脈に依存しない抽象的な 命名 でも問題はないと思います。 ただ、用途が限定されたメソッドで呼び出し箇所が1か所しかないケースなどにおいてはコードリーディングの障害になってしまいそうです。 実体験ですが、中身にどんな情報が入っているかの把握や推測が難しくなってしまいました。 デバッグ で何とか情報を得ようとしましたが、クラス単位で 命名 が不適切な箇所が多くあったので全体の構造理解にはかなり苦労したことを覚えています。 不適切な 命名 の変数を放置したまま実装を進めても、コードリーディング時に無駄なリソースが割かれてしまうので リファクタリング 着手の最初のステップとして全体的に変数名を見直しました。 変数名見直しは主に以下の2点を意識しました。 1.地道に デバッグ して変数に格納されているデータの中身を確認する 格納されているデータの把握ができないと方針が立てられないため、 デバッグ を通じてデータの中身を確認していきます。変更しなくてもよさそうな変数や、不足した情報がある変数をリストアップして修正可能な箇所を洗い出しました。 2. 命名規則 を決めて粒度をそろえる 一例ですが、以下のような 命名規則 を決めました。 リストを扱う配列にはListを付けて $hogeList のような形にする 返り値がリストであることが自明であるにもかかわらず、変数名からそれが読み取れないケースが多くありました。こういったケースは 機械的 に修正する方針に決めました。 情報量を増やせそうな場合は増やす デバッグ した結果、用途が限定的かつ情報量を足せそうな変数に対しては情報量を増やす方針を決めました。メソッドの呼び出し階層が深いケースでも、呼び出し元までたどる必要がなくなるので以降のコードリーディングに大いに役立ちました。 ②実装時 新規で追加したメソッドやクラスの 命名 も、コードレビューのやり取りを通して何度も修正していきました。 PRの指摘などでいただいたものの中で今後も意識しておきたいと思ったものをまとめたいと思います。 変数名を変に省略しようとしない 変数名もそうですが変に省略しようとしない方が大体上手くいきますね。今は IDE が優秀なので長くても別に困りませんし。 下記は簡単な例ですがクラスの 命名 ではできるだけ汎用的な 命名 は避けたほうが無難という学びを改めて認識しました。 変に省略するよりも役割を適切に表現する 命名 にすることで可読性は上がる場合が多そうです。 対称性を意識する *2 書籍プリンシプルオブプログラミングの中で対称原理について触れられている箇所があり、以下のようなメリットが紹介されていました。 コードの形に対称性があると、予測がつきやすく、コード理解のスピードが速まります。また、視覚的にも美しいコードになるので、これもコードの理解のスピードを速めてくれます。 対称性を全く意識しない 命名 にすると、例えば以下のようなコード例になります。 命名 に対称性がなく、可読性が低いです。 例えば、 insert と deleteLast は 命名 の粒度がそろっていません。 insertAnimal はリスト全体に関する操作が想定されますが、 deleteLastAnimal は「最後の動物だけ」という限定的な操作が想定されます。このままだと、コードリーディング時に 命名 から直感的に処理を把握していくことが難しくなってしまいそうです。 次に、ロジックはそのままで 命名 が対称になるように修正されたコードの例を示します。 命名 の粒度をそろえ、 add ⇔ remove と open ⇔ close のようにごく一般的な対称性を意識しました。 命名 から役割も推測でき、「登録削除」「動物園の開閉」と観点が限定的になるのでコード理解のスピードが速くなります。 対称性は 命名 だけでなく設計全般にも役立ちそうな基本的な考え方なので、今後も意識しておきたいです。 まとめ 苦労の多かった リファクタリング ですが、チーム内外の様々な方の手厚いサポートのおかげもあっておよそ1か月半ほどで実装~受け入れが無事完了しました。 今回紹介したもの以外にもたくさんの学びがあったので、今後の業務に積極的に役立てていきたいと思います。 最後までご覧いただきありがとうございました。 *1 : O'Reilly Japan - リーダブルコード *2 : プリンシプル オブ プログラミング 3年目までに身につけたい 一生役立つ101の原理原則
アバター
この記事は ラクス Advent Calendar 2024 の14日目の記事(予定)です。 はじめに シンボリックリンク切替によるデプロイについて 今回の改善における無停止デプロイのスコープ 検証したこと 検証におけるゴール 検証観点 ①realpathキャッシュの動作検証 ②アプリケーションコードの動作検証 ③本番想定のアクセス下の動作検証 アプリケーションの改修内容 おわりに はじめに こんにちは、kasuke18 です。 楽楽販売のDevOpsチームメンバーとして、リリース周りなどインフラチームと協働する部分を主に担当しています。 私達のプロダクトでは、 シンボリックリンク 切替による無停止デプロイを導入しました。 導入にあたって私達のコンテキスト(特にサーバ構成などの アーキテクチャ )に合うか検証のうえ、必要なアプリケーション改修を実施しました。 その検証内容と改修ポイントをご紹介します。参考になれば幸いです。 シンボリックリンク 切替によるデプロイについて このデプロイフローの導入前は、 rsync によるファイルの直接差し替えで対応していました。 PHP のファイル読込は遅延読込されるため、タイミングによっては処理中のプロセスがエラーになってしまうケースがあります。 (例えば新しいクラスを含むコードをデプロイする際、その新ファイルの配置より先に利用個所のロジックが処理されてしまうと未定義としてエラーになる) そのため顧客影響を避けることから、リリース作業を夜遅くに実施するなど運用コストが高い状態が続いていました。 これを改善するため、各所で導入事例のある「 シンボリックリンク 切替によるデプロイ」を検討しました。 上記のようなデプロイフロー・具体的なデプロイの手順( mv コマンドで シンボリックリンク を上書きする)などは、導入事例を参考に、割とそのまま利用できる部分が多かった記憶があります。 https://developers.prtimes.jp/2022/05/31/prtimes-deploy-improvement/ とはいえ単純にデプロイ手順を変えるだけでよいのか、具体的にはアプリケーションの作り込みを変える必要があるのか、といった観点では私達のコンテキストに近しい事例があまり見つからず、導入するにあたって本当に問題ないのかを検証しました。 今回の改善における無停止デプロイのスコープ シンボリックリンク 切替という手法なので、サーバ上のアプリケーションコードの差し替え部分にのみ影響します。改善効果が期待できる範囲もサーバ上の PHP プロセスが担う処理に限定されます。 逆にフロントエンド↔バックエンド間のインタフェースの変更(リク エス トパラメータの増減)や PHP プロセス↔データベース間のインタフェースの変更(DBテーブル定義の変更)には対応できません。これらの改善には別の手段が必要になりますのでご留意ください。 検証したこと まず私達のプロダクトへ導入できないか検討するにあたって、他社の事例などを参考にさせていただきました。 例えば下記の導入事例などを見ると、realpathキャッシュというもの原因で問題が発生するリスクが説明されています。 https://www.klab.com/jp/blog/tech/2016/1062120304.html これだけを見ても単に導入すると、痛い目に遭う可能性が高そうです。 またOPcacheの利用有無やサーバソフトウェアなど、実行環境による違いによる挙動の変化もあり得るため、私達のコンテキストに合った検証が必要でした。 ちなみに今回検証を行った構成は以下のとおりです。 PHP 8.2 OPcache利用なし Apache + mod_ php 検証におけるゴール 今回の検証では、上記のような変更があったとしても「1つの PHP プロセス内では、デプロイ前・後のどちらかのコードのみが実行される」ということを担保することを目的に検証を進めました。 一番リスクなのは新旧コードが入り混じって実行されることで、その場合はなにかエラーが発生したとしてもまともに調査することができません。 新バージョンの反映に多少の遅延が生まれることは許容し、それよりも1つの PHP プロセス内での一貫性を優先しました。 検証観点 以下の3段階で検証を行い、新旧コードが入り混じって実行されることがないかを確認しました。 ①realpathキャッシュの動作検証 ②アプリケーションコードの動作検証 ③本番想定のアクセス下の動作検証 ①realpathキャッシュの動作検証 まずは私達のコンテキストにおけるrealpathキャッシュの効き方を把握するため、ミニマムなサンプルコードを作成しました。 sleep中に シンボリックリンク を切り替えてrealpathキャッシュの内容やロードされたクラスのファイルパスがどうなるかをするかを確認します。 詳細は長くなるので割愛しますが、下記のような関数を利用しました。 realpath_cache_get() : この関数の実行時点のrealpathキャッシュ内容を取得 ReflectionClass::getFileName : クラスが定義されているファイルを取得 // require_once("/app/symlink/SampleClass1.php"); // var_dump(realpath_cache_get()); ["/app/symlink/SampleClass1.php"]= > // キャッシュのキーはrequire_onceに指定したパス array(4) { ["key"]= > int(3616146466484062189) ["is_dir"]= > bool(false) ["realpath"]= > string(28) "/app/before/SampleClass1.php" // シンボリックリンク解決済のパスがキャッシュされる ["expires"]= > int(1733634941) } // var_dump((new ReflectionClass ("SampleClass1"))- > getFileName()); string(28) "/app/before/SampleClass1.php" // シンボリックリンク解決済のパスが取得できる ②アプリケーションコードの動作検証 ここでは実際のアプリケーションが実行されるパターンを洗い出し、新旧コードが混在することがないかを確認しました。 例えば私達のプロダクトでは、Webアクセス・ CLI 実行(バッチ)・メール受信からの起動 などのパターンがありました。 それぞれパターンについて、処理の何処かでsleepを仕込み、実行中に シンボリックリンク の切替を行い検証します。 ③本番想定のアクセス下の動作検証 アプリケーションコードを利用するという点では②と同じですが、検証したいポイントが異なります。 ②ではsleepを仕込んだ単発処理で、理論的上問題がないかを検証しました。 それに対して③では、アプリケーションの通常利用中に シンボリックリンク を継続的に切り替えて問題がないかを検証します。 イメージとしては同じバージョンの ディレクト リを2つ用意し、そのうち片方にメソッド・クラスのインタフェース変更を行い、従来の rsync によるデプロイではエラーになる状態を作ります。 その状態で シンボリックリンク の向き先を新旧切り替え続け、アプリケーション操作でエラーが発生しないかを確認します。 またアプリケーション操作は、リリース時に利用している動作確認ツールがありましたので、それを流し続けることで実現しました。 アプリケーションの改修内容 詳細は長くなるため、検証結果はまた別の機会にします。 特に①のサンプルコードも合わせて記載できるように頑張ります。 検証結果を踏まえて、アプリケーションの改修が必要な点は、 require に指定されるファイルパスが、すべて「 シンボリックリンク 解決済みのパス」となるようにすることです。 例えばComposerで生成されたautoloaderを利用している場合、autoloader経由で読み込まれるファイルは特別な考慮を必要としません。 autoload*.php では __DIR__ を基にパスを組み立てているため、 シンボリックリンク 切替の影響を受けません。( PHP の __DIR__ で取得できるパスは シンボリックリンク 解決済みのパス) 一方で、autoloader自体を読み込む際のパス指定には注意が必要です。 // OK例 require_once(__DIR__ . "/vendor/autoload.php"); // NG例 require_once("/path/to/project/vendor/autoload.php"); require_once(ini_get("user_dir") . "/vendor/autoload.php"); 上記NG例のように シンボリックリンク を含むパスになっていると、その処理タイミングで シンボリックリンク 切替が重なると、新旧入り混じった状態でアプリケーションが動いてしまう可能性があります。 特にWebアクセスにおいては、realpathキャッシュがリク エス トをまたいで共有されるケースがある(処理された Apache のプロセスが同じ場合は共有される)ため、分かりづらい形でエラーになってしまうことがあります。 おわりに 私達のプロダクトで「 シンボリックリンク 切替によるデプロイ」を導入したときの検証内容・アプリケーション改修内容をご紹介しました。 realpathキャッシュの挙動調査や Apache のプロセスとの関連など、記事のボリュームの都合で書ききれなかった部分については、今後別記事で詳しくご紹介します。
アバター
こんにちは、あるいはこんばんは。楽楽販売の開発をやっている @taclose です☆ ISUCONに参加するのはこれで2回目ですが、 今回は7位でした! ISUCON14 TOP30 微妙!とか言わないで!頑張った方ですよ!運が良かった方ですよ!(と言いたい!) 今日はそんなISUCON14がどんな感じだったのかを振り返っていこうと思います! 記事の概要・想定読者 ISUCONの準備 前回の反省からはじまる 前回の反省点 練習はISUNARABE! ISUCON当日 ISUCONの初動:初回ベンチマークまでにやる事! 初回後の次の一手:DBのINDEX見直し 各自が怪しいポイントを重点的に攻める 4時以降:サーバ構成を真剣に考える 最後のチューニング 最終結果 今回のISUCONの振り返り 良かった点 反省点 最後に 記事の概要・想定読者 想定読者 今年ISUCON出たけど、悔しい思いをした方 来年ISUCON出てみようかな?と思う方 記事の概要 ISUCON14の細かいチューニングポイントはあまり取り上げません。 Git管理や Makefile の準備やノウハウ集ではありません。 ISUCONに挑戦する準備や練習、当日の困りポイントをまとめた体験談です。 では早速、当日までの準備から振り返ってみましょうっ ISUCONの準備 前回の反省からはじまる 11月ごろ行われた作戦会議では以下の話が出ました。 前回の敗因はずばり準備不足、練習不足!以下のような社内ハンズオンを開催するぐらいの知識量はあったものの、 tech-blog.rakus.co.jp 当日は8時間という限られた時間で実践するのは至難の業でした。 前回の反省点 サーバ構成変更時のコマンド準備が不十分だった( Makefile に ベンチマーク 前のコマンドを準備するなど)。 サーバ構成変更時にネットワーク設定でアタフタして0点になりかけた。 そこで今回は事前準備とサーバ構成変更の練習に重きを置くことにしました。 練習はISUNARABE! ISUNARABE を使い、本番さながらの練習をしました。 特に課題だった AWS での3台サーバの使い方や初動の役割分担を重点的に練習しました。前回に比べると少ない練習時間でしたが、効果はありました。 ISUCON当日 ISUCONの初動:初回 ベンチマーク までにやる事! 私たちのチームでは以下の作業を分担して進めました。 configやアプリをGit管理下に置く makeコマンドで設定ファイルやアプリをデプロイ MySQL やnginxの初期設定(slow queryのログ出力など) 作業が終わったら初回 ベンチマーク を実行。 10時半時点でスコア1000点でした。 初回後の 次の一手 :DBのINDEX見直し nginxログ解析で遅い API は分かりますが、まずはDBのINDEXを整備するのが効率的。 pt-query-digestで解析し、30分~1時間で対応しました。この時点でスコアは3800点。 各自が怪しいポイントを重点的に攻める 我がチームは 全員遊撃部隊 ! nginxログやプロファイラー結果を見て処理時間が長い箇所を探る 怪しい箇所を自由に攻める 13時半でスコア8000点、15時に15000点、16時に18000点まで到達しました。 4時以降:サーバ構成を真剣に考える 3台サーバの使い方を再検討。 1台をnginx+アプリ用、もう1台をDB用に割り当て 16時半でスコア26000点。 最後のチューニング サーバ構成変更に伴い、nginxや MySQL の設定値を見直し。 17時半でスコア37000点。 最 終結 果 39957点 今回のISUCONの振り返り 良かった点 チームメイトに恵まれたこと。感謝感謝です!お疲れ様でした! 反省点 N+1問題への対処が不十分だったこと。 抜本的な解決策を思いついても時間内では躊躇してしまい、キャッシュで逃げてしまった。 場数を踏めばこういった勘所も磨かれると思うので、次回再挑戦したいと思います! 最後に これからISUCON始める人、始めたばかりの人に参考になれば幸いです! もしこれからISUCONはじめるぞ!という方は 目指せISUCON!!社内WEBパフォーマンス改善ハンズオンのすすめ - RAKUS Developers Blog | ラクス エンジニアブログ でも紹介している書籍を読まれたり、ローカルでパフォーマンスチューニングを練習してからやる事をお勧めします! ランク外で終わっても非常に学びが多く楽しいイベントなので、是非みなさんトライしてみてください!!
アバター