セーフィー株式会社のブログ - TECH PLAY

TECH PLAY

セーフィー株式会社

セーフィー株式会社 の技術ブログ

269

こんにちは。セーフィー株式会社エンジニアの伊藤です。 今回のお話は、動画配信サービスを開発・運営されている皆さまへ 「UI/UX」 についてのお願いです。冒頭からかなり対象を絞った内容に感じられるかもしれませんが、きっと多くの方にも共感していただける部分があると思います。 どうぞ気軽にお読みいただければ幸いです。 まずは こちら をご覧ください。(※音が出ます) ※ iOS では上手く表示されない不具合があります。動作環境は PC がおススメです。 動画見ながら寝落ち問題 シークバーのツライところ 理想のシークバーの提案 まとめ おまけ(シークバーの作り方) ステップ1 設計図を書く ステップ2 座標変換 ステップ3 描画 ステップ4 再生位置の移動 & スケール変換 まとめ 動画見ながら寝落ち問題 さて、皆さんは「動画配信サービス」を利用していますか? YouTube、Netflix、Amazonプライム・ビデオ、他にも数えきれないほどのサービスが乱立していますが、現代人なら何かしら一つは使っているのではないでしょうか。中には、いくつものサービスに加入して、毎晩ついつい夜更かししてしまったり、休日は朝から晩まで動画漬け、、なんて方もいらっしゃるかもしれませんね。かく言う私も、そんな動画漬けの毎日を送っている一人です。 そして今回は、そんな生活の中でどうしても気になる “あるUI” について、一言物申したいと思います。 それは 「シークバー」 です。 私は普段、寝ころびながらスマホで動画を見ることが多いのですが、ついつい 寝落ち してしまうことがあります。気づけば動画は再生し終わっていて、「あれ?どこまで見たんだっけ?」と、後から見返そうとするのですが、そこで困るのがシークバーです。動画の総再生時間に対して、シークバーの表示があまりにも短く、指で正確に寝落ちした位置まで移動するのがとても難しいのです。 シークバーのツライところ 「いやいや、それって本当にそんなに難しいの?」とピンとこない方もいるかもしれません。そこで、どれくらい操作が難しいのかを定量的に検証してみましょう。 まず、シークバーの物理的な長さですが、スマホを横向きにしたときの画面幅に依存します。端末によって違いはあるものの、大きめのスマホでも画面の横幅はせいぜい 150mm 程度です。(ちなみに、私が使っているスマホは 110mm でした) ただし、これは画面全体の幅。実際のシークバーには左右にマージン(余白)があるため、バー自体の有効な長さはもう少し短くなります。ここでは仮に、シークバーの長さを 140mm としてみましょう。この状態で 2時間(= 7,200秒)の映画を見ているとすると、 1mm あたりの時間 : 7,200秒 ÷ 140mm ≒ 50 秒/mm さらに、指の腹の太さはだいたい 15mm くらいありますよね。つまり、 指先 1 本分に約 12 分 (=50秒 × 15mm)の映像が圧縮されているわけです。 この状態で「あと5秒戻りたい」と思っても、1mm以下の繊細な操作が求められます。まるでお米に字を書くようなものですね。しかも、苦労してシークバーを微調整したところで、動画が再生されるまでは、どの場面から始まるのか分かりません。「このシーンはまだ意識があったな」「あっ、ちょっと行きすぎた」そんなことを考えながら、スクリーンを何度も指でスリスリ。そして、行きすぎては戻し、戻しすぎては進める。こうして、微調整を何度も繰り返す羽目になるのです。 理想のシークバーの提案 そこで冒頭の動画の新しいシークバーの提案です。各社の動画配信サービスでぜひ採用していただきたい、 理想のシークバー を自分なりに実装してみました。 工夫したポイントは、主に以下の2つです。 1. シークバーのスケールを拡大・縮小できる 再生位置の指定は、マウスならドラッグ操作で、スマホならタッチ操作で行います。これ自体は一般的な操作ですが、ここに スケーリング機能 を加えました。 マウスホイールで拡大・縮小(スマホの場合はピンチイン・ピンチアウト) スケールを大きくすれば、ざっくり全体を早送り・巻き戻し スケールを小さくすれば、秒単位での微調整もストレスフリー 「まず大まかに探し → 細かく調整する」 という動作が、スムーズに完結できるのです。 2. 動画のサムネイルを表示して視覚的に検索できる シークバーの上に 動画のサムネイルを表示する 機能も加えました。これは最近のプレイヤーにもよく見られる機能ですが、今回の提案ではスケール拡大・縮小と組み合わせて使えるのがポイントです。 拡大時には、前後のフレームが視認できるようにし、シーンの切り替わりもひと目で把握できる 「どこで寝落ちしたか」が視覚的に探しやすくなる この2点を組み合わせることで、「あの場面を探したいのに見つからない」という、あのもどかしさから解放されるのではないかと考えています! まとめ 以上、現場からのお願いでした! ちなみに冒頭のデモのサムネイル部分の描画には、ブラウザにそこそこ負荷がかかっているようで、上手く動作しないことがあります。特に iOS ではサムネイルが上手く表示されない不具合が見つかっています。動作環境は PC がおススメです。 また今回作ったシークバーをお好きな動画でお試しいただけるように、 お手元の動画ファイルを読み込めるバージョン を用意しました。良ければこちらも使ってみてください。 動画配信サービスを手がける皆さま、どうかこの寝落ちユーザーの声に耳を傾けていただければ幸いです! おまけ(シークバーの作り方) ここからはエンジニア向けのお話しになります。せっかくなので、スケールを自由に変更できるシークバーの実装方法について解説したいと思います。 冒頭の動画サンプルでは、サムネイル表示などの機能も含めているため、コードが少し複雑になっています。そこで、今回はシーク機能に限定したシンプルな デモ を用意しました。こちらのコードをベースにして、具体的な実装ポイントや仕組みをわかりやすく説明していきたいと思います。ソースコードは こちら です。 実装方法はいくつか考えられますが、今回はWebアプリとしての開発ということで、 Canvas API を使って実装しました。Canvas API は標準的な描画処理のインターフェイスを提供しており、モバイルなど他のプラットフォームでも似たような機能やライブラリがあるので、比較的簡単に移植が可能だと思います。 ステップ1 設計図を書く まずはデザインを決めて、その設計図を描きましょう。Canvas API では点や線、長方形といった基本的な図形のほか、テキストや画像も描画できますが、どんなGUIを作るにしても、まずは 描きたいものを細かな要素に分解する ことが大切です。 サンプルは下図のようなデザインになっていますが、よく見ると線と長方形、そしてテキストで構成されていることが分かると思います。 こちらに細かく寸法を記入していきます。 寸法の取り方はセンス次第で自由に決めていただいて構いませんが、 このようなパラメータを変数としてまとめておく と、あとからデザインを調整するときにとても便利です。 const SEEKBAR_POS_Y = 60 ; const SEEKBAR_HEIGHT = 15 ; const SCALE_LINE_HEIGHT = 8 ; const SCALE_TIME_TEXT_SIZE = 16 ; const CURRENT_TIME_TEXT_SIZE = 28 ; const HEIGHT = 120 ; ステップ2 座標変換 続いて、シークバーの描画に必要な知識をインプットしていきましょう。 シークバーの横方向は、動画の時間の経過を表しています。ここで、動画の時間と画面上の描画位置を結びつけるために、 「座標変換」 という考え方が役立ちます。聞き慣れない言葉かもしれませんが、実は中学生のときに習った 一次関数 の考え方とほぼ同じものです。 今回作ったものは、まず図のように動画全体から、その一部を切り取った赤枠の部分を画面上に描画しています。このように考えると、 「動画の時間軸」と「画面の座標軸」 という、2つの異なる座標軸が存在していることになります。 ここで、動画の時刻を 、画面の水平方向の位置を とします。さらに、 画面の左端の位置に対応する時間: 画面1ピクセルあたりの時間幅: とすると、次のような変換式が成り立ちます。 こちらの式だけでは、時間軸と画面座標軸の変換を具体的にイメージしづらいと思うので、下記のように値を定義して、その関係を図示してみます。 画面の幅: 現在時刻: 画面の右端の位置に対応する時間: 動画の長さ: 図の上側の青色の直線が時間軸で、下側のオレンジ色の直線が画面座標軸になります。グラフにすると次にような直線(一次関数)です。 この直線の式の定数項である を変化させると、時間軸上で切り取る範囲が変わります。その結果、画面上ではシークバーの表示位置が左右に移動し、まるで時間が動いているように見えるのです。 また、直線の傾きを表す の値を変えると、画面上で切り出される時間の範囲が変わります。これにより、シークバーのスケールが拡大・縮小されたような見た目になります。 ちなみに今回のデザインでは、画面の中央の位置が現在時刻 ( ) をあらわしているので、 は現在時刻から画面幅の半分 ( )の時間を差し引いた値となり、次のように表せます。 というわけで、動画の切り出し位置を制御するための変数として現在時刻 ( ) 、スケールを制御するための変数として1ピクセル当たりの時間( ) 、この 2 つを保持しておけば十分です。 // 現在の時刻 currentTimeMs = 0 ; // 1ピクセルあたりの時間(ミリ秒) timeMsPerPix = 2400 ; 元の変換式に を代入して消すと、現在時刻 ( ) と1 ピクセル当たり時間幅 ( ) を用いた式に整理できます。 ここからさらに変形して イコールの形にすると、時間軸上の座標 を画面上の座標 に変換できます。 これらが動画の時間軸と画面の座標軸を行き来するための変換式になります。描画位置の計算のために頻繁に利用するので 関数化 しておきます。 pix2time ( pix ) { const startTimeMs = this. currentTimeMs - this. canvas . width / 2 * this. timeMsPerPix ; return pix * this. timeMsPerPix + startTimeMs ; } time2pix ( timeMs ) { const startTimeMs = this. currentTimeMs - this. canvas . width / 2 * this. timeMsPerPix ; return ( timeMs - startTimeMs ) / this. timeMsPerPix } ステップ3 描画 さて、準備が整ったので、いよいよ描画方法について解説していきます。 描画に用いる Canvas API の利用方法はここで詳しく解説はしませんが、長方形とテキストの描画しか用いないので、下記の2点だけ雑に理解していればオッケイです。 長方形の描画 (fillRect) テキストの描画 (fillText) 描画処理は draw() 関数 の中にまとめられていますが、少し長いので段階的に見ていきましょう。 背景の描画 まずは背景の描画から。ここでは、キャンバス全体を指定した色で塗りつぶしています。つまり、この時点では背景が真っ白になっているだけです。 ctx . fillStyle = SEEKBAR_BACKGROUND_COLOR ; ctx . fillRect ( 0 , 0 , this. canvas . width , this. canvas . height ) ; シークバー本体の描画 次にシークバー本体を描画します。バーの背景は画面の横幅いっぱいに長方形を描くだけです。その後に動画コンテンツが存在する部分を緑色で塗り分けます。 // シークバーの背景描画 ctx . fillStyle = SEEKBAR_BLANK_COLOR ; ctx . fillRect ( 0 , SEEKBAR_POS_Y , this. canvas . width , SEEKBAR_HEIGHT ) ; // シークバーの動画コンテンツがある部分を描画 const dataStartPosX = this. time2pix ( 0 ) ; const dataEndPosX = this. time2pix ( this. durationMs ) ; ctx . fillStyle = SEEKBAR_DATA_COLOR ; ctx . fillRect ( dataStartPosX , SEEKBAR_POS_Y , dataEndPosX - dataStartPosX , SEEKBAR_HEIGHT ) ; シークバーの目盛りの描画 続いて少し長めですが、シークバーの目盛りを描画しているコードをご紹介します。 ctx . font = SCALE_TIME_TEXT_SIZE + 'px sans-serif' ; const { scaleInterval , textInterval } = this. adjustScale ( this. timeMsPerPix ) const startTime = this. pix2time ( 0 ) const endTime = this. pix2time ( this. canvas . width ) const firstScaleTime = Math . floor ( startTime / scaleInterval ) * scaleInterval let i = 0 while ( 1 ) { const scaleTime = firstScaleTime + i * scaleInterval if ( scaleTime > endTime ) { break; } if ( scaleTime > this. durationMs ) { break; } if ( scaleTime < 0 ) { i ++ continue } // 目盛りを描画 const scalePosX = this. time2pix ( scaleTime ) const scalePosStartY = SEEKBAR_POS_Y + SEEKBAR_HEIGHT const scalePosEndY = scaleTime % textInterval === 0 ? scalePosStartY + SEEKBAR_SCALE_HEIGHT * 2 : scalePosStartY + SEEKBAR_SCALE_HEIGHT ctx . fillStyle = SEEKBAR_SCALE_COLOR ; ctx . fillRect ( scalePosX , scalePosStartY , 1 , scalePosEndY - scalePosStartY ) // 時間をテキストで描画 if ( scaleTime % textInterval === 0 ) { ctx . textAlign = 'center' ctx . textBaseline = 'top' ctx . fillText ( this. time2str ( scaleTime ) , scalePosX , scalePosEndY + 5 ) } i ++ } 最初に描画する目盛りの時間は以下のように計算しています。 const firstScaleTime = Math . floor ( startTime / scaleInterval ) * scaleInterval ここで使っている scaleInterval は目盛りの間隔を表す変数で、1ピクセルあたりの時間幅に応じて調整しています。この位置は、下図のように 画面の左端ギリギリの外にある目盛りの時間 を指しています。 最初の目盛りの位置が決まれば、あとは画面右端まで等間隔で線を描画していくだけです。 let i = 0 while ( 1 ) { const scaleTime = firstScaleTime + i * scaleInterval ・ ・ ループを抜ける判定 ・ const scalePosX = this. time2pix ( scaleTime ) ・ ・ scalePosX の位置に目盛りを描画 ・ ++ i } 現在時刻の描画 現在時刻を示す赤い線とテキストを描画して完成です。 // 中心の線を描画 ctx . fillStyle = SEEKBAR_CENTER_LINE_COLOR ; ctx . fillRect ( this. canvas . width / 2 , 0 , 1 , this. canvas . height ) ; // 現在時刻の文字背景を塗りつぶす const textBgWidth = 100 const textBgHeight = CURRENT_TIME_TEXT_SIZE + 5 const textBgPosX = ( this. canvas . width - textBgWidth ) / 2 const textBgPosY = ( SEEKBAR_POS_Y - textBgHeight ) / 2 ctx . fillStyle = SEEKBAR_BACKGROUND_COLOR ; ctx . fillRect ( textBgPosX , textBgPosY , textBgWidth , textBgHeight ) ; // 現在時刻のテキストを描画 ctx . font = CURRENT_TIME_TEXT_SIZE + 'px sans-serif' ; ctx . textAlign = 'center' ctx . textBaseline = 'middle' ctx . fillStyle = SEEKBAR_TIME_TEXT_COLOR ; const textPosX = this. canvas . width / 2 const textPosY = SEEKBAR_POS_Y / 2 ctx . fillText ( this. time2str ( this. currentTimeMs ) , textPosX , textPosY ) ; ステップ4 再生位置の移動 & スケール変換 最後に、マウスイベントに合わせて再生位置の移動やスケール変更を行っているコードを見ていきましょう。 まず、再生位置の移動はマウスのドラッグ操作に連動して currentTime を更新するだけです。 this .canvas . addEventListener ( 'mousedown' , ( e ) => { this. isMouseDown = true this. mousePosX = e . offsetX }) this .canvas . addEventListener ( 'mouseup' , () => { this. isMouseDown = false }) this .canvas . addEventListener ( 'mouseleave' , () => { this. isMouseDown = false }) this .canvas . addEventListener ( 'mousemove' , ( e ) => { if ( this. isMouseDown ) { const prevMousePosX = this. mousePosX this. mousePosX = e . offsetX const diffX = prevMousePosX - this. mousePosX const newCurrentTime = this. currentTimeMs + diffX * this. timeMsPerPix this. currentTimeMs = Math . max ( 0 , Math . min ( this. durationMs , newCurrentTime )) this. draw () } }) 次に、スケールの変更もシンプルで、マウスホイールの操作に応じて timeMsPerPix を調整するだけです。 this .canvas . addEventListener ( 'wheel' , ( e ) => { e . preventDefault () ; let newTimeMsPerPix = this. timeMsPerPix if ( e . deltaY > 0 ) { newTimeMsPerPix *= 1 . 1 } else { newTimeMsPerPix *= 0 . 9 } this. timeMsPerPix = Math . max ( MIN_TIME_MS_PER_PIX , newTimeMsPerPix ) this. draw () }) これで、マウス操作による直感的なシークとスケール調整が実現できます。タッチ操作もサポートしていますが、実装方法は過去に執筆した 記事 でも解説しておりますので、こちらを参考にしていただければと思います。 まとめ 長くなりましたが、以上が処理の全容になります。 今回解説した座標変換の考えを利用すれば、アイデア次第で色んなものが作れそうな気がしませんか?ちなみに座標軸を2次元に拡張すれば、GoogleMap のようなマップアプリも作れます。みなさまの何かのお役に立てれば幸いです。 最後までお付き合いいただき、ありがとうございました。
こんにちは!セーフィーでQAエンジニアをしている福山です。 今回は、入社間もない私が、他プロジェクトのQAメンバーを巻き込み、探索的テストで製品品質を向上させた経験をお話しします。 この記事は、特にこんな方々に読んでいただけると嬉しいです。 探索的テスト や新しい品質保証の取り組みを考えているQAエンジニアの方 チームや部署を横断した連携で、組織全体の生産性や品質を高めたいテックリードやマネージャーの方 新しい環境で自身の強みを活かして成果を出したい中途入社の方 課題:リリースを前に、担当チームだけでは見つけられない不具合の壁 成功の鍵は「巻き込む力」!他チーム連携で意識した5つの工夫 1. タイミングを見極める 2. 心理的ハードルを下げて「テストに集中できる」環境を作る 数値的な目標は慣れてから 気軽に共有できる場を設ける バグを見つけることだけが成果ではない 3. 上長を味方につける 4. モチベーションを維持する 週替わりの画面担当ローテーション 個別フォローと密なコミュニケーション 迅速なフィードバック 5. 共有の場から関係性を築く テスト結果を数値的に可視化 関連題材を用いた勉強会 挑戦の裏側:ぶつかった壁と、思いがけない発見 ぶつかった壁 実施時間と他業務との調整 自身の認知度と信頼感 思いがけない発見 メンバーの協力的な姿勢 多様な視点からの発見 まとめ:組織を動かし、成果を出すために必要なこと 課題:リリースを前に、担当チームだけでは見つけられない不具合の壁 セーフィーのQA部門には、合計16名のQAエンジニアがいます。それぞれが複数の製品を担当しており、私が担当する製品のQAメンバーは4名。残りの12名は別の製品に携わっています。 日頃からテストスクリプトを使った網羅的なテストと、探索的テストを並行して実施していましたが、担当プロジェクトのメンバーだけでは、どうしても見つけにくい不具合があると感じていました。 迫りくるリリースを前に「どうすればもっと品質を高められるだろう?」と考えた私がたどり着いたのが、 「他プロジェクトを担当するQAメンバーを巻き込む」 というアイデアでした。同じQAという職種でも、普段は別の製品に携わっているメンバーの新鮮な視点と専門的な経験を活かした探索的テストは、きっと製品の隠れた問題を発見し、品質を一段と引き上げてくれると確信したのです。 成功の鍵は「巻き込む力」!他チーム連携で意識した5つの工夫 他プロジェクトのQAメンバーに協力を依頼するにあたり、スムーズな連携と参加者のモチベーション維持が重要だと考え、以下の点を意識して準備を進めました。 1. タイミングを見極める テストの実施タイミングは非常に重要です。今回は、機能が90%以上実装されており、かつリリースまで1ヶ月半というタイミングで協力を依頼しました。未実装の機能が多いとテスターが混乱しますし、開始が遅すぎると開発への負荷も大きくなるため、このタイミングが最適だと判断しました。 2. 心理的ハードルを下げて「テストに集中できる」環境を作る 主担当ではない製品の探索的テストはどうしても心理的ハードルがあるため、できるだけハードルを下げ、スムーズに進むようにいくつか工夫を行いました。 数値的な目標は慣れてから まずは目的を意識しつつも慣れてもらうことを最初の目標にしました。裏では具体的な目標数値は設定していましたが、いきなり数値を提示すると逆効果だと考え、後から共有することにしました。 気軽に共有できる場を設ける Backlogへの直接起票はやめ、スプレッドシートに記入してもらう形式にしました。これにより、内容の精度や不具合の重複を気にせず報告できるため、心理的なハードルが下がります。 バグを見つけることだけが成果ではない 不具合が検出できなかった場合でもモチベーションを下げないよう、「不具合がない」ことも貴重な情報であることを伝えました。また「違和感」や「改善点」も歓迎し、UI/UX向上に直結する重要な情報だと強調しました。 3. 上長を味方につける プロジェクトを円滑に進めるには、上長(グループリーダー:GL)の理解と協力が不可欠です。私たちは2つのグループに分かれているため、他グループのメンバーを巻き込むには、各グループのGLの理解と協力が必要でした。事前に相談し、快諾を得るとともに貴重なアドバイスもいただきました。上層部のサポートは、プロジェクトを成功させる上で非常に心強い後押しとなります。 4. モチベーションを維持する 約1ヶ月半の探索的テストへのモチベーションを維持してもらうため、具体的な仕組みをいくつか導入しました。 週替わりの画面担当ローテーション 参加者には週ごとに担当画面を割り当て、飽きを防ぎながら主要機能を優先的にテストできるよう工夫しました。 個別フォローと密なコミュニケーション リモートワーク中心のメンバーには、DMなどを活用して積極的にコミュニケーションを取り、不具合検出の感謝を伝えたり、困っていることがないか確認しました。 迅速なフィードバック 報告された内容は、極力その日のうちに確認し、何かしら返信するように心がけました。迅速なレスポンスは、報告する側のモチベーション維持につながります。 5. 共有の場から関係性を築く テスト結果を数値的に可視化 毎週、テスト結果を数値的に可視化し、参加者全員に共有しました。これにより、自分たちの活動が製品品質にどのように貢献しているかを実感してもらいました。さらに、特に参考になった報告内容については、感謝の意を込めて共有し、参加者の努力を称えました。 関連題材を用いた勉強会 テストの中盤には、探索的テストに関する勉強会を開催しました。共通の題材を通して、お互いの考え方やテストスタイルを共有する機会を設け、チーム全体のテストスキル向上と連携強化を図りました。 挑戦の裏側:ぶつかった壁と、思いがけない発見 今回の取り組みでは、もちろん悩みもありましたが、幸運にも助けられる場面も多くありました。 ぶつかった壁 実施時間と他業務との調整 各メンバーの業務が見えない状況で、どの程度時間を捻出可能かは非常に悩ましい点でした。GLに相談し、最低1時間/週という下限値を設けて自主性に任せる方針を決定しました。 自身の認知度と信頼感 最も困ったのがこちらです。中途入社から半年弱ということもあり、普段接点のない他グループのメンバーに自身の存在を知ってもらい、信頼関係を築くことが課題でした。勉強会などを通じて積極的に自己開示を行い、少しずつ関係性を構築していきました。 思いがけない発見 メンバーの協力的な姿勢 通常、初めての試みでは様子見から入ることが多いものですが、率先してスタートダッシュを切ってくれたメンバーがいました。また、中盤でも熱心に多くの報告をくれたメンバー、そして終盤まで継続してテストに参加してくれたメンバーなど、様々な協力がプロジェクトを前進させました。 多様な視点からの発見 各メンバーがそれぞれの得意な観点や、様々な視点から不具合を検出してくれたのは大きな収穫でした。これにより、私自身も「こんな観点があったのか」と学びを深めることができました。この多様な視点こそが、探索的テストの真価だと改めて感じました。 まとめ:組織を動かし、成果を出すために必要なこと 他プロジェクトのQAメンバーを巻き込んだ探索的テストは、製品の品質向上に大きく貢献しただけでなく、部内間のコミュニケーション活性化にもつながる、非常に有意義な取り組みでした。 本記事の最大の価値は、製品の品質向上という 結果 だけでなく、 その成果を生み出した「他チームを巻き込む」ための具体的なプロセスとノウハウ を共有した点にあります。具体的な数値は皆さんのチームの状況によって変わるかもしれませんが、ここに書かれている工夫や考え方は、きっと皆さんの組織でも再現できると思います。 私自身のように中途入社でドメイン知識が少ないメンバーでも、今回の工夫を凝らすことで、きっと素晴らしい成果を生み出すことができるはずです。 本記事が、皆さんの品質保証活動の一助となれば幸いです。
こんにちは。25新卒エンジニアの中です。 大学院では、ある車の会社と共同で「機械学習を用いて物理シミュレーションを置き換える手法」について研究を行い、先日ローマにて行われたAI関連の学会でポスター発表してきました。エスプレッソもパスタもワインも美味しかったです。 このブログでは、入社してから4ヶ月弱で行われた新卒研修プロジェクトで学んだ、「全体像の可視化」の重要性について話していきます。 新卒エンジニア研修について 研修で直面した壁 プロジェクトの全体像が見えなかった初期フェーズ 振り返り会と「可視化」の徹底 Before After 研修を振り返ってみて 4月-5月前半:課題発見フェーズ もし全体像が可視化されていたら… 5月後半-6月前半:課題発見フェーズ2 6月後半-7月:開発と未来設計のフェーズ まとめ 新卒エンジニア研修について 今回のセーフィーの新卒エンジニア研修では、4月真ん中から7月末までの4ヶ月弱で、「社内課題を解決するためのプロダクト開発」を行ないました。この研修の最大の目的は、「プロダクト思考を体現し、エンジニアとして成長する」ことです。つまり、ユーザー目線に立ち、プロダクトを考え、それに沿って開発を進め、その上で成長していくことが求められました。 研修で直面した壁 研修プロジェクトが始まってすぐに私たちが直面したのは、『チーム全員でプロジェクトの全体像を把握すること』の難しさでした。私たちのチームには、以下の2つの目的がありました。 新卒研修プロジェクトの目的:プロダクト思考を身につけ、エンジニアとして成長する プロダクトの目的:ユーザーの課題を解決する プロジェクトの全体像の把握ができていないと、この2つの目的を同時に達成することは難しいと痛感しました。 プロジェクトの全体像が見えなかった初期フェーズ チームでの開発フェーズが始まった初期段階では、プロジェクトの進捗状況が把握できていませんでした。 口頭でのタスク管理:口頭で決まったことがタスクとして記録されず、それぞれのタスクの粒度もバラバラでした タスクの偏り:誰が何をするのかが曖昧になり、チーム全体でタスクを抱えている人と、そうではない人の差が大きくなっていきました。 漠然とした不安:目の前のタスクをこなすことに精一杯で、「本当にユーザーの課題を解決するものなのか?」「このプロセスが自身の成長に本当につながっているのか?」という漠然とした不安が常に付きまといました。 これは、プロジェクト全体の把握ができていないことにより、「自身の成長」と「ユーザーの課題解決」という2つの目的を見失っていたことが原因でした。 振り返り会と「可視化」の徹底 プロジェクト全体の把握ができていないという課題を解決するため、私たちは開発機関の中間地点で開催した振り返り会で現状を共有しました。そこで、私たちは以下のシンプルなルールを徹底することに決めました。 タスクは必ずチケットとして可視化し、粒度を揃える 具体的なルールは以下の通りです。 チケットは1~2営業日以内で完了するものに限定する 処理中のタスクは1~2個にする 複数のタスクが同日に並ぶ場合は、全てその日で終わらせられる場合のみとする このルールにより、メンバー間でタスクの共通認識が生まれ、スムーズに開発を進められるようになりました。 Before 弁当競合調査の中で何をするのか、どう調べるのかがわからず、進捗状況が一目で分かりづらい。開発本部会は1日のはずなのに4日分引かれており、その中で何をするのかがわからない。 After アイテム画面実装という親タスクに対し、何をするのかを小タスクとして設定し、進捗を確認しやすくなりました。 研修を振り返ってみて もし研修開始当初に「全体像の可視化」の重要性に気づけていたら、私たちのプロジェクトはもっとスムーズに進んだという仮定のもと、開発研修を振り返っていきたいと思います。 4月-5月前半:課題発見フェーズ この時期は、プロジェクトの進め方や課題を発見するための手法に関する知識が不足していました。 もし全体像が可視化されていたら… 最終ゴールの明確化:最終的に完成させるプロダクトの規模やビジョン、最終到達点をチームで明確に合意していれば、そこから逆算してマイルストーンを設定できたはずです Backlogの適切な運用:ゴールが定まっていれば、Backlogのチケットの粒度も自然と定まり、手探りでの運用を避けることができたはずです 5月後半-6月前半:課題発見フェーズ2 この期間は、メンバーが入れ替わりで研修に参加していたため、チーム内での進捗共有が不十分になってしまいました。 もし「全体像」が可視化されていたら... スムーズな進捗共有:プロジェクトの全体像が可視化され、Backlogに適切にチケットが反映されていれば、戻ってきたメンバーもすぐに状況を把握できたはずです チームの一貫性の維持:チームとしての判断軸が定まっていれば、メンバーが抜けても方向性がぶれることはなかったでしょう 6月後半-7月:開発と未来設計のフェーズ 開発期間は、当初の計画よりもミニマム開発の完了が遅れてしまいました。また、明確なルールがないままアジャイル的な進め方になったため、進捗の把握に苦労しました。 もし「全体像」が可視化されていたら... 運用を見据えた設計: 開発の初期段階から「運用しやすい設計になっているか?」「コストに見合う価値があるか?」といった問いに向き合え、属人化を避けるための人員配置や計画を立てられたはずです まとめ この研修プロジェクトを通して、私は技術的なスキルだけでなく、『チーム全員が同じ全体像を共有し、目的を意識して開発を進めること』が、プロジェクトの成功と自身の成長に不可欠だと学びました。 研修で直面した壁は、私たちを大きく成長させてくれました。配属されて数週間経った今、この経験を活かし、 自身の成長とチームとしての成果、両方の視点を持って行動すること を心がけています。 今後、この研修で開発したプロダクトは、社内運用に向けてチームで議論を重ねていきます。技術を学ぶだけではないこの新卒研修プロジェクトを経験できたこと、そして多くの壁をチームで乗り越えた経験は、これからのエンジニアとしてのキャリアにおいて、常に活かされていく大切な教訓です。
こんにちは! モバイルチームの夕田です! 先日開催された国内最大級のiOSエンジニア向けカンファレンス「 iOSDC Japan 2025 」に、セーフィーのiOSチームが参加しました! 今年セーフィーは本イベントに シルバースポンサー として協賛させていただきました! 下記 connpass のページをぜひご確認ください! yumemi.connpass.com おわりに 「ブログを書くまでがiOSDC」ということで、無事に私たちもiOSDCを締めくくることができました✌️ 最後になりますが、このような素晴らしいイベントを企画・運営してくださったスタッフ、関係者、スポンサーの皆様に心より感謝申し上げます。 来年も楽しみにしています!! セーフィーでは一緒に働くエンジニアの仲間を随時募集しています。 少しでも興味を持たれた方がいましたら、ぜひ下記の採用ページをご覧ください。 safie.co.jp
はじめに セーフィーの情報システムグループでグループリーダーをしている松尾と申します。 セーフィーは「映像から未来をつくる」というビジョンのもと、クラウド録画サービスを提供しています 。私たちのサービスは、防犯・見守りに留まらず、業務効率化や現場DXなど、様々なシーンで社会の「安心安全」を支えています。 情報システムグループ(以下、情シスG)では2025年1月から半年ほど、アジャイルを導入したプロジェクト・タスク管理を実施しました。 今回はこちらの取り組みについて、主に以下の内容を書いていきたいと思います。 はじめに なぜアジャイルを導入したか どのようにアジャイルを導入したのか アジャイル勉強会の実施 スプリントの実施 情シスアジャイルプレイブックの作成 アジャイルを実施するための環境づくり どのような変化があったか 細かい改善の例 半年間を振り返って おわりに なぜアジャイルを導入したか 「日々の問い合わせ対応に追われ、本来注力すべき中長期的なタスクがなかなか進まない…」多くの情報システム部門が抱える悩みではないでしょうか。 私たちセーフィーの情シスGも例外ではありませんでした。タスクは常に「実施中」のまま停滞し、定例報告は「進んでいない状況」の共有になりがちで、チームの士気にも影響が出始めていました。 具体的には以下のような問題がありました。 日々の社内からの問い合わせ対応と、目標設定に紐づく長期的なタスクを並行して実施しており、目標に紐づく長期タスクは各自が開始と終了を設定して進めているが、 突発的な業務などにより、タスクの進捗が遅れがちになっていた その結果、 タスクがずっと「実施中」のままとなり、進捗が見えづらい状態 となっていた 2週間毎に実施している定例では「タスクが進んでいない状況」を共有することになり、 メンバーのモチベーションも上がらない状態になっていた 進捗が見えないため、 適切なタイミングでチームでサポート出来ないというジレンマ があった これらの課題を解決するため、情シスGでは以下の目的を掲げました。 無理のない形でメンバーが成果を出せるようにする そのために、 成果を可視化し、チーム内外にすぐに共有できる状態 を作る 目標設定の状況を明確にし、進捗を正確に把握できる状態を作る 目標達成までのプロセスを細かく刻み、その方法が適切か、 より良い方法はないかを常に探求できるようにする 定例会議を、単なる進捗報告ではなく、 成果を共有する場 にする これらの目的を達成するためのアプローチとして、私たちは「アジャイル」な働き方に着目しました。 どのようにアジャイルを導入したのか アジャイル勉強会の実施 まず、チームでアジャイルを始めるにあたり、セキュリティチームと合同でアジャイルの勉強会を実施しました 。詳細はこちらの記事をご覧ください。 参考記事 engineers.safie.link この勉強会を通じて、アジャイルを導入する際には以下を気をつけるということを学びました。 フレームワークの 「やり方だけ」を取り入れることはしない こと アジャイルの原則とプラクティスの目的を理解し、 現状のチームに合ったものを取り入れ、自分たちのものにしていく こと 上記をふまえ、情シスGに合う形で以下のような形でアジャイルを取り入れてみることにしました。 まずは 「半年間のトライアル」 として始めてみる アジャイルの専門用語はできるだけ使わない 情シスGの中で 取り入れられることだけを利用 する 最終的に、この仕組みがメンバーが 無理なく成果を出すための仕事の仕方の型として定着する ことを目指す スプリントの実施 2週間を1つのスプリント(期間)とし、以下のサイクルで業務を進めていきました。 2週間でやること決め(スプリントプランニング) 2週間で実施することを決める 朝会(デイリースタンドアップ) 毎日実施し、問い合わせ対応とタスクの進捗状況、困っていることを確認する 定例(スプリントレビュー) 2週間で実施した成果を発表する ふりかえり(スプリントレトロスペクティブ) 2週間の振り返りを実施し、その中で優先度の高い対応すべきタスクを決める 情シスアジャイルプレイブックの作成 特に意識したのは、単にフレームワークを導入するだけでなく、「なぜこのプラクティスを行うのか」という目的意識をチーム全員で共有することでした。 そのために、各プラクティスの目的や具体的な進め方を 「情シスアジャイルプレイブック」 として言語化しました。 このプレイブックは、途中でチームメンバーが変わっても「無理なく成果を出す仕組み」が継続されるための、私たちのチームの重要な道しるべとなっています。 記載した主な内容 背景:なぜ情シスGがアジャイルに取り組むのか 目的と具体的な進め方 スプリント(2週間)の進め方 2週間でやること決め(スプリントプランニング) 朝会(デイリースタンドアップ) 情シス定例(スプリントレビュー) ふりかえり(スプリントレトロスペクティブ) Notionページに情シスアジャイルプレイブックをまとめています。 アジャイルを実施するための環境づくり アジャイル導入と並行して、メンバーが目標設定に集中できる環境づくりも進めました。 具体的には、やり方がある程度決まっている定常業務のマニュアルを整備し、業務委託のメンバーに対応してもらう体制を作ることで、正社員メンバーが課題解決に集中できるような環境作りを進めました。 どのような変化があったか スプリントを進めていく中で、以下のような変化が現れました。 スプリント開始1ヶ月ほど プロジェクトの全体像を把握 できるようになった 目標に沿って 一日のタスクを可視化 できるようになった 自分の目標と進捗を以前より意識 するようになった 他のメンバーが何の目標を立てているか 分かるようになった やることが明確になり、目標やマイルストーンを立てることで 相談しやすくなった 2週間単位のスプリントを意識し、具体的に何回のスプリントでプロジェクトを終わらせなくてはいけないと考えたことで、 プロジェクトの全体像を意識し、どこまで実施すれば完了なのか、ということを考える ようなったという意見が出てきました。 また、スプリントでは必ず設定したタスクを完了する必要があるため、 完了するために今日はどのタスクをやるべきかを意識する ようになりました。 タスクを意識することで、進めるうえでの課題点が分かるようになり、チームで 相談がしやすく なりました。 2回目スプリントのふりかえりの様子 スプリント開始2ヶ月ほど (アジャイルブックの作成により)タスク管理の取り組みを言語化できた ゴールが抽象的なプロジェクトへの恐怖感が少なくなった (とりあえず2週間のタスクを決めて進め、都度修正していくサイクルが出来てきた) タスクのプロセスを意識し、 できるだけ細かくタスク化するようになった タスクを細分化することで、スケジュールに沿った対応ができた 突発的なタスクを考慮して、 バッファを持たせた目標設定をする感覚が馴染んできた 前回の振り返りと比較することで、より意識して目標管理に取り組めるようになった 2週間単位での仕事の進め方に慣れてきたこともあり、チームで 2週間で実施可能なタスクの量の感覚が少しづつ分かってきた という意見が出てきました。その結果、突発的なタスクを考慮し、 ある程度バッファをもたせた目標設定が出来る ようになってきました。 また、2週間でタスクが区切られているということから、ゴールが抽象的だったり全体のタスクサイズが分からないものでも、 とりあえず2週間で出来る事を決めて、都度方向を修正していく 仕事の進め方が出来るようになってきました。 細かい改善の例 スプリントを繰り返す中で、タスク管理の方法を細かく変更をしていきました。 今回は一例として、朝会の運営方法をどのように変更していったかをご紹介します。 課題: 朝会のスピード感がない 改善策: Backlogの表示をガントチャートからボード形式に変更。「今日やること」のステータスを追加し、 現在取りかかっているタスクを分かりやすくした 課題: 誰かが困っていても、その内容が分かりづらい 改善策: 当番制だった朝会のファシリテーションをグループリーダーに固定 。各パートでメンバーに 「確認したいことはありますか?」という問いかけを入れる ようにした 課題: 「2週間やること決め」の場が、ただタスクを各自が決めるだけの場になってきている 改善策: 「2週間やること決め」でメンバーが2週間でやることを決めた後に、疑問点や知りたいことなど皆から意見をもらう問いかけを追加し、 コミュニケーションの場にするようにした 課題: 困っていることの相談が解決策の議論にまで発展し、朝会が長引いてしまう 改善策: 朝会を2部制に変更 。前半は定常業務中心のヘルプデスクパートとし、非定常業務の相談は後半に回すようにした 課題: 問い合わせ対応を誰がやるか、お見合いになってしまう 改善策: チームで話し合い、 一旦の対応として問い合わせ対応をチケットごとの輪番制に変更 した このように、2週間ごとにふりかえりと細かい改善を繰り返すことで、朝会の運営方法を少しずつ改善していきました。 半年間を振り返って 半年間のトライアルを経て、チームには以下の変化や文化が定着しました。 細かい確認と改善を繰り返す仕組み (アジャイルな仕事の進め方)がある程度、 チームに定着 した 2週間ごとの定例が、成果を発表できる場になった 細かく成果を発表していく仕組みになったことで、 目標設定の評価が以前より書きやすくなった 非定常の問い合わせ対応をタスク毎の輪番制にすることで、チーム内でのノウハウ共有が進んだ。 やったことのない分野もやってみる文化が根付いてきた 「完璧でなくてもいいので、とりあえずやってみる。解決しなければチームのメンバーに相談する、プロに頼る」 という雰囲気が定着した 「頼ることは悪いことじゃない」 というマインドが浸透した 細かい確認と改善を繰り返す仕事の進め方が定着したことで、プロジェクトを進めるうえで手戻りが少なくなったり、方向性を修正しながら動くということが出来るようになりました。 特に、 とりあえずやってみて、分からなければチームに相談する、頼ることは悪いことではない という文化が定着したことは、チームにとっても非常に重要な事だと考えています。 上記の文化が定着したからこそ、問い合わせ対応の輪番制でメンバーが実施したことがない問い合わせがあった場合でも積極的に対応したり、難易度が高いプロジェクトについてもまずはやってみて、チームに相談して進めていくことが出来るようになったと感じています。 半年間のトライアルについてふりかえりをした様子 おわりに 最後までお読みいただき、ありがとうございました。今回は、セーフィーの情報システムグループが取り組んだアジャイル導入の道のりをご紹介しました。 今回のアジャイルの導入はこれで終わりではなく、今回の取り組みでチームに今後も改善を続けていく予定です。 私たちの試行錯誤の過程が、同じようなチームの課題に悩む方や、情報システム部門の働き方に関心のある方にとって、少しでも参考になれば幸いです。
導入 お久しぶりです。戌亥です。晴れてセーフィー株式会社新卒エンジニアとなりました。( 参考 ) 研修で動画の仕組みに関して学ぶ機会があったのですが、そのうちの画像圧縮に関してイメージしづらい部分があると感じました。同期エンジニアで勉強会があり、今回はそこで話した内容を一部加筆修正して解説しようと思います。 導入 コンピュータでの画像の扱い方 YUVとは 離散コサイン変換(DCT) まとめ コンピュータでの画像の扱い方 画像圧縮の前に画像がデータとして扱われていることを知らない方向けに、画像がおおよそどのようにデータとして扱われているかを説明します。 説明のために画像を拡大して見ることができるアプリをGeminiで作成しました。 画像ピクセル拡大ツール (クリック固定機能付き) 画像を入力し、クリックした地点を8×8マスで拡大してみることができます。拡大だけでなく、RGB値で表示するモードとバイナリ値で表示するモードがあります。 こちらが画像を拡大した時の図です。左の画像の小さくオレンジ色になっている(セーフィーのロゴの嘴)部分が拡大されたものが右のグリッド図になります。 一般的に画像は0~255の256段階のRGB(Red, Green, Blue)値を各画素ごとにもっています。以下の画像は拡大したグリッド図をRGBの値で表したものです。 コンピュータは0と1で解釈するのでデータ量は2進数で考える必要があります。この0または1をビットと言う単位で考えます。 256は2進数で2 8 なので0~255を表すためには8ビットが必要です。画像において、このビット数を減らすのが画像圧縮です。 YUVとは YUVとはRGBやHSVなどと同じ色空間(色の表し方)の1つです。 人の眼の「輝度信号に対して敏感」と「色差信号に対して鈍感」という特性に合わせた表し方で、輝度信号Yと青色成分の色差U(または )と赤色成分の色差V(または )で構成されています。 ディスプレイに表示させる際やカメラで撮影した生のデータはRGBですが、この「色差信号に対して鈍感」という特性から、YUVに変換させることでビット数を減らすことができます。 これを体験できるアプリをGeminiを使って作りました。 YUV3層ビューワ ここに画像ファイルを上げると、YUVそれぞれの層がどのような役割をしているかが直感的にわかります。またこちらのスキップ数というのは、Yで縦×横、Uで縦×横、Vで縦×横とそれぞれ2次元の画素を持っています。これを何個ずつ進むかというものです。 例えばスキップ数が1であればその層は完全な情報です。スキップ数が2だと1つ飛ばして、飛ばしたマスはその前の値で埋められるので、同じ画素値が2つ連続することになります。そうした時の全体のビット数はYが100%、Uが50%、Vが50%となります。つまりビット数は2/3で67%ほどになります。これはYUV 4:2:2と呼ばれる方式と同じです。 YUV 4:2:0と呼ばれる水平・垂直方向にそれぞれ1つ飛ばしていく方式ではYが100%、Uが25%、Vが25%となりビット数は1/2となります。 アプリの方で試してもらうとわかる通り、Yをスキップするとすぐにわかりますが、UとVを少し飛ばしただけでは気づきません。こうして色差信号を減らしていくと圧縮することができます。 ITU-R BT.601というアナログ信号とデジタル信号を相互に変換するための規格があります。これによると、RGBとYUVの関係式は式1のように定義されています。 式1:RGB to YUV 離散コサイン変換(DCT) DCTは厳密には圧縮方法ではなく、圧縮のために必要な処理の1つです。変換のために画像に周波数の考えを導入します。 右のようなのっぺりとした濃淡の変化が少ない画像を低周波な画像、唐草模様や左の濃淡の激しい画像を高周波な画像といいます。自然画では高周波な画像が少なく、高周波成分を削ってしまってもあまり影響を与えません。そこでDCTを行って画像を周波数にしてから高周波成分を削ることで画質にあまり影響を与えずに画像を圧縮できます。 DCTの具体的な手法の前に、フーリエ変換という概念をお話しします。「全ての周期信号は正弦波の合成で表現できる」という考えがあり、これに基づき波形が永遠に続くと仮定して合成元の正弦波を導けます。ざっくりいうとこれがフーリエ変換であり、波形がどの周波数のどのくらいの強度の正弦波で構成されているかがわかるというものです。 DCTの式は式2で示すことができますが、勉強していた当時、これを見てもよくわかりませんでした。 式2:Image to Power DCTはフーリエ変換と似たことをやっており、画像の各層を2次元の波だと考え、ブロック単位で細かく分けた画像を周波数成分に分解します。 例えば8×8マス単位でDCTを行う場合、まず0~7Hzの波形を作ります。 そうすると下記の図のような8つの波形ができます。 図:0~7Hzの離散波形 それをもとに下記の図のような基底関数画像といわれるグレースケールの画像を作成します。これは2つの波の該当する部分を8×8の各マスで掛け算します。 図:基底関数画像(u, v)= (5, 2) このような画像を0~7Hzの8パターンを縦×横の計64パターンを用意します。 これは8×8マス単位でDCTを行うのであればどんな画像に対してでも同じ64パターンの画像を使います。 下記の図のように、生成した64パターンの画像と対象のブロックとの内績を取った値がDCT変換後の画素値となります。 図:DCT変換 DCT変換後の画像はブロック単位(この場合は8×8の画像)になります。分割した数だけDCT画像(係数)があるのでこの状態では圧縮されていません。画像が周波数情報に変わっただけです。 これを体験できるアプリも同様にGeminiを使って作りました。 DCT/IDCT可視化ツール 画像を入力し、クリックして範囲を指定するとその部分に対してDCT係数を計算することができます。出力されたDCT係数に対して全範囲を選択して逆DCTをすることで、元の画像に戻すことができます。当然範囲を限定して逆DCTをかけると圧縮によって元の画像とは少し違う結果となります。出力された表を見るとその画像の局所的なDCT係数がわかりますが、各係数がその周波数の影響を与える大きさだと思っていただけるといいです。ぜひいろんな条件で試してみてください。(逆DCT結果の画像がグレースケールなのはYUVのY成分のみで計算しているためです。) まとめ 動画圧縮の仕組みについてYUVとDCTを用いる部分について簡単なアプリを使って解説しました。YUVを用いることで色差情報を削ることができ、DCTを用いて画像を周波数成分にすることで画質に影響を与えない情報に絞って削ることができることがわかりました。大学で習っている当初はあまり何に使うのか理解できなかったフーリエ変換などの概念も、割と様々な場面で出てくることが多いので、昔の自分にしっかりやっておくよう忠告したいところです。 また私が調べていたときは、出力結果画像の差でみることができるYUVはまだしも、DCTに関しては64パターンの謎の画像(基底関数画像)の意味がずっとよくわからなかったので、今回のアプリで誰かの理解の助けになるとうれしく思います。 余談ですが、最近買った「H.264/AVC教科書」が図も交えてしっかり書かれていて個人的におすすめです。この記事で動画圧縮の仕組み等に興味を持ってくださった方は読んでみてもいいかもしれません。
こんにちは、iOS エンジニアの @monolithic_adam です。 iOSDC Japan が近づきましたね。今年は新しい会場で楽しみにしています。 #Safie は今年初めてシルバースポンサーとして参加させていただきます! 会場にてセーフィーのメンバーを見かけたら、ぜひ声をかけてください。 iOSDC Japan 2025参加情報 ノベルティー パンフレット クラウド録画サービス「Safie」について Our App Safie Viewer(セーフィー ビューアー) 最後に We are hiring! iOSDC Japan 2025参加情報 ノベルティー セーフィーオリジナルのアンブレラマーカーです。 ノベルティー付きチケットの方にはお届けされるはずなので、ご活用ください! パンフレット iOSDCブック内のパンフレットにも、以下の内容を掲載させてもらっています。 是非ともご覧ください! クラウド録画サービス「Safie」について 今年スポンサーとして初参加ということもあり、Safie(セーフィー)というサービスについてご存じない方ばかりかと思います。 この場で簡単にSafieについてご紹介させてください。 Safieのサービスについて 一言で言うと、カメラの映像をクラウド化するサービスで、iOSアプリとしてはカメラを操作したり、映像を閲覧するといった機能を提供しています。 Our App ということで、次にiOSアプリについて紹介させてください! Safie Viewer(セーフィー ビューアー) アプリは こちら からダウンロード可能 デモ機能により、無料・アカウントなしで体験可能です #SafieViewer ではいくつか特徴的な機能を提供する必要があり、 動画の再生 WebRTC / HLSのハイブリッド動画配信に対応 イベントや録画有無を表示するためのリッチなシークバー 広角撮影して歪んだ動画を3D空間で補正 参考: セーフィーの新製品「Safie Go 360」のAndroid実装 - Safie Engineers' Blog! トークバック機能 アプリから音声を入力、 WebSocket を経由してカメラから音声を再生 といった技術的にも面白い部分が色々とあります。 最後に 今年は新しい会場ですし、セーフィーも初スポンサーで色々とワクワクしています! 会場でセーフィーくんのTシャツを着ている人を見かけたら、是非ともお話ししましょう! 最高にiOSDCを盛り上げていきましょう!! ※ iOSDCに関しての最新情報は公式サイトや各イベントページをご確認ください 他社様と合同で非公式のアフターイベントもやります! yumemi.connpass.com We are hiring! モバイルチームでは、多様な環境で世界に向けたアプリをともに開発する仲間を募集しています! open.talentio.com
はじめに こんにちは、データドリブン推進室でデータエンジニアをやっている鶴です! 先日、 OSS BIツールについてのブログ記事 を同じチームの小宮が投稿しておりましたが、その中でご紹介していた「Lightdash」でカスタムチャートという機能があります。 個人的に気になっていた機能だったので実際に触ってみてどのような感じで使えるのか、グラフはどんな感じになるのかご紹介したいと思います! はじめに Lightdashのカスタムチャートとは カスタムチャートを触ってみる 最後に Lightdashのカスタムチャートとは Lightdashには 公式ドキュメント に記載があるように標準でさまざまなチャートを使うことができます。 2025/06時点では以下の10個のチャートがあり、普通に使う分には困らない感じです。 表 (Table) 円グラフ (Pie chart) ファネルチャート (Funnel chart) 棒グラフ (Bar chart) 面グラフ (Area chart) 折れ線グラフ (Line chart) 横棒グラフ (Horizontal bar chart) 散布図 (Scatter chart) 複合グラフ (Mixed chart) 数字表示 (Big Value) 参考:積み上げ棒グラフのイメージ 更にLightdashではビジュアライゼーションの表現の幅を上げる機能として カスタムチャート 機能があります。 この機能は Vega‑Lite をベースとした高度なビジュアライゼーション機能で、Lightdash上で標準グラフではできないようなグラフスタイルや細かい設定をJSONベースで作成できます。 (なお、こちらの機能は公式ドキュメントに記載があるようにまだベータ版となっておりますのでご注意ください) 使い方としてはとても簡単で以下のようにしていくと使えます。 「Query from tables」で対象テーブルを開く 対象ディメンションとメトリクスを選択し、Chartの「Configure」をクリック チャート設定の「Chart type」のリストの中にある「Custom」をクリック JSON編集画面が出てきます(簡単に編集できますね!) またこのカスタムチャート機能ですが最近の アップデート でテンプレート機能が搭載され、より触りやすくなりました! 今回は気になったいくつかのチャートを試しに作ってみてどんなグラフができるのか試してみます! カスタムチャートを触ってみる 最近実装されたテンプレートはカスタムチャートを開いた時に表示される「+Insert template」から選ぶことができるようです。 テンプレートをクリックするとJSONコードが生成され、自動的に集計対象がマッピングされる感じになってます。 このfieldで利用されている項目はResultsの下にあるSQLで自動生成されたSQLの項目名を利用しているようです。 なお、利用する項目によってうまく表示されなかったりするのでその場合は編集して調整して利用しましょう。 それでは気になっていたグラフをそれぞれ見ていきたいと思います! 棒グラフ (Bar chart) テンプレートを利用して適当に作成したサンプルデータの商品カテゴリと売上を使って棒グラフを選んでみました。 テンプレートを使うことでワンクリックで棒グラフが作られました! 当たり前ではありますが見た目は標準の棒グラフと変わらない感じですね。 ヒートマップ 会員ランクと地域の会員平均年齢を使ってテンプレートを使ってみましたがうまく項目が選ばれなかったため手動で編集し作成してみました。 現在ヒートマップは標準グラフにはなく、カスタムチャートのみの機能になるためこちらを使うことになりそうですね。 バブルチャート 商品カテゴリと売上、平均単価、平均顧客年齢を使い散布図のバブルチャートを作成してみました。 軸の scale プロパティで年齢のスケールを30歳以上にすることで見やすくしています。 おそらく標準の散布図ではサイズオプションがなかったためバブルチャートは作れないと思われますので利用したい方はこちらを使うことになるかなと思います。 分散積み上げ棒グラフ(人口ピラミッド) 次はテンプレートにないものを作ってみます。 vega-liteのexampleを眺めていて気になった 人口ピラミッド です。 サンプルコードを見よう見まねで作ってみたところいけました…! こういった手の込んだグラフはBIツールで作る経験がなかったので作れるんだなと感動しちゃいました。。。 こういったチャートが作れるのはメリットですね。 Tableauのリファレンスライン的な平均ライン 最後に棒グラフ上に平均ラインを 表示 させるexampleも試してみましたが苦戦しつつも表示できました。 なんだかんだ使うことありそうなラインです。 コードの工夫の仕方によっては目標値等のラインを設定できるのではと思います。 最後に 気になっていたカスタムチャートのいくつかのグラフや機能を触ってみました。 Lightdashはまだまだ成長の段階で他のBIと比べてビジュアライゼーションの面で実現できないことがあったりしますが、このカスタムチャート機能を使うことで痒いところに手が届くいい機能だなと思います。 しかしながらUIでぽちぽちっとグラフを作るようなBIツールの本来の操作と比べたらやはり難しい部分もあり、JSONコードを編集して作るのは苦労する面もありますのでChatGPT先生等にご教示いただきながら作ってみるといいかもしれません。 公式サイトにはサンプルコードがあるので流用できる部分も多いと思います。 またこのLightdashですが2025年6月現在でも高頻度で アップデート が行われており、今後も機能がさらに充実していくと思いますし、自分自身もかなり期待しています! Lightdashのチャート機能が気になっている方々の参考になれば幸いです!
2025年新卒として開発本部に配属されました、恩智太陽です。 早速ですが若手エンジニアの皆さん、セキュリティを意識してコーディングできていますか? 私はセーフィーに入るまでほぼ意識せずWebアプリ開発など行っていました… IT業界においてセキュリティは理系、文系どちらに行っても切っても切り離せない英語のような重要な存在です。 そんなセキュリティの学習をおろそかにしてしまうと、どんなにいいサービスを作ったとしても、悪意のある人の手によって台無しにされてしまうことがあります。 それぐらいとても重要な分野ですが、セーフィーでは新卒研修の一環として座学+ワークショップ+ハンズオン形式で実施するセキュリティ研修があります。 この記事の前半では研修で何を学んだのかについて、後半では学んだことをどのように業務でアウトプットしたのかをご紹介します。 はじめに この記事で伝えたいこと 誰向けの記事? 座学:アジャイルと脅威モデリングについて ワークショップ:脅威モデリングを実践! 4つの問いに対して行ったこと ステップ1:DFDで「私たちは何に取り組んでいるか?」を明確にする ステップ2:STRIDEで「何が問題になりうるか?」を洗い出す ステップ3 :リスクマップで優先順位をつけて対策を考える ステップ4:+/Δ (プラス/デルタ)で振り返える 完成後にチームごとに発表 このワークショップで感じたこと 脆弱性診断ハンズオン:ハッカーになって攻撃してみた データベースの情報を抜き取ってみる SQLインジェクションの本当の恐ろしさ 脆弱性診断ハンズオンで感じたこと アウトプット:セキュリティ研修で得た知識を実践へ! 新卒研修プロジェクトに取り入れる ①設計の土台である非機能要件に反映 ②作った機能に「攻撃」を仕掛けてみる まとめ はじめに この記事で伝えたいこと セーフィーのセキュリティ研修の概要について 学んだことをどのように仕事に生かしたのか 誰向けの記事? セキュリティの重要性をあまり認識していない方(開発者) 攻撃手法の概要は把握しているが、具体的な攻撃のイメージがついていない方 どんなところに脆弱性が潜んでいるのかイメージできない方 座学:アジャイルと脅威モデリングについて 研修の前半では、まず アジャイル開発 について学び、その後に 脅威モデリング を学びました。 私自身、「アジャイル開発」というものは知っていたのですが、「なぜセキュリティの研修なのに、なぜアジャイル開発の話から始まるんだろう?」というのが正直な感想でした。 それは、アジャイル開発の強みである「スピード」が、セキュリティ対策の遅れで弱点に変わるからです。開発終盤での設計の修正は、多大な手戻りとコストがかかってしまいます。 そこで重要なのが、セキュリティ対策を開発初期から行う「シフトレフト」という考え方です。 開発のスピードと安全性を両立させるためには、まずその土台となるアジャイル開発の全体像を理解することが不可欠だったのだと、深く納得しました。 出典:Security Compass - How to Sell Security Training Costs Internally https://www.securitycompass.com/blog/how-to-sell-training-costs-internally-2/ (2025年7月30日アクセス) ※「設計段階(青)」で不具合を見つけて修正するコストを基準の1とした時、「開発段階(橙)」、「テスト段階(灰)」、「リリース段階(黄)」それぞれで対応した時のコストのかかり方 脅威モデリングとは、システムに対して「どのように攻撃が発生しうるか」を体系的に分析し、具体的な攻撃経路やリスクを特定するためのリスク分析手法であり、シフトレフトを実践するための強力なプラクティス(実践手法)の一つです。 この手法の強みは、設計段階で 潜在的な問題 を 早期に発見・修正 できるため、後の工程での修正コストを最小限に抑えることができるということに加え、「チーム内の共通理解を形成できること」があります。 DFD(データフロー図)のようなモデルを使うことで、チーム全員が同じ図を見て同じ目線で「システムがどのように攻撃されうるか」を議論できるようになります。 研修を受ける前の私は、正直なところ「セキュリティ対策=実装段階でのコーディングにて対策」というイメージでした。 しかし、この研修を通してシステムの設計そのものに脆弱性が潜む可能性や、チーム全体で脅威について対話し、共通認識を持つことの重要性を学びました。 セキュリティは専門家だけの閉じた仕事ではなく、開発に関わる全員で対話し、作り上げていくことが重要だと、セキュリティに対する考え方が大きく変わる経験となりました。 ワークショップ:脅威モデリングを実践! 座学の後は、チームに分かれて脅威モデリングのワークショップを体験しました。 課題は、タスク管理Webアプリ(脆弱性診断ハンズオンで用いる「 Bad Todo List 」)の要件をベースに、どのような脆弱性があるのかを、脅威モデリングの「4つの問い」に沿って考えていくというものでした。 タスク管理Webアプリ(脆弱性診断ハンズオンで用いる「Bad Todo List」)の要件(研修資料から抜粋) ワークショップの流れについては「 セーフィーの脅威モデリングベースのセキュア開発トレーニング 」の記事をご確認ください。 4つの問いに対して行ったこと ステップ1:DFDで「私たちは何に取り組んでいるか?」を明確にする 脅威モデリングの最初の問い、「私たちは何に取り組んでいるか?」を明確にするため、今回は一般的に使われる「DFD (データフローダイヤグラム)」を用いてシステムの全体像を分析しました。 DFDの要素(研修資料から抜粋) DFDは、システムにおけるデータの「流れ」と「処理」を視覚的に表現する図で、詳細な動作(How)よりも「何をするのか(What)」を明らかにすることに長けています。 DFDの矢印はあくまでデータの流れ(Data Flow)のみを記述しなければならず、書き始めはこのルールになかなか慣れませんでした。 つい「データを取得するリクエスト」のような、処理の制御に関する矢印を何度も書きそうになり、苦労しました。 ですが、Bad Todo Listの要件から上記のようなDFDを作成していくと、要件を見ただけでは気づけなかった視点や懸念点が次々と浮かび上がってきました。 ステップ2:STRIDEで「何が問題になりうるか?」を洗い出す 次に、「何が問題になりうるか?」という問いに答えるため、 「STRIDE」 というフレームワークを使って「起きたら困ること(リスク)」を洗い出しました。 STRIDEは、以下の6つの観点から脅威を分類する「型」です。 STRIDEの説明(研修資料から抜粋) このフレームワークを用いることで、攻撃者の動機や目的といった、より高い視点から体系的に脅威を分析することができます。 作成したDFDの各機能やデータの流れに対して、 「ユーザー情報登録の際に、大量の登録リクエストが送られてくると、データベースが破壊される可能性があるから D(サービス拒否) が当てはまる」 「タスクの編集の際に、タスクを勝手に書き換えられたり、消されたりする可能性があるから S(なりすまし )と T(改ざん) が当てはまる」 などとSTRIDEの観点を当てはめていくことで、具体的なリスクを次々と見つけることができました。 私が作ってきたアプリも世の中に公開した場合、こんな風に攻撃者の視点で見られていたのかもしれないと思うと、少しぞっとしました。 ステップ3 :リスクマップで優先順位をつけて対策を考える 多くのリスクを洗い出すと、次に「では、それに対して何をするのか?」を考えます。 しかし、すべてを一度に対策しようとすると、本当に危険なリスクへの対応が遅れてしまう可能性があります。 そこで、 「リスクマップ」 を使い、対策の優先順位を決めました。 研修で説明されたリスクマップ(研修資料から抜粋) これは、 「リスク=発生確率 × 影響度」 という考え方に基づき、「発生しやすさ」と「発生した場合の被害の大きさ」で評価し、優先順位をつけていく手法です。 今回の要件では、タスクの情報などを保存するデータベースに関する攻撃が一番「影響度」が高く、「発生確率」に関しても攻撃しやすい箇所であると考えました。 よって、「データベースに大量の登録処理が行われ、データベースが破壊される」というリスクを「発生確率」「影響度」ともに 5 と設定し、一番優先度が高いものとしました。 このステップを通して、やみくもにすべてのリスクに対策するのではなく、最も危険なリスクから対処するという、現実的かつ合理的な手法を学びました。 ステップ4:+/Δ (プラス/デルタ) で振り返える 各スプリントの最後には、 「+/Δ (プラス/デルタ)」 という手法で振り返りを行いました。 これは、活動の良かった点(プラス)と改善点(デルタ)を出し合う手法です。 「+/Δ (プラス/デルタ)」のアウトプットイメージ(研修資料から抜粋) 最初のスプリントで、私たちはタスクの「追加」「編集」「削除」をそれぞれ別のプロセスとしてDFDに書き出しました。しかし、結果的に3つのプロセスに繋がるデータの流れは、ほぼ同じ内容になってしまったのです。 この結果から、私たちは「 プロセスの粒度をきちんと決めずに書き始めてしまった 」という改善点(デルタ)を見つけ出しました。 この気づきを元に、次のスプリントではまず「データの流れが同じ処理は、一つのプロセスにまとめよう」と決めました。 その結果、DFDの粒度が統一されてプロセスが整理されただけでなく、同じリスクに対する付箋も一つに集約でき、格段に見やすい図に改善することができたのです。 この振り返りがあったことで、今回のスプリントで良かったことは引き続き継続し、改善点は次回のスプリントで意識して行動することができたため、アジャイル開発ととても相性のいい手法だと感じました。 完成後にチームごとに発表 ワークショップの最後にチームごとに作成したDFDをそれぞれ発表しました。 プロセスの粒度の違い、ユーザー権限ごとに表を分けて分析していたりなど、考え方の違いや、ホワイトボード、付箋の使い方が各チームそれぞれ個性があり、全く系統の違うDFDが完成していたので非常に面白かったです。 チームによってアプローチが異なり、多様な視点に触れることができました。 3チームのホワイトボード このワークショップで感じたこと この一連のワークショップを通して、脅威モデリングを行う上で便利なワークフローが多く存在し、それを用いることによって最初からでは気づけなかった脆弱性やリスクの大きさに気づくことができたため、ワークフローの有用性を改めて感じました。 一方で、「フレームワーク通りにやれば安心」という「罠」に陥ってはいけない、という講師の金原さんの言葉が印象に残っています。 最も重要なのは、フレームワークを参考にしつつも、チームや顧客との対話を続け、学び、改善し続けることなのだと実感しました。 脆弱性診断ハンズオン:ハッカーになって攻撃してみた 研修の最後に、実際に脆弱なWebアプリケーション(やられアプリ)に対して攻撃を仕掛けるハンズオンを行いました。 ローカルプロキシ「Burp Suite Community Edition」を使い、ハッカーが実際に脆弱性をどのように見つけ、どのように攻撃するのかを体験しました。 ここでは、代表的な攻撃である SQLインジェクション をどのように体験したかを紹介します。 SQLインジェクションは、アプリケーションが想定しないSQL文を意図的に実行させ、データベースを不正に操作する攻撃です。 (出典)IPA - 1. 安全なウェブサイトの作り方 - 1.1 SQLインジェクション https://www.ipa.go.jp/security/vuln/websecurity/sql.html (2025年7月30日アクセス) 今回は先のワークショップでリスク分析をした「 Bad Todo List 」という脆弱性を持ったアプリケーションで体験しました。 注意⚠️ 本記事で紹介している内容はあくまで学習目的で、特別な許可のもと安全な環境下で実施したものです。 許可なく第三者のウェブサイトやシステムに対して、脆弱性を試すような行為を行うことは法律で固く禁じられておりますので、ご注意ください。 サイトにアクセスしてみると、以下のようなごく普通のTodoアプリの画面が表示されますが、このサイトにはいたるところに脆弱性が潜んでいます。 Bad Todo Listのタスク一覧画面 データベースの情報を抜き取ってみる 脆弱性のある入力フォームに「 パソコン' OR 'a'='a 」と入力してみます。 これを入力すると、プログラムの内部ではSQL文が次のように組み立てられます。 ... AND todo LIKE ' パソコン ' OR ' a ' = ' a ' ; この命令文は、データベースに対して「 Todoに『パソコン』と書かれている 」または(OR)「 『a』と『a』は等しい 」という条件でデータを要求します。 「 'a'='a' 」は常に真(true)なので、結果として「 すべての条件を無視して、データベースにある情報をすべて表示しろ 」という命令に変わってしまうのです。 その結果、以下の画像のように、公開、非公開の設定(画像右側)に関係なくすべてのデータベースの情報が表示され、見えてはいけない重要な情報が見えてしまいます。恐ろしい… Bad Todo ListでSQLインジェクションを検証した結果画面 SQLインジェクションの本当の恐ろしさ 表示されている情報をすべて抜き取れるだけでも十分に恐ろしいですが、SQLインジェクションの危険性は これだけではすみません。 攻撃者は、この脆弱性を利用してさらに悪質な攻撃を仕掛けることができます。 個人情報の窃取 UNION といった特殊な命令を組み合わせることで、Todoリストだけでなく、システムに登録されている全ユーザーのID、メールアドレス、さらにはパスワード情報まで盗み取ることが可能です。 データの改ざんと削除 情報を盗むだけでなく、データベース内の情報を自由に書き換えたり、全てのデータを削除したりすることもできてしまいます。 サーバーの乗っ取り データベースの設定によっては、SQLインジェクションを足がかりに、最終的にWebサーバーそのものを乗っ取られてしまうケースさえあります。 このように、たった一つの入力欄の不備から、システム全体を揺るがす甚大な被害に繋がりかねないのが、SQLインジェクションの本当の恐ろしさなのです。 脆弱性診断ハンズオンで感じたこと このような形で様々な攻撃手法を実践的に学びました。 SQLインジェクションだけでも深刻な被害につながるのに、Webアプリケーションには他にも無数の攻撃経路が潜んでいるという事実に、「一体どうすればこんな攻撃を思いつくのか」と、その発想力に圧倒されるばかりでした。 これまでは「どうすれば動くか」という「作る側」の視点でしかコードを見ていませんでしたが、この研修では「どうすれば壊せるか」という「攻撃される側」の視点を初めて得ることができました。 アウトプット:セキュリティ研修で得た知識を実践へ! ここからは、セキュリティ研修後に私がどのようなアウトプットを行ったかを紹介します。 新卒研修プロジェクトに取り入れる 現在、私たち新卒エンジニアは社内の課題を解決するシステムを開発するという研修に取り組んでいるのですが、そこで今回のワークショップやハンズオンで学んだ対策を盛り込みました。 ①設計の土台である非機能要件に反映 まず、プロジェクトの設計の土台となる非機能要件に、今回の研修で学んだ攻撃手法から実際に作るシステムでどんな脆弱性が考えられるかを洗い出し、それに対する対策を具体的に定義しました。 分類 項目 対策内容 アクセス・利用制限 アプリケーションレベルのアクセス制限 SlackOAuthを通して、許可された利用者(@safie.jpアカウント)のみがシステムを利用できること。 アクセス・利用制限 安全な認証・セッション管理 JWTを利用し、サーバー側で電子署名を検証することでデータの完全性を担保する。 脆弱性対策 SQLインジェクション対策 DBへの問い合わせはプレースホルダを用い、不正なSQL実行を防御する。 脆弱性対策 クロスサイトスクリプティング(XSS)対策 ユーザー入力値をWebページに表示する際は、適切にエスケープ処理を行う。 脆弱性対策 クロスサイトリクエストフォージェリ(CSRF)対策 トークンを用いて、意図したユーザーからのリクエストであることを検証する。 このように開発の初期段階からセキュリティを要件として定義することで、後工程での手戻りを防ぎ、まさにセキュリティ研修で学んだ「シフトレフト」の考え方を実践することができました。 ②作った機能に「攻撃」を仕掛けてみる 次に、定義した非機能要件の一つ、「アプリケーションレベルのアクセス制限」を実装し、実際にテストを行いました。 この機能は、許可されたドメイン(@safie.jp)以外のアカウントではログインできないようにするものです。 研修でお世話になった講師の金原さんに協力をお願いし、脆弱性診断で使った「Burp Suite」を用いて、外部のメールアドレスでログインを試みる擬似的な攻撃を仕掛けてもらいました。 「本当に防げているのだろうか?」という漠然とした不安が、Burp Suiteの画面でどのようなリクエストが飛んできて、どのようにデータが処理され、どのように外部からのメールアドレスがはじかれているかを可視化でき、設計したセキュリティ機能に自信を持つことができました。 実際の検証の様子 まとめ 今回のセキュリティ研修では、座学でのインプットだけでなく、脅威モデリングワークショップや脆弱性診断ハンズオンといった実践的な演習を通して、セキュリティの知識を深く理解することができました。 特に、実際に手を動かして攻撃者の視点を体験したことで、これまで漠然としていた脅威が、具体的なイメージ(勘所)として掴めるようになったのが一番の収穫だと感じています。 この研修で得た学びを活かし、今後の開発業務では開発初期段階から常にセキュリティを意識し、安全なプロダクト作りに貢献していきたいと思います。 25年度新卒研修に関する記事はこちらをご覧ください。 2025年、新卒エンジニア研修はじめました Notion初学者のためのショートカット活用術:業務効率を上げる第一歩 100人をマネジメントした指揮者が 新卒で挑戦した「不確実性」と向き合うチームビルディング 新卒一年目のエンジニアが感じた、プレ開発で見えたチームの“成長” ゼロから学んだフロントエンド実装 毎日の日報報告をワンボタンで ハッカーの視点を身に付ける!新卒が学んだセキュリティ研修 ← 本記事
はじめに こんにちは!セーフィーでサイバーセキュリティを担当している金原です。 以前のブログでは「 セーフィーのサイバーセキュリティ戦略の作り方 」や「 セーフィーのセキュリティチームづくり 」といったテーマで、私たちの取り組みを紹介させていただきました。 今回は、そのサイバーセキュリティ戦略の打ち手の一つである「セキュリティはみんなの仕事」を実現するためのトレーニングの実験として、アジャイル脅威モデリングをベースにした「新卒エンジニア向けのセキュリティ研修」を実施しましたので、その様子をご紹介します。 この記事が、エンジニアとしてセキュリティスキルを高めたい方、社内でのセキュリティ研修を企画されている方、そして脅威モデリングに興味がある方にとって、少しでも参考になれば嬉しいです。 なお、本記事は講師の私目線の記事です。 受講生の新卒エンジニア恩智さんの受講生目線の記事 も公開されてますので、合わせてご確認ください! はじめに 大切にしたいのは「学習定着率」 コンセプトは「アジャイル脅威モデリング」 研修の全体像 アジャイル脅威モデリング(座学)の内容 脅威モデリングワークショップ(グループ討議)の進め方 脆弱性診断ハンズオン(自ら体験する)の進め方 フィードバック(効果検証) アジャイル脅威モデリング(座学)のアンケート結果 脅威モデリングワークショップ(グループ討議)のアンケート結果 脆弱性診断ハンズオンのアンケート結果 まとめ 大切にしたいのは「学習定着率」 皆さんは、研修で学んだ内容がどれくらい身についているか、意識したことはありますか? ラーニングピラミッドによると、ただ講義を聞くだけの研修では、学習内容の定着率はわずか5%だと言われています。せっかくの研修が、それではもったいないですよね。 (出典)キャリア教育ラボ - 平均学習定着率が向上する「ラーニングピラミッド」とは?? https://career-ed-lab.mynavi.jp/career-column/707/ (2025年7月25日アクセス) そこで今回の研修では、学習定着率がより高いとされる「 グループ討議 (50%) 」と「 自ら体験する (75%) 」を活動の中心に据え、学んだ知識がしっかりと身につくように設計しました。 コンセプトは「アジャイル脅威モデリング」 今回の研修のコンセプトは「 アジャイル脅威モデリング 」です。 「なぜセキュリティ研修でアジャイル?」と思われるかもしれません。 その理由は、アジャイル開発が多くのエンジニアにとって学びたいテーマであることに加え、その価値観や原則が、脅威モデリングを効果的に実践するための考え方と非常に親和性が高いからです。例えば、タイムボックス・プランニング・ふりかえりのようなアジャイルのプラクティスを活用することで、 対話を中心とした脅威モデリング を、楽しみながら実践できると考えました。 では、その「脅威モデリング」とは一体何なのでしょうか。 脅威モデリングは、開発するシステムにどのようなセキュリティ上の脅威(=悪いことが起きる可能性)が潜んでいるかを、設計段階で洗い出すためのリスク分析手法です。「起きたら困ることは何か?」という視点でシステムの弱点を探し、事前に対策を講じることを目的とします。 このように、脅威モデリングは設計段階でセキュリティリスクを体系的に分析できるため、開発者が学ぶべきプラクティスとして最適です。「NIST SP800-218(Secure Software Development Framework)」や「OWASP SAMM 2.0」のようなフレームワークでも推奨されており 、これからの開発者にとって必須のスキルと言えるでしょう。 (出典) NIST Special Publication 800-218 https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-218.pdf (2025年7月25日アクセス) OWASP SAMM - About us https://owaspsamm.org/about/ (2025年7月25日アクセス) 研修の全体像 研修は丸一日かけて、以下の3部構成で行いました。 1. アジャイル脅威モデリング(座学) まずはアジャイルと脅威モデリングの基礎をインプット。座学で学んだ知識も、その後のワークで すぐに実践する ことで、定着率の向上を図ります。 2. 脅威モデリングワークショップ(グループ討議) アジャイルのプラクティスを活用しながら脅威モデリングを実践。チームで「 何が起きたら困るか 」など対話しながらリスクを洗い出します。 3. 脆弱性診断ハンズオン(自ら体験する) 実際に「やられサイト」を攻撃し、脆弱性がどのように悪用されるのかを 手を動かして 学びます。 アジャイル脅威モデリング(座学)の内容 座学パートでは、アジャイルと脅威モデリングそれぞれの「Why?」と「What?」を解説し、基礎知識を身に着けてもらいました。 脅威モデリングに関しては、実践するための以下の 4つの重要な問い を中心に進め方を説明しました。 【問1】私たちは何に取り組んでいるか? 【問2】何が問題になりうるか? 【問3】それに対して何をするのか? 【問4】十分にうまくできたか? また、最初の問い「私たちは何に取り組んでいるか?」に対する答えを可視化する DFD(データフロー図)の書き方 は、後のワークショップの質を高めるために、丁寧にレクチャーしました。 座学の様子。皆さん、真剣に説明を聞いてくれました。 脅威モデリングワークショップ(グループ討議)の進め方 知識は、使ってこそ初めて実践的なスキルとなります。 このワークショップでは、座学で学んだ脅威モデリングの進め方を、アジャイルのプラクティスを使いながらグループで実践します。 その目的は、単に手順をなぞるのではなく、対話を通じてリスクを発見する楽しさや、グループで協力して徐々にセキュリティを高めていくという、アジャイルと脅威モデリング双方の価値観や原則をリアルに体験してもらい、その活動やマインドセットを自分たちのものにしてもらうことにあります。 ワークショップは、以下の流れで進めました。 新卒2〜3名と先輩エンジニア1名でグループを組みます。(全3グループ) 各グループはホワイトボードを2枚使います。 ホワイトボードの1枚目は、脅威モデリング(モデル作成→リスクの洗い出し→対策検討)に使います。(【問1】~【問3】に該当) ホワイトボードの2枚目は、ふりかえりの内容を書き出します。(【問4】に該当) DFDはマーカーで書き込み、洗い出したリスクや対策は付箋で貼り付けます。 アジャイルのプラクティスを取り入れ、「 プランニング(10分) → 脅威モデリング(25分) → ふりかえり(10分) 」を1スプリント(45分)のタイムボックスとし、これを3回繰り返します。 脅威モデリングのお題として、分析対象アプリの要件を提示します。 なお、今回のお題は、後の脆弱性診断ハンズオンで用いる「やられアプリ( Bad Todo List )」として、【問1】のDFD作成からスタートしました。 ワークショップの様子。ホワイトボードを囲んで、活発に対話されていました! ホワイトボード1枚目(DFDとリスク) ホワイトボード2枚目(ふりかえり) 最後にそれぞれのグループで成果を発表! 脆弱性診断ハンズオン(自ら体験する)の進め方 このハンズオンの目的は、脅威モデリングで特定したリスクを、 攻撃者の視点で「追体験」する ことにあります。 頭で理解しただけの脅威も、自らの手で攻撃を成功させた瞬間に、生々しい実感へと変わります。この体験こそが、セキュリティを自分ごととして捉え、より安全な設計・開発につなげるための最も効果的な学びです。 具体的には以下のツールを使ってハンズオンを実施しました。 やられサイト : Bad Todo List 攻撃ツール : ローカルプロキシ「Burp Suite Community Edition」 また、取り扱う脆弱性は、IPAの「 安全なウェブサイトの作り方 」で紹介されている主要な脆弱性として、一歩ずつ解説しながら、実際にツールを操作して探してもらいました。 SQLインジェクション OSコマンド・インジェクション ディレクトリ・トラバーサル セッション管理の不備 クロスサイト・スクリプティング(XSS) クロスサイト・リクエスト・フォージェリ(CSRF) これらの脆弱性が、脅威モデリングで学んだ STRIDE のどの脅威カテゴリに繋がるのかをマッピングすると以下のようになります。 脅威と脆弱性のマッピング(研修資料から抜粋) そして、なんと今回のハンズオンには、『体系的に学ぶ安全なWebアプリケーションの作り方』の著者である 徳丸浩先生 に、特別ゲスト(オブザーバー)としてオンラインでご参加いただきました!徳丸先生、本当にありがとうございました! 徳丸浩先生とハンズオン実施中の様子(画像が小さくて見にくいですが、ちゃんと徳丸先生ご本人がいらっしゃいます!) フィードバック(効果検証) 研修のゴールである「 セキュリティの勘所がわかる 」が達成できたかを検証するため、ワークショップで実践したふりかえりの手法「 +/Δ(プラス/デルタ) 」を使ってアンケートを取りました。 研修全体としては、研修のゴールの達成度は「 8.83点 (10点満点)」となりました。初回にしては及第点の効果を得ることができたのではないでしょうか。 研修全体の達成度 以下、それぞれのコンテンツのアンケート結果です。 アジャイル脅威モデリング(座学)のアンケート結果 座学でしたが、想定よりも満足度は高く、目的や手法が理解できたというフィードバックもいただけました。座学は学習定着率に難はありますが、知識の共通理解づくりという点ではワークの前に実施すれば効果が高まることが期待できます。 プラス(よかったこと) 「アジャイルの手法の詳細の説明、それを踏まえたうえでアジャイルを行う際の脅威モデリングについての解説があったため、話の道筋がわかりやすかった」 「知らない内容がほとんどで、かつ分かりやすく勉強になった」 「脅威モデリングがなぜ必要なのかがわかりましたし、DFDなど知らなかったことも知れた」 脅威モデリングワークショップ(グループ討議)のアンケート結果 満足度は非常に高く、ポジティブな意見が多く寄せられました。ワークショップ中は非常に活気があり、タイムボックスを区切って実施したことでメリハリも生まれたと感じています。 プラス(よかったこと) 「座学で学んだ内容をワークでアウトプットすることで、より知識を深められた。各スプリントもあっという間でした!」 「脅威の見つけ方が少しわかったし、単純に楽しかった」 「DFD, リスクマップ, STRIDEのフレームワークを実際に使用して脆弱性について考えることで知識が定着した」 脆弱性診断ハンズオンのアンケート結果 こちらは満足度にばらつきがあり、インプットが多くなりすぎた点が課題として残りました。 プラス(よかったこと) 「テストサイトで脆弱性を見つけていくのが楽しかった」 「事前学習である程度は理解していたが、どうやったら攻撃されるのかを実際に学ぶことができた」 デルタ(改善したいこと) 「もう少し後半、手を動かしながら学びたかった」 「実際に簡単なアプリを作ってから穴を探す形式の方が、実装と紐づきそうだと思った」 まとめ 今回は新卒エンジニア向けに「セキュリティの勘所を掴む」ことをゴールに研修を設計しました。 具体的には、オンラインでの事前学習(今回の記事では説明していませんが)から始まり、座学でのインプット、ワークショップとハンズオンでのアウトプットへと繋がる構成を取りました。アンケート結果からも、この一連の流れが知識の定着に効果的だったと感じています。 積極的に参加してくれた新卒エンジニアの皆さん、ありがとうございました!今回学んだ勘所を、ぜひ実際の業務で活かしてください。(来年も実施します!) もし、読者の皆様の組織でも「セキュリティが他人ごとになっている」と感じているなら、この「アジャイル脅威モデリング」のアプローチが、その文化を変えるきっかけになるかもしれません。本記事が皆様のお役に立てれば幸いです。
こんにちは。25新卒エンジニアの緑川です。 この記事では、私が社内でのルーティンを自動化しようとして、うまくいったりいかなかったことについて紹介します。 目的 先輩に倣う Notionボタン Slackワークフロー NotionボタンからSlackへwebhook送信 失敗 多重投稿の防止 おわりに 目的 セーフィーの開発本部では、メンバーの勤務状況を把握するため、毎日仕事の開始時と終了時に「始業」「終業」のスタンプをSlackチャンネルへ送信するルールがあります。 さらに、我々新卒エンジニアは研修期間中、その日やったことについてまとめた日報をNotionに書き、報告に添付することになっています。 こんな感じ こんな感じ この投稿をするためには、 日報のURLをコピーする Slackに、 :syugyo: と打ち込む 改行して、 日報 と打ち込む 日報 の部分を選択 URLをペースト という、たいしたことはないものの毎日だと微妙に面倒くさい作業を要していました。 この日報報告の投稿を、たとえば、ボタンを一つ押すだけでできたら楽じゃないかと考えました。 先輩に倣う そんなことを1年先輩のトレーナーに相談すると、まさに同じことを去年やったと教えていただきました。やり方は以下の通りです。 GASのウェブアプリを作り、クエリパラメータつきのGETリクエストを送るとその内容がJSONでSlackに送られるようにする Notionの日報テンプレートに、クエリパラメータとして記事のIDと作成者を埋め込んだリンクを自動で作成する関数を置く 関数で生成されたリンクをクリックするとブラウザからGASにリクエストが飛び、それをもとにSlackに情報が送信される Slackで、 {名前} :syugyo: 日報 という投稿を行う sequenceDiagram ユーザー -&gt;&gt; Notion: 記事上の通知リンクをクリック Notion -&gt;&gt; GAS: クエリパラメータ(記事URLなど)を埋め込んだURLで遷移 Note over GAS: クエリパラメータからSlack向けJSONを作成 GAS -&gt;&gt; Slack ワークフロー: JSONを含んだPOSTリクエストを送信 Slack ワークフロー -&gt;&gt; 出退勤連絡チャンネル: 記事リンク付き終業報告を送信 これを拝借(コピペ)すれば目的達成! ですが、それだけだと面白くないので、何かアップデートを加えたいものです。このやり方では、NotionとSlackの仲介役としてGASが使われています。このGASをなくし、SlackとNotionで直接通信することはできないか?と考えました。 Notionボタン Notionでは、ボタンというオブジェクトが使えます。 ボタンが押されると指定した処理が実行可能という便利な機能ですが、その中に Webhook送信 というものがあります。これは、POSTリクエストで指定URLにJSONを送信するというシンプルな動作をします。ここから作成者と記事のIDを送信するように設定することができれば、Slackを直接操作できそうです。 Slackワークフロー ここでSlack側の機能について説明します。今回終業報告の自動投稿に使ったのは、 Slackワークフロー です。これは特定のイベントが起きたときに設定した処理を実行できるという機能です。よって、Webhookを受信したときに、そこに含まれた情報を埋め込んだ投稿を指定チャンネルに送信するように設定します。 今回は、日報のURLを特定するのに必要な記事のID(notion_id)と書いた人の名前(name)を受け取るように設定し、それをメッセージに埋め込むことにします。 手順は以下の通りです。 Slack左下の「その他」から、「自動化」を選択 右上の「+新しいワークフロー」を押す ワークフローの開始条件を「Webhook を使って開始する」 受け取る変数として、notion_idとnameを設定 指定チャンネルに受け取った情報を埋め込んだメッセージを送信するように指定 <ol start=5"> ワークフローのURLをコピーしておく NotionボタンからSlackへwebhook送信 いよいよNotionボタンからSlackへのWebhook送信を試してみます。 まず、日報の記事テンプレートの中にボタンを作成し、日報の記事には自動でボタンが準備されるようにします。そして、以下のようにWebhookの設定を行います。 /button コマンドでボタンを作成する。 下の画像のような設定画面が表示 実行を「Webhook」を送信する」に設定。 送信先のSlackワークフローURLを入力 「コンテンツ」の項目から送信したいページのプロパティを選択 これで完成!実際にボタンを押してSlackに投稿がされるかどうか試してみます。 失敗 しかし、 いくらボタンを押しても何も起きませんでした 。ここで、Notionから送信されるJSONの構造がどうなっているのか気にしていなかったことに気づきました。 調べてみると、Notionから送信されるWebhookに含まれるJSONは、設定した情報以外にも多くのメタデータを含み、階層も複雑になっていることがわかりました。一方、SlackワークフローではJSONの1階層目にあるキーだけを参照できるようでした。 { &quot; notion_id &quot;: &quot; &lt;id&gt; &quot;, &quot; name &quot;: &quot; &lt;name&gt; &quot; } 上は、Slackで受け取れるJSONのイメージです。階層構造はありません。 { &quot; id &quot;: &quot; {ID} &quot;, &quot; timestamp &quot;: &quot; 2025-01-01T07:00:00.001Z &quot;, &quot; workspace_id &quot;: &quot; {ID} &quot;, &quot; workspace_name &quot;: &quot; {ワークスペース名} &quot;, &quot; subscription_id &quot;: &quot; {ID} &quot;, &quot; integration_id &quot;: &quot; {ID} &quot;, &quot; type &quot;: &quot; page.created &quot;, &quot; authors &quot;: [ { &quot; id &quot;: &quot; {ID} &quot;, &quot; type &quot;: &quot; person &quot; } ] , &quot; accessible_by &quot;: [ { &quot; id &quot;: &quot; {ID} &quot;, &quot; type &quot;: &quot; person &quot; } ] , &quot; attempt_number &quot;: 1 , &quot; entity &quot;: { &quot; id &quot;: &quot; {ID} &quot;, &quot; type &quot;: &quot; page &quot; } , &quot; data &quot;: { // この値がほしい &quot; parent &quot;: { &quot; id &quot;: &quot; {ID} &quot;, &quot; type &quot;: &quot; page &quot; } } } 上はNotionから送られるJSONの例です。多くのメタデータが入り、階層構造になっていることがわかります。Slackで受け取りたいNotion記事の情報は data キーの下に入るため、Slackは直接取得することができません。 つまり、NotionボタンとSlackだけの連携は難しいようです。GASを削除するという目論見は潰えてしまいました。 多重投稿の防止 仕方がないので、GASの削除はあきらめ運用しました。お手軽に報告できるのは機能自体は好評でよく使われたものの、日報内をワンクリックするだけで終業報告を送ることができるという手軽さから、一つ問題が発生してしまいました。日報は書いた本人以外も読むことがあるので、その時に誤って通知ボタンをクリックされた結果、その時点で もう一度終業報告が投稿されてしまった のです。しかも、Slackでの送信元が日報を書いたユーザーではなくワークフローのbotになるので、投稿を削除することもできません。これでは困ります。 この問題を解決するために、以下のような方法を考えました。 GASで、記事IDをスプレッドシートに記録する。 リクエストが来るたびに、スプレッドシートを確認し、既存の記事IDだったらSlackへ情報を送らない これで、最初の一回しかSlackに通知が投稿できません。 以下はGASに実装したソースコードの全体です。 // GETリクエストが来たときに実行する関数 const doGet = ( e ) =&gt; { const params = e . parameter const workflow_url = &quot;&lt;SlackワークフローのURL&gt;&quot; ; if ( ! params . id || ! params . name ) return ContentService . createTextOutput ( &quot;パラメータエラー発生: &quot; + e . message ) ; const data = { &quot;notion_id&quot; : params . id , &quot;name&quot; : params . name } if ( is_artice_already_saved ( params . id )) { return ContentService . createTextOutput ( &quot;指定された日報はSlackに投稿済みです &quot; ) ; } save_article_id ( params . id , params . name ) try { UrlFetchApp . fetch ( workflow_url , { method : &quot;POST&quot; , contentType : &quot;application/json&quot; , payload : JSON . stringify ( data ) , }) } catch ( e ){ return ContentService . createTextOutput ( &quot;Slackエラー発生: &quot; + e . message ) ; } return ContentService . createTextOutput ( &quot;退勤を登録しました!&quot; + JSON . stringify ( params )) ; } // IDが登録済みか const is_artice_already_saved = ( id ) =&gt; { var sheet = SpreadsheetApp . getActiveSpreadsheet () . getSheetByName ( 'シート1' ) ; const idIndex = 1 ; // A列 const lastRow = sheet . getLastRow () ; const ids = sheet . getRange ( 1 , idIndex , lastRow , 1 ) . getValues () ; // 各セルの値を入力IDと比較 for ( let i = 0 ; i &lt; ids . length ; i ++ ) { if ( ids [ i ][ 0 ] === id ) { return true ; } } return false ; } // スプレッドシートへの書き込み const save_article_id = ( id , name ) =&gt; { var sheet = SpreadsheetApp . getActiveSpreadsheet () . getSheetByName ( 'シート1' ) ; sheet . appendRow ([ id , name ]) } また、最終的なシーケンス図は以下のようになりました。 sequenceDiagram participant User participant Notion participant GAS participant GoogleSpreadsheet as Google Spreadsheet participant SlackWorkflow as Slackワークフロー participant SlackChannel as 出退勤連絡チャンネル User-&gt;&gt;Notion: 記事上の通知リンクをクリック Notion-&gt;&gt;GAS: クエリパラメータ(記事IDなど)を埋め込んだURLで遷移 Note over GAS: クエリパラメータからSlack向けJSONを作成 GAS-&gt;&gt;GoogleSpreadsheet: 記事IDの存在確認 alt 記事が存在する GoogleSpreadsheet--&gt;&gt;GAS: 記事IDが存在する Note over GAS: 処理を終了 else 記事が存在しない GoogleSpreadsheet--&gt;&gt;GAS: 記事IDが存在しない GAS-&gt;&gt;SlackWorkflow: JSONを含んだPOSTリクエストを送信 SlackWorkflow-&gt;&gt;SlackChannel: 記事リンク付き終業報告を送信 end GASでの処理に、スプレッドシートに記事が記録されているかどうかの条件分岐が追加されました。この対策を行ったあと意図しない終業報告がされる事故は起こっていません。 自動終業報告の様子 自動終業報告の様子 上は、実際に終業報告が行われている様子です。 おわりに 結果的にはほとんど先輩の作ったものの模倣にはなってしまいましたが、このシステムの制作を通じてアプリ間の連携方法について知見を得ることができました。たとえば、同期の間で読んだ本や参加した勉強会についてNotionで紹介する場があるのですが、その更新通知を自動でSlackに送信する仕組みを整備できました。 また、なによりも実際に同期の新卒メンバーに作ったものが毎日使われているのがうれしいです。今後は課題を見つける能力と技術知識をさらに鍛え、ユーザーの課題を解決できるエンジニアになりたいです。 25年度新卒研修に関する記事はこちらをご覧ください。 2025年、新卒エンジニア研修はじめました Notion初学者のためのショートカット活用術:業務効率を上げる第一歩 100人をマネジメントした指揮者が 新卒で挑戦した「不確実性」と向き合うチームビルディング 新卒一年目のエンジニアが感じた、プレ開発で見えたチームの“成長” ゼロから学んだフロントエンド実装 毎日の日報報告をワンボタンで ← 本記事 ハッカーの視点を身に付ける!新卒が学んだセキュリティ研修
はじめに こんにちは。2025年度新卒入社しました川上達也です。 この記事では新卒開発研修の中で個人的に開発したRaspberry Piを使ったアプリについてお話しします。 私のこれまでの開発経験はPythonによるAI開発のみで、バックエンドやサーバーサイドといった概念も考えずに開発していました。 入社後に行う開発研修では新卒エンジニア7人で社内課題を解決するプロダクトを作ります。このプロダクト開発をチームで円滑に行うために、開発を始めるより先に新たな技術領域を身につける必要がありました。 そのためプロダクト開発に先駆けてプレ開発として、「近くでも遠くでも忙しさがわかるアプリ」を作りました。近くにいるときはRaspberry Piの画面で、遠くにいるときはSlackのステータスで「忙しい」、「話しかけてもいいよ」、「お昼寝中」の3つの業務状態を確認できます。 この記事ではプレ開発を通して身につけたフロントエンド開発の成果を伝えようと思います。 はじめに 作ったもの システムの説明 動作の説明 Reactの処理 FastAPIの処理 所感 作ったもの 今回、SlackのステータスをRaspberry Piから更新するアプリを作りました。Raspberry Piに接続したタッチディスプレイから入力を行い、Slackのステータスの更新を行います。 実際に使っている様子は次の画像のようになります。 アプリを起動すると次の画像の左側のようにStartと書かれた状態で画面が表示されます。この状態はSlackのステータスには影響を与えません。 その後3つのボタンを押すとそれぞれのボタンに対応したステータスにSlackとWebクライアントで更新が行われます。 この時にSlackでも更新が行われます。 システムの説明 アプリのユースケース図を示します。 ユーザーがステータスの入力を行うと、そのステータスをWebクライアントで表示します。 そしてシステム構成は次のようになります。 Raspberry Piでユーザーの入力を処理するWebクライアントとSlackサーバーにリクエストを送信してステータスを更新するAPIサーバー両方の機能を持っています。 WebクライアントはReactで、APIサーバーはFastAPIで実装しました。 動作の説明 ここでWebクライアントを実装するReactの処理とSlackステータスの更新を行うFastAPIの処理を説明します。 Reactの処理 入力処理 予め想定してある3つのステータスに合わせたボタンを3つ生成します。それぞれのボタンに onClick イベントと key を設定しておき、押されたボタンに応じたステータス表示とリクエストの送信を行います。 const Status = ["busy", "free", "sleep"] {Status.map((work) =&gt; ( &lt;button className="btn-color" key={work} onClick={() =&gt; onClickButton(work)}&gt; {work} &lt;/button&gt; ))} ステータス表示 useState を使い、押されたボタンの持つ key に対応する画像と文字列をそれぞれ状態として表します。ボタンが押された時に状態の更新が行われ、新たな画像と文字列が状態に設定されます。そして状態が変更されるとレンダリングが実行され、画面に表示されるステータスが変更されます。 const onClickButton = (work:string) =&gt; { const [workStatus, setWorkStatus] = useState&lt;string&gt;("Start"); const [imgPath, setImgPath] = useState&lt;string&gt;(sunImg); setWorkStatus(work) if (work === "busy"){ setImgPath(busyImg) }else if (work === "free"){ setImgPath(freeImg) }else if (work === "sleep"){ setImgPath(sleepImg) } send_request('http://localhost:8000/api/update_status', work) } リクエストの送信 fetch APIを使ってReactからFastAPIへPOSTリクエストを送信します。その際に async/await 構文を使い、非同期処理を実装します。このPOSTリクエストでは押されたボタンに対応する key を送信します。 const send_request = async (req:string, work:string) =&gt; { try { const requestOptions = { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ work: work }) }; fetch(req, requestOptions) .then(response =&gt; response.json()) } catch { console.log("error") } } FastAPIの処理 ステータスのセット FastAPIではまずReactからPOSTリクエストを受け取ります。このPOSTリクエストではユーザーが押したボタンに対応する key を受け取ります。この key を使い、Slackサーバーに送信するための text ステータスと emoji ステータスを設定します。 text ステータスと emoji ステータスをSlackサーバーに送信すると、 text ステータスはそのままステータスの文字として、 emoji ステータスは emoji ステータスに対応した絵文字として、ステータスが更新されます。 from fastapi import FastAPI, Query from slack_sdk import WebClient @app.post('/api/update_status') async def UpdataStatus(data: Work): SLACK_BOT_TOKEN = "xxx" if data.work == "busy": status_text = data.work status_emoji = ":working-in-office:" elif data.work == "sleep": status_text = data.work status_emoji = ":zzz:" elif data.work == "free": status_text = data.work status_emoji = ":good-nya:" リクエストの送信 slack_sdk の WebClient を使って、 text ステータスと emoji ステータスをユーザーの token とともにSlackサーバーへ送信します。Slackサーバーが受け取った token によりユーザーを識別して、ユーザーのステータス更新が行われます。 client = WebClient(token=SLACK_BOT_TOKEN) try: response = client.users_profile_set( profile={ "status_text": status_text, "status_emoji": status_emoji, "status_expiration": 0 }) except Exception as e: print(f"unexpected error: {e}") このようにして更新されたステータスはSlackとWebクライアントで次の画像のように表示されます。 💤 sleepとなっているところがステータスです。SlackでもWebクライアントでも同じステータスを表示することができます。 また更新可能な3つのステータスはそれぞれ次の業務状態を想定しています。 忙しい時・作業に集中したい時 余裕のある時 お昼寝中 所感 このアプリ実装を通して、これまで開発経験のなかったフロントエンドやAPIの実装をすることができました。ReactとFastAPIを連携させましたが、React単体でも実装できるアプリであったのでFastAPIを使う必要はありませんでした。今回は「エンジニアとして成長する」という研修の目的を意識しReactとFastAPIを連携させる構成を選択してより多くの技術に触れました。この構成は、それぞれの技術の役割分担や連携方法を学ぶ上で非常に有益でした。一方でどのような技術選択が最適なのかを考えることも、今後は行わなければならないところではあります。 現在私は開発研修の中でチームのふりかえり会の運営を担当しています。ふりかえり会の一つとしてチームメンバー同士で褒め合うことをしました。その進行方法は、次のように設定しました。 チームメンバーから1人 褒める人 、もう1人 褒められる人 を選択する 1分間 褒める人 が 褒められる人 を褒める 褒められる人 が感想を言う この進行を1ターンとしてチームメンバー全員が 褒める人 と 褒められる人 の両方を担当する1セクションを繰り返します。 この会を盛り上げながら、円滑に行うために次の画像のようなアプリを開発しました。 抽選ボタンを押すと、 褒める人 と 褒められる人 をランダムで抽選します。表示を押すとターンが進行して、抽選された 褒める人 と 褒められる人 が表示されます。 再び抽選ボタンを押すと、 褒める人 と 褒められる人 の抽選が行われセクションが進行します。抽選ボタンを押して行う抽選はバックエンドで実装しました。その際に”すでに行った 褒める人 と 褒められる人 の組み合わせになる抽選は行わない”という制約を持たせました。バックエンドのPythonでこの機能を実装することで、短い時間でアプリを作ることができ、役割分担を考えて技術選定を行えました。 初めてのフロントエンド実装を通して得た学びを糧に新たなアプリを作ることができました。これからもユーザーにとって価値あるプロダクトを開発できるエンジニアを目指して、学習を続けていきます。 また私は入社前に自身の開発経験の少なさを不安に思っていました。現在はこの記事で説明したプレ開発や開発研修での学びを通してエンジニアとしての成長を感じています。この記事を読んで入社後の不安を抱えている学生や就活生の助けになればいいと思います。 25年度新卒研修に関する記事はこちらをご覧ください。 2025年、新卒エンジニア研修はじめました Notion初学者のためのショートカット活用術:業務効率を上げる第一歩 100人をマネジメントした指揮者が 新卒で挑戦した「不確実性」と向き合うチームビルディング 新卒一年目のエンジニアが感じた、プレ開発で見えたチームの“成長” ゼロから学んだフロントエンド実装 毎日の日報報告をワンボタンで ハッカーの視点を身に付ける!新卒が学んだセキュリティ研修
自己紹介 初めまして!セーフィー株式会社 25卒エンジニアの高橋侑歩です。 大学院では機械工学研究科に所属し、ロボットアームと機械学習について研究を行っていました。 「映像から未来をつくる」というセーフィーのビジョンに共感し、25卒のエンジニアとして入社を決意しました。 新卒エンジニア研修 セーフィーの新卒エンジニア研修では、4~7月までの4ヶ月を使って、社内課題を解決するためのプロダクト開発を行ってきました。 今年も7人の新卒エンジニアが1つのチームとしてプロダクト開発を行っていますが、メンバーのバックグラウンドや得意分野がそれぞれ違う中で、技術に対してどうやってキャッチアップしていくかがチームとしての課題だと感じていました。 そこで、アイデアを確定させるための期間としていた5月にプレ開発という形で、簡単なアプリを作成し、メンバーが技術のキャッチアップを行う機会を設けることにしました。 ただ、他の研修と重なり、メンバーの一部が参加できないという状況であったため、全員が参加できるように2回に分け、それぞれ6日間でプレ開発を行いました。 自己紹介 新卒エンジニア研修 プレ開発の目的 ①開発経験を積むための技術のアウトプットを行う場にする ②チームで開発することの難しさと楽しさを体感する プレ開発で行ったこと 開発したもの 開発の過程 プレ開発に参加したメンバーからの評価 プレ開発を終えて感じたチームの成長 まとめ プレ開発の目的 プレ開発は2つの前提のもと、実施することにしました。 1つ目は「開発経験を積むため、技術のアウトプットの場にする」、2つ目は「チームで開発することの難しさと楽しさを体感する」です。 ①開発経験を積むための技術のアウトプットを行う場にする 「メンバーのバックグラウンドや得意分野がそれぞれ違う中で技術に対してどうやってキャッチアップしていくか」はチームの中でも、重要な課題の1つだと感じていました。 また、オンライン学習教材を通して、知識や技術のインプットはできるものの、実際に手を動かして物を作るという経験はできていませんでした。 そこで、オンライン教材で学んだ知識や技術をプレ開発でアウトプットすることで、チームの技術力を向上させることができると考えました。 ②チームで開発することの難しさと楽しさを体感する チームとして何かを成し遂げたことのあるメンバーはいたものの、チームで開発を経験したことのあるメンバーはあまり多くありませんでした。 25卒エンジニア研修の目的としてチーム開発を通してプロダクト思考を学ぶことが掲げられています。プレ開発を通してチームで開発することの難しさと楽しさを経験できればチームとして目的を達成できるのではないかと考えました。 プレ開発で行ったこと ここでは、2回行ったプレ開発のうち、1つを紹介します。 開発したもの プレ開発では、フロントエンド・バックエンド・データベース(DB)の要素があり、6日間である程度形にできる分量のものを開発できるようなテーマである必要がありました。 そこで、「オフィスビルのキッチンカー出店状況を可視化するツールの開発」というテーマでローカルで動くWebアプリの開発を行いました。 セーフィーのオフィスビルでは、毎日1階にキッチンカーが5台ほど出店しています。毎日違うキッチンカーが来るので、なんのキッチンカーが来ていて、何を売っているのかを可視化できたら便利だと考えたためです。 開発の過程 6日間という開発期間の中で、いつまでに何をするのかを明確にするため、ガントチャートを作成して、スケジュールの管理を行いました。 1つのタスクにつき1人の担当者を決め、成果物を共有することでチームとして開発をすることを意識しながら取り組みました。 ミニマムでWebアプリを作るために必要な画面遷移図やシーケンス図、ER図などを作成する際にも、これまでの経験を問わずやってみたいものに挑戦しました。 以下のER図は過去に経験がないメンバーの1人が、プレ開発期間を通してアウトプットしたものです。 プレ開発に参加したメンバーからの評価 プレ開発に参加してくれたメンバーから、プレ開発にアンケートを取り、目的が達成できたかどうかを調査しました。 アンケートでは以下の4つの質問に10段階で評価をしてもらいました。 プレ開発に参加してよかったですか? プレ開発で自身の成長を感じましたか? プレ開発で技術的なアウトプットをすることはできましたか? プレ開発を通して、チームで開発することの難しさと楽しさを体感できましたか? 結果はこのようになりました。(グラフ内の数値はアンケート回答者の平均値) また、プレ開発に関して、以下のようなフィードバックをいただきました。 楽しかったし、やったことがないことができた 短い時間の中で、できる限りのことができましたし、何より参加してとても楽しかった 個人のタスクをやりながら、リーダーとしてチームの進捗を管理できるようになった 作ったものを突き合せないと整合性がとれないということは実感できた 完全に初めてチーム開発を体験したので、もう少しヒントやtipsなどがあると嬉しかった プレ開発の目的としていた「開発経験を積むため、技術のアウトプットの場にする」、「チームで開発することの難しさと楽しさを体感する」という観点では、目的はある程度達成できたのではないかと感じます。 プレ開発を終えて感じたチームの成長 チーム開発経験がない・大学や大学院では全く別の分野に関する研究を行っていたなどバックグラウンドの違うメンバーが、6日間という短い期間の中でWebアプリを開発することができました。 メンバーのそれぞれが興味のある領域に対してのインプットを行い、プレ開発を通してアウトプットすることができるきっかけを作れたのはよかったのではないかと感じます。 また、研修が始まってまだ1ヶ月しか経っていない状況で、今回のプレ開発がメンバー同士の仲を深めるきっかけとなったことも非常に良かった点の1つです。技術的なスキルアップだけでなく、チームとしての結束を強めることができたと感じています。 まとめ 新卒のエンジニア研修の中で、「バックグラウンドや得意分野がそれぞれ違うメンバー同士が、どうやって技術的なキャッチアップしていくか」という課題に対するアプローチを考え、実践しました。結果として、チームメンバーの技術力向上と、チームとしての関係性が向上したという点で、プレ開発を実施してよかったと感じています。 エンジニアの新卒研修の途中ですが、一人前のエンジニアになるために日々精進していきます。 最後までお読みいただきありがとうございました! 25年度新卒研修に関する記事はこちらをご覧ください。 2025年、新卒エンジニア研修はじめました Notion初学者のためのショートカット活用術:業務効率を上げる第一歩 100人をマネジメントした指揮者が 新卒で挑戦した「不確実性」と向き合うチームビルディング 新卒一年目のエンジニアが感じた、プレ開発で見えたチームの“成長” ← 本記事 ゼロから学んだフロントエンド実装 毎日の日報報告をワンボタンで ハッカーの視点を身に付ける!新卒が学んだセキュリティ研修
はじめに こんにちは。法務部 知財グループの永島です。今回は、知財グループ立ち上げから現在までの約3年間を、「1年目」「2年目」「3年目」と時系列で区切り、それぞれのステージでどんな挑戦があったのか、具体的な取り組みを深掘りしていきます! はじめに 【1年目:2022年】ゼロから始まった、手探りの一年 【2年目:2023年】芽を出し始めた知財活動!1人で走り続けた試行錯誤の日々 【3年目:2024年~】新たなステージへ!2人体制で目指す、セーフィー知財の未来 まとめ:3年間で築いたものと、これからの「楽しむ知財」への挑戦 おわりに 【1年目:2022年】ゼロから始まった、手探りの一年 2022年1月、グループリーダーの渡辺がセーフィーに入社し、初代の知財担当としての仕事が始まりました。それは輝かしいスタートというより、まさに「何もない」場所から、何をするべきかを探す日々からの幕開けでした。 最初のミッション:社内ヒアリングと現状把握で見えたリアル 入社直後の1ヶ月は、役員や開発のキーパーソンとの面談を実施しました。そこで皆さんの声に耳を傾けるうち、「知財の重要性は分かるけど、何をしたら…?」「うちの事業で特許って取れるのだろうか?」といった、現場の率直な戸惑いや疑問に触れることができました。 方針決定:セーフィー知財、2つの約束 この現状を踏まえ、知財活動の柱として二つのことを掲げました。 「地に足の着いた知財活動(権利形成)」 :まずは守りを固める! 「知財に強い企業文化を築くこと」 :みんなで知財を強くする! ↑知財に強い企業文化を築くことの重要性を全社員向けの知財研修で伝えました。 土台作り:信頼できるパートナー探しとルール作り まずは、会社の状況を理解し、柔軟に対応いただける特許事務所を探すことから始めました。そして、活動の根幹となる職務発明規程も、特許庁の情報を参考にしながら、本当に手探りで整備を始めました。 発明発掘へGO!:社内に眠る「お宝」を探せ 商品開発会議に参加したり、CTOや新規事業部門の方々と話したり、そして何より「この技術、面白いかも!」と熱意を持つ社員の方々と対話する機会を少しずつ作っていきました。 初の出願と早期審査:「まずやってみる」精神で挑戦 特許出願や他社特許のクリアランス調査もできることから始めました。一人のため、先行技術調査は時間を区切って効率的に行うなど工夫が必要でした。そして、少しでも早く社内に良い報告をしたくて、「早期審査制度」を活用しました。UIデザインなどの意匠出願も、試験的に開始しました! 1年目の成果と手応え:初の特許登録に歓喜! 早期審査の活用もあり、セーフィーにとって記念すべき初の特許登録が実現!これは、開発に携わった社員にとっても大きな自信となり、本当に嬉しい出来事でした。Excelでの暫定的な管理体制や、商標活動もこの年からスタートしています。 【2年目:2023年】芽を出し始めた知財活動!1人で走り続けた試行錯誤の日々 1年目で蒔いた種が、少しずつ芽を出し始めた2年目。引き続き1人体制ではありましたが、活動を少しでも前に進めようと試行錯誤する日々でした。 発明のタネが続々!高まる期待と嬉しい悲鳴 1年目の活動の甲斐もあってか、社内から「こんなアイデアはどうだろう?」と多くの相談をいただけるようになりました。嬉しい反面、一人ではすぐに対応しきれないほどの状況になり、協力してくれる皆さんに感謝するばかりでした。 権利化プロセスの確立と、失敗からの学び 特許、意匠、商標の権利形成活動を続ける中で、早期審査のメリットを実感する一方、自社が既に公開していた情報が先行技術として引用されるという、今となっては苦い、しかし貴重な経験もしました。この経験から、出願前にはIR・広報部門としっかり連携することの重要性を改めて痛感しました。 攻めの知財活動へ:他社調査の本格化と「伝わる」報告 競合だけでなく、投資先やアライアンス先の知財状況も調査をするようになりました。その結果を報告する際も、どうすれば専門外の方に伝わるか、星の数で評価を示したり、図やグラフを用いたりと、試行錯誤の連続でした。投資担当者との対話からは「知財は経営指標の一」という視点を学びました。 社内啓発スタート:「知財を楽しむ」文化の第一歩 「知財をみんなで楽しもう!」をモットーに、分かりやすく、専門用語を避けた参加型の知財研修(入社時必須&任意)をスタート。さらに、半年に一度の「知財レビュー」で実際の事例を紹介し、知財を身近に感じてもらう試みも始まりました。地道に続けてきた知財研修を通じて、開発や企画部門以外にも「知財、面白いね」と言ってくれる方が現れたりもしました。活動が少しずつ社内に根付いているのかもしれないと、ささやかな手応えを感じ始めた時期でした。 ↑知財レビューの資料の一部。特許が登録された際は知財的な観点の特徴を【ココがすごいよ】ポイントとして全社的に展開しています。 1人体制の奮闘と見えてきた「次」への課題 ありがたいことに活動の幅が広がるにつれ、1人体制では限界があることも、正直に感じ始めていました。管理システムの本格導入を検討し始めるなど、将来を見据え、次の一手を考えなければいけない時期に差し掛かっていました。 【3年目:2024年~】新たなステージへ!2人体制で目指す、セーフィー知財の未来 そして、2024年。3年目を迎え、セーフィーの知財部門は新たなステージへと突入します。 組織としての進化:2人体制と理想のチーム像 活動を前に進めるため、組織的な変化もありました。永島がJOINし2人体制となり、渡辺はグループリーダーという役割を担うことになりました。壁打ち相手として、議論を深め戦略を練り上げ部門としての成長を図っています。 目指す未来:リーダーの視点と「攻める知財」 そして、セーフィーの知財部門が目指すのは、単なる「守り」に留まらない知財の未来です。 スタートアップでの挑戦を「実証実験(POC)」と捉え、常に会社の未来を模索しています。その中で、知財が営業活動に貢献できるように、「攻め」にも繋がる活動を続けていきたいです。 新たなステージの証明:相次ぐ外部からの評価 こうした日々の活動の証として、2024年には外部から評価をいただく機会にも恵まれました。 まず、 特許庁の「 「事例から学ぶ 商標活用ガイド」 - ビジネスやるなら、商標だ!- (2024年版) 」に、これまでの取り組みを掲載 していただきました。私たちの活動が、少しでも他の方の参考になるかもしれないというのは、大変光栄なことでした。 (出典)特許庁,商標の活用事例集「事例から学ぶ 商標活用ガイド」 - ビジネスやるなら、商標だ!- (2024年版),セーフィー株式会社「映像から未来をつくる」その想いをロゴに込めて・・・,URL: https://www.jpo.go.jp/support/example/document/trademark_guide2024/guide01.pdf#page=18 (2025年6月9日アクセス) さらに、 関東地方発明表彰で「発明奨励賞」を受賞 するという、望外の機会もいただくことができました。( こちらの記事 で詳しく紹介) この受賞は、開発を進める社員たちの大きな励みになったと感じており、それが何より嬉しかったです。 まとめ:3年間で築いたものと、これからの「楽しむ知財」への挑戦 たった一人でゼロから立ち上げたセーフィーの知財部門。この3年間で、権利形成の基盤を築き、社内の知財意識を高め、そして何よりも「知財を楽しむ」という文化の種を蒔き、育ててきました。 これからも、セーフィーの知財部門が、社員と共に「知財を楽しみながら」、会社の成長を力強くドライブしていく姿に期待してください! おわりに セーフィーではエンジニアを積極的に採用しています。 興味がある方は是非下記サイトを一度覗いてみてください。 https://safie.co.jp/teams/engineering/
こんにちは!25卒エンジニアの夕田です! 「このプロジェクト、本当に終わるのかな?」 エンジニアであれば、誰もが一度はそんな不安に駆られたことがあるのではないでしょうか。 漠然としたゴール、曖昧な要件、先の見えない課題の山……。 まるで霧の中を手探りで進むように、開発の現場には常に「不確実性」がつきまといます。 私自身、学生時代に指揮者として団体を率いてきた経験があります。 音楽の世界も、エンジニアリングとよく似ています。 演奏会という「本番」に向けて、多くのメンバーがそれぞれのパートを練習し、最終的に一つの「作品」を創り上げます。 しかし、楽譜通りに音を出しても良い演奏はできません。 そんな時、指揮者に求められるのは、まさにこの「不確実性」をマネジメントし、チーム全体を最高のパフォーマンスへと導くことです。 私が指揮者として学んだ知見は、形は違えど、エンジニアのチーム開発に応用ができると気付きました。 本記事では、学生時代にオーケストラの指揮者として培った経験と、新卒エンジニアとして現場で得られた学びを融合させ、 両者に共通する不確実性やチームビルディングの重要性 について共有させていただきます。 経歴 不確実性の高い「音楽」をアジャイルで合奏する マエストロがするチームビルディング 1. 開発研修での目標設定:未来像から見出すチームの可能性 2. 5年後になっていたいエンジニア像:個人の夢がチームを動かす 3. 「ito」で深まる信頼関係:遊び心から生まれるチームビルディング 4. 今持っている技術力:強みの可視化で不確実性を減らす おわりに 経歴 広島市立大学にてマンドリン・ギター部に所属し、大学ではマンドリン奏者・指揮者として活動。大学院では指揮者に専念。三大学合同定期演奏会にて50名規模の指揮を担当(第25回三大学合同定期演奏会)。その後、中国学生マンドリン連盟が主催する、中国ブロック定期演奏会の全大学参加大合同ステージにて100名規模の指揮を担当。(第62回中国ブロック定期演奏会 大合同指揮、第64回中国ブロック定期演奏会 大合同指揮) 不確実性の高い「音楽」をアジャイルで合奏する アジャイル開発という概念に触れた際、私は自身の指揮者としての経験との間に共通点があると気が付きました。 それは私が指揮をする合奏練習で、曲の難易度に関わらず行っていた「ルーティーン」です。 私の行う合奏練習は、まさしく アジャイル開発 でした。 具体的には、以下の3つのステップを繰り返していました。 成果物の全体像把握(スプリントレビュー) 練習の冒頭に、楽曲全体を一度通しで演奏します。 これは、現在の成果物(合奏)を「ユーザー」(指揮者である私)に提示し、全体的な品質と方向性を評価するプロセスに相当します。 不確実性の高い要素の優先的解消(バックログの管理、スプリントの実行) 全体像を把握した後、詳細な修正作業へと移行します。 この段階で最も重視したのは、 「特にまずい!!」と思った箇所から優先的にフィードバックを行い、改善に着手する ことです。 例えば、極端なパート間における拍のずれや、オーケストレーションのバランス調整などがあげられます。 このアプローチは、まさにエンジニアリングにおける 「不確実性の高い要素から優先的に処理する」 ことの実践でした。 継続的な改善サイクル(イテレーション) 合奏を通じて得られた具体的なフィードバックを基に、次回の練習(スプリント)でチームが取り組むべき改善点を明確に提示します。 各メンバーはそれを受けて個別の練習に励み、次回の合奏でその成果をアウトプットします。 この継続的なサイクルを繰り返すことで、楽曲の不確実性を段階的に低減させ、最終的に聴衆がストレスフリーに聴くことができる、 最高の演奏という「完成されたプロダクト」 へと昇華させることが可能となるのです。 いかに複雑で不確実性の高い開発課題に直面したとしても、 「フィードバックを積極的に取り入れ、不確実性の高い要素から着実に解消していく」 アプローチは極めて有効です。 完成度を追求する前に、まずは全体像を把握し、課題の根源を特定します。 この原則は、指揮者の曲作りとソフトウェア開発において普遍的に適用されるものであると考えます。 マエストロがするチームビルディング チームビルディングにおいて、私が最も重要視しているのは、単なる情報伝達を超えた「関係性構築」です。 「指揮棒から音は出ません」 私がどれほど熱意を込めた指揮をしても、実際に音を出すのは目の前にいる奏者たちです。 あくまで指揮者は「お願いする」立場です。 私の中にある最高の音楽を作るためには、奏者たちとの間に言葉では表現しきれないほどの信頼関係が不可欠でした。 彼らが私の意図を汲み取り、自らの音で表現してくれるからこそ、一つの音楽が生まれます。 これは、ソフトウェア開発におけるマネジメントにおいてもそのまま当てはまると考えます。 マネージャーがどんなに素晴らしいビジョンや設計を描いても、実際にコードを書き、プロダクトを形にするのはエンジニア一人ひとりです。 まるで指揮棒から音が出ないように、マネージャーが直接コードを書くわけではありません。 どれだけ言葉を尽くしても、メンバーとの間に深い信頼関係がなければ、最高のパフォーマンスは引き出せないと思います。 だからこそ、私はメンバー間の関係性構築、特に 心理的安全性の確保 に積極的に時間を投資しました。 そこで行ったのが「マエストロがするチームビルディング」です。 ここからは具体的な取り組みについて紹介します。 1. 開発研修での目標設定:未来像から見出すチームの可能性 「開発研修が終わったときにどんなエンジニアになっていたいか」という目標を共有しました。 あるメンバーは技術的なリーダーシップと分かりやすい説明を重視し、また別のメンバーはチームへの貢献に重きを置いていると共有しました。そして、私自身はマネジメントと組織開発への関心を示しました。 これらの目標共有は、単に個人の志向を知るだけでなく、チームとしてどのような強みや役割を持つメンバーがいるのかを理解する貴重な機会となりました。 それぞれの目標が明確になることで、不確実な開発課題に対し、誰がどのような視点から貢献できるのかという見通しが立ちやすくなりました。 2. 5年後になっていたいエンジニア像:個人の夢がチームを動かす 「5年後になっていたいエンジニア像」を共有する場では、さらに深い、メンバーの価値観や人生観に触れることができました。 大胆な起業の野望を披露するメンバー、音楽や趣味に対する強い情熱を共有するメンバー、T字型人材を追求するメンバー、その未来像は多岐にわたりました。 ここで行った共有は、メンバーがお互いの多様な価値観を理解し、より深いレベルで人間関係を構築する上で非常に有益でした。 個人の夢や情熱を知ることで、互いへのリスペクトが生まれ、それがチーム内での協調性を自然と高めていくことにつながったと感じています。 3. 「ito」で深まる信頼関係:遊び心から生まれるチームビルディング 💡 ボードゲーム「ito」 1~100までの数字が書かれたカードを使い お互いの数字を言葉で表現し合う協力型パーティゲーム 出典:ArclightGames ito https://arclightgames.jp/product/ito/ (2025/7/10アクセス) 「ito」では、お互いの価値観や感覚を言葉にすることで、それぞれの個性を見ることができました。 ちなみに1時間プレイしましたが、1度もクリアできませんでした。。笑 これは情報が不足している中で、お互いの意図を深く汲み取ろうとしなかったり、あるいは誤解したまま判断してしまったことが原因です。 しかし、この経験を通じて、情報共有の重要性はもちろん、普段の何気ないコミュニケーションがいかに重要かをチーム全員が肌で感じることができました。 加えて、ゲームを通じてメンバーの人間性が垣間見え、それが後の開発における心理的安全性の向上にも繋がったと感じています。 4. 今持っている技術力:強みの可視化で不確実性を減らす 最後に、「学生時代に学んだ今持っている技術力」を共有する場を設けました。各自がこれまで培ってきたスキルや経験を武勇伝形式で発表し、質疑応答を行いました。 Web開発と機械学習をバランス良く習得しているメンバーや、AI領域の専門性を持つメンバー、さらにはアマチュア無線といったユニークなスキルも共有されました。 この技術力共有会は、チーム内でのスキルの「見える化」を促進し、誰がどのような分野に強みを持っているのか、またどのような興味を持っているのかを明確にしました。 これにより、今後の開発において、タスクの割り振りや困った時の相談先が自然と見えてくるようになりました。 それぞれの専門性を知ることで、高め合える関係性が構築され、結果的にプロジェクトの不確実性を減らし、効率的な進行につなげることができたと感じています。 おわりに 本記事では、指揮者としての経験と新卒エンジニアとしての学びから、開発現場に常に存在する「不確実性」、そしてそれを乗り越えるための「チームビルディング」についてお話ししました。 最高のパフォーマンスを出すには、日々のコミュニケーションを通じて心理的安全性を確保することが重要です。 心理的安全性を担保することで、メンバーが安心して意見を交わし、失敗を恐れずにチャレンジできるようになります。 この強固な信頼関係こそが、不確実性を乗り越え、最高の成果を生み出すチームの強力な武器となるでしょう。 最後までご覧いただきありがとうございました! 25年度新卒研修に関する記事はこちらをご覧ください。 2025年、新卒エンジニア研修はじめました Notion初学者のためのショートカット活用術:業務効率を上げる第一歩 100人をマネジメントした指揮者が 新卒で挑戦した「不確実性」と向き合うチームビルディング ← 本記事 新卒一年目のエンジニアが感じた、プレ開発で見えたチームの“成長” ゼロから学んだフロントエンド実装 毎日の日報報告をワンボタンで ハッカーの視点を身に付ける!新卒が学んだセキュリティ研修 .box-d1 { max-width: 500px; margin: 1.5em auto 1em 0; /*左寄せ*/ border: 2px solid #565656; border-radius: 5px; overflow: hidden; } .box-d1 > div { font-size:90%; padding: .5em 1em; color: #ffffff; background:#565656; } .box-d1 li, .box-d1 p { font-size:90%; margin: 0.5em 1em; }
2025年新卒として開発本部に配属されました、恩智太陽です。 現在、新卒エンジニア研修の一環で、社内課題の解決をテーマに、企画から実装まで一貫して取り組む自由度の高いチーム開発に励んでいます。 この研修を通して痛感したのは、Notionのショートカットを使いこなすことによる業務効率の劇的な向上です。 「Notion初学者」である私自身の視点から、これからNotionを使い始める方や、さらに活用したいと考えている皆さんに向けての記事を書くことにしました。 はじめに このテーマを選んだ背景 誰向けの記事? ショートカットキーを習得するメリット ショートカットの入力5パターン ショートカットページの見方 必須ショートカット 見出し(h1, h2, h3) 箇条書きリスト(bullet) 番号付きリスト(num) トグルリスト(toggle) トグル見出し1~3(toggleheading1~3) ここまでのブロックタイプを使うと… 個人的にお勧めするショートカット 区切り線 直前に使った文字色を反映(color) 太字 下線 コールアウト(callout) ここまでのショートカットを使うと… まとめ はじめに このテーマを選んだ背景 セーフィーの研修では、Notion、Slack、Backlogなど、多くのツールに初めて触れる機会がありました。 特にNotionは、学習内容の記録から議事録作成、チームのポータルサイト作成まで、研修期間中に最も頻繁に利用するといっても過言ではないほど活躍の場面が多いツールです。 私自身、Notionに触れるのは大学時代以来でしたが、使っていくうちに「もっと使いこなせたら、さらに業務が楽しく、効率的になるはずだ」と感じるようになりました。その中で、Notionを楽しく、スピーディーに使いこなすための鍵となるのが「ショートカットキー」であることに気づきました。 この発見をNotion初心者の方々に共有したいという思いから、この記事を執筆することにしました。 この記事では「もし入社時にこんなNotionのまとめページがあったら嬉しいな」という私の思いを詰め込んだ内容を書いていこうと思います。 今後入社される新卒の方で、「Notionをほとんど触ったことがない」という方にとって、少しでもお役に立てれば幸いです。 誰向けの記事? Notionをまだ一度も使ったことがない方 基本的な機能は知っているけれど、操作のたびに「ブロックタイプの変換」メニューを探していて、もっと効率的にNotionを使いたいと感じている方 頻繁に使うショートカットキーを一目で見たい方 ショートカットキーを習得するメリット 議事録の効率化と質の向上 インプットの質とスピードの両立 マークダウン記法も取得できる(Notionはマークダウン形式で記述できる、エンジニア向け) 「自分Notion使いこなせてる!まとめるの楽しい!!」という優越感(議事録係が楽しくなります) ショートカットの入力5パターン Notionのショートカットキーは、主にブロックの先頭で特定のキーを入力することで機能します。基本的な使い方として、以下のパターンがあります。 特定の記号 + Space キー (日本語入力時は Enter キー) (マークダウン記法) ブロックの種類を素早く変換できます。 例: # + Space : 見出し1を作成 特定の記号でテキストを囲む (マークダウン記法) 入力中のテキストに直接スタイルを適用できます。 例: **適用したい文章** または __適用したい文章__ : 太字にする Ctrl / Cmd + 特定のキー 文字装飾やページの操作など、様々なアクションを実行できます。 例: Ctrl + B : 選択範囲を太字にする Ctrl / Cmd + Shift + 特定のキー さらに高度なブロック操作や書式設定が可能です。 例: Ctrl + Shift + L : チェックリストを作成 Ctrl + Shift + 数字キー (1-9) : ブロックタイプを特定のタイプに変換(例: Ctrl + Shift + 1 で見出し1) スラッシュコマンド ( / または ; ) によるブロック操作 ブロックの先頭で特定の記号を入力し、続けてコマンドやブロック名を入力すると候補が一覧で表示され、その中からブロックタイプを選択することで、新しいブロックを素早く挿入したり、既存のブロックタイプを変換したりできます。 半角英数入力時: / (スラッシュ) コマンド / に続けて、挿入したいブロック名を入力、または表示された候補から選択し Enter キーで決定 例: /h1 と入力して Enter :見出し1を作成 日本語入力時: ; (セミコロン) コマンド 日本語入力モードのまま ; を入力し、続けて日本語でブロック名を入力すると、 / コマンドと同様に候補が表示され、選択・決定できる スラッシュコマンドの 日本語入力版 例: ;見出し と入力して Enter :見出し1を作成 ※個人的なおすすめと補足 スラッシュコマンド ( / や ; ) は、利用可能なブロックの種類を視覚的に確認しながら直感的に操作できるため、初心者の方には特におすすめです。 一方で、特定の記号と Space キーを組み合わせる、テキストを記号で囲むといったマークダウン記法のショートカット(1,2の方法)、 Ctrl と Shift を使用するショートカット(3,4の方法)に慣れると、よりスピーディーな入力が可能になり、マークダウン記法も覚えられるので個人的には「1~4」のショートカットをお勧めします。 ショートカットページの見方 これからNotionのショートカットを紹介していきます。以下の形式で記載しております。 ブロックタイプ名:ブロックタイプの名前、「;」,「/」を入力した後にブロックタイプ名を入力すると使用することができます(「;見出し1」、「/h1」) ショートカット:ブロックタイプを使用するためのショートカット 見た目:使用した場合のNotion上での表示画面 補足:使用用途など 必須ショートカット まずNotionをまとめるうえで「これがないとNotionじゃない!」というブロック要素のショートカットを紹介します。 見出し(h1, h2, h3) 「#」 + Space (「##」で 見出し2 、「###」で 見出し3 ) 見出しは1~3と種類があり、大きさを変えて使うことができます。 箇条書きリスト(bullet) 「*」「・」「-」「+」のいずれか + Space Ctrl + Shiht + 「5」 箇条書きのところでカーソルを合わせTabキーを押すとネストを深くできます(Shiht+Tabでネストを浅くできる) 議事録ですごく重宝する 番号付きリスト(num) 「数字」 + 「.」 + Space Ctrl + Shift + 「6」 箇条書きリスト同様、Tabキーでネストを変更可能 トグルリスト(toggle) 「>」 + Space 用語の補足や、説明が長くなってしまう場合などに用いる トグル見出し1~3(toggleheading1~3) 「>」 + Space してトグルリスト作成後に、「#」 + Space(「##」,「###」でも可能) 見出しとトグルを合わせたもの 見出しを作成した後に 「>」 + Space でも可能です ここまでのブロックタイプを使うと… ここまで様々なブロックタイプのショートカットを紹介してきました。 実際にこれらのブロックタイプを使うとどれぐらい見やすくなるかを体感していただくために研修中に自分が記述した議事録の文を用いて「ブロックタイプ適応前の文章」と「ブロックタイプ適応後の文章」とで比較していただこうと思います。 以下の議事録は「オフィスに冷凍弁当を供給する冷凍庫を設置し、各ユーザーに適したおすすめの冷凍弁当を選定する機能を通じて、オフィス内で手軽な栄養摂取をサポートするプロダクト案」を練っているときのものです。 ブロックタイプ適応前の議事録 誰が何を話したかを淡々とまとめていているだけで、「いつ話題が切り替わったか」と「何が決まったのか」が全く分からない議事録となっています。 ですが、今まで紹介したショートカットを使って議事録を書いてみると以下のようになります。 ブロックタイプ適用後の議事録 どうでしょうか?先ほどのブロックタイプ適応前の文章に比べて、「いつ話題が切り替わったか」、「何が決まったのか」の点が明確になっているかと思います。これを「ブロックタイプの変換」から要素をいちいち選択していたら会議のスピードに間に合わないですが、ショートカットを覚えることで、スピードを落とさず見やすい分が書けるようになります。 個人的にお勧めするショートカット 次に、自分がよく使うお勧めのショートカットを紹介します。ブロックタイプではないですが、文字を装飾するショートカットも追加しています。 区切り線 「-」を三つ(※全角では変換されません) 下のコンテンツとの差がはっきり認識できるようになり、視認性が向上する 直前に使った文字色を反映(color) Ctrl + Shift + H 文字色で協調すべき部分を目立たせられる 太字 文字を選択後、Ctrl + Shift + 「B」 「 」+ 「太字にしたい文章」 + 「 」(「_」二つでも可) 文字色と同じく、協調すべき部分を目立たせられる(短い単語を強調したいときに使われる) 下線 文字を選択後、Ctrl + Shift + 「U」 協調すべき部分を目立たせられる(多くの文字を強調したいときに使われる) コールアウト( callout ) ※コールアウトに関しては「/callout」で呼び出すしか方法がないです😢 コールアウトの中にブロックタイプを挿入することができ、画像のコールアウトでは「太字」と「区切り線」が使われています。 ここまでのショートカットを使うと… 最後に今まで紹介したショートカットを先ほどの議事録に適用すると以下のようになります。 以下の点が見やすくなったのではないかと思います。 「区切り線」や「下線」を用いて項目の境目を見やすくする 「コールアウト」を用いて多数決や資料の文章を目立たせる 「文字色」で結論がどうなったかを明確にし、決定事項が一目でわかるようにする まとめ 今回の記事では、Notionのショートカットの紹介とその具体的な活用例をご紹介しました。 最後にお見せした議事録の例はブロック要素がかなり多いので、すべて使おうとすると議事録のスピードに間に合わないと思います。 しかし、まずはご自身が「これは便利だ」と感じるショートカットをいくつか選び、会議中や会議後の見直しの際に、特に強調したい箇所や情報の区切りを整えるために活用するだけでも、Notionのドキュメントは格段に見やすく、分かりやすくなるはずです。 Notionには、今回ご紹介しきれなかった便利なブロックタイプやショートカットがまだまだたくさんあります。本記事をきっかけにNotionのショートカットの可能性に気づいていただけたなら、ぜひご自身でも色々と検索して活用してみてください。 ここで挙げきれなかったブロックタイプ、ショートカットはたくさんあるので、今回の記事でNotionのショートカットに興味をもっていただけた方は、是非ご自身で調べてみてください! この記事が、皆さんのNotion活用を後押しし、日々の業務効率向上に少しでもお役立てできれば幸いです。 25年度新卒研修に関する記事はこちらをご覧ください。 2025年、新卒エンジニア研修はじめました Notion初学者のためのショートカット活用術:業務効率を上げる第一歩 ← 本記事 100人をマネジメントした指揮者が 新卒で挑戦した「不確実性」と向き合うチームビルディング 新卒一年目のエンジニアが感じた、プレ開発で見えたチームの“成長” ゼロから学んだフロントエンド実装 毎日の日報報告をワンボタンで ハッカーの視点を身に付ける!新卒が学んだセキュリティ研修
はじめに こんにちは!2025年4月にセーフィーに新卒として入社した竹田です。私は25卒内定者として約10か月間、企画本部 AIソリューション部でインターンをしていました。ビジネス職として採用され、APIという単語も知らなかった私が、奮闘しながらシステム開発を行ったのでこのたび記事にさせていただくことにしました。 今回は「映像×生成AI」を使ったオフィス5S管理ツールについて紹介させていただきます。 はじめに なぜつくろうと思ったのか?~総務の課題見つけちゃったの巻~ どんなものをつくったのか~血と汗と涙(?)の結晶の巻~ どうやってつくったの?~いばらの道を振り返るの巻~ ステップ① 静止画取得 ①-1:設定ファイルからデバイス情報を取得する ①-2:Safie APIからの画像取得 ①-3:画像の保存と更新 ステップ②:LLMのBot作成 ステップ③:LLMのAPI連携 ステップ④:画面作成(UI作成) 完成したツールのその後~つくってどうだったの?の巻~ 今後の可能性~未来に羽ばたけの巻~ 取り組みを終えて~ちょっと成長しましたの巻~ なぜつくろうと思ったのか?~総務の課題見つけちゃったの巻~ 弊社は事業拡大に伴い社員数が急増し、オフィス環境の適切な管理が課題となっています。特に総務部門において、大きく2つの問題が顕在化してきていました。 まず第一に、社員数の増加に比例して総務部門の業務負担が著しく増大していたことです。日々のオフィス巡回、整理整頓の確認、備品管理など、5S(整理・整頓・清掃・清潔・躾)に関わる業務量が従来の管理体制では対応しきれない状況となっていました。 第二に、オフィスの拡張に伴い設置された多数の防犯カメラからの映像確認作業が煩雑化してきたことです。複数フロアに渡る数十台のカメラ映像を目視で確認する作業は、人的リソースの観点から非効率であり、重要な異常を見落とすリスクも高まっていました。 現状のオペレーションと課題に関する詳細を以下の図に示します。 このような状況を打開するため、LLM(大規模言語モデル)の活用が最適であると判断しました。LLMを活用することでカメラ映像からの環境異常の自動検出だけでなく、状況に応じた改善提案の生成など、従来のオフィス管理で顕在化していた負担を軽減できると考えたからです。 上記の背景から、LLMを活用した効率的なオフィス5S管理ツールのシステム開発を行うことにしました。本ツールは総務部門の負担軽減と管理品質の向上を同時に実現することを目指しています。 この時の構想としては上の図のような複数台のデバイスに対して一度に質問ができる仕組みがあれば便利そうだという構想を練っていました。質問に対して見る必要のあるデバイスの情報のみが結果として返ってくれば、大幅に目視の手間を省けると考えました。 どんなものをつくったのか~血と汗と涙(?)の結晶の巻~ 早速ですが、最終的に作ったツールを紹介させてください。 ツールの機能は大きく3つあります。 1.分析するデバイス(カメラ)を選択する機能 どのデバイスから情報を取得するかを決めるために設定ファイル(簡単なJSONファイル)を用いています。この設定ファイルには『デバイスID』と『エリア名』が書かれていて、このファイルを編集するだけで、別のデバイスから情報を取得する場合でも簡単に切り替えることができます。 2.分析に使う画像を更新する機能 分析にリアルタイムの画像を使用したい場合、「画像を更新」ボタンを押すだけで5S分析用の最新静止画を自動的に取得できます。取得した画像は「取得画像一覧」に表示されるため、簡単に確認できます。 3.テキストで質問を送って回答を取得できる機能 この機能では、利用者の目的に合わせてテキストで質問ができます。質問に対しては、該当するデバイスの画像、AIによる解説、該当の映像までのリンクが表示されます。視覚情報とAIの説明で、効率よく問題を解決できるという特徴があります。 どうやってつくったの?~いばらの道を振り返るの巻~ さて、ここからは実際に作っていく過程をお話しできればと思います。 システムの構成は以下のようになっています。 このシステムは、利用者が入力した質問と各デバイスから取得した静止画を組み合わせてLLMに送信します。LLMはその情報を処理して回答を生成し、システムはその回答をツールの仕様に合わせて整理してから利用者に表示します。つまり、質問と画像を入力として受け取り、AIが処理した結果を見やすい形で出力する仕組みになっています。 こちらはGradioで実装しており、作成時点ではローカル環境で実行しています。 ここから実際に作成した手順に合わせて、工程を説明します。 ステップ① 静止画取得 ①-1 設定ファイルからデバイス情報を取得する ①-2 Safie APIからの画像取得 ①-3 画像の保存と更新 ステップ② LLMのBot作成 ステップ③ LLMのAPI連携 ステップ④ 画面作成(UI作成) ここでは、各ステップに対して、前半が作業内容(🧑‍💻)、後半が私の感想(🗣️)という構成で書かせていただいています。 ステップ① 静止画取得 まず初めに取り組んだのは各カメラから分析用の静止画を取得する作業です。 システム構成図のこの部分ですね。 こちらは3Stepで作業を実施しました。 ①-1:設定ファイルからデバイス情報を取得する 🧑‍💻)どのカメラから情報を取得するかは、管理者が変更しやすいように、JSON形式の設定ファイルで管理するようにしました。このファイルには、各カメラのIDと、オフィス内のどのエリアを映しているかの名前が紐付けられています。アプリ起動時や設定変更時にこのファイルを読み込み、連携するカメラを特定します。 書いたコードがこちら def load_device_info_from_json (): &quot;&quot;&quot; デバイスファイルからデバイスID、エリア名のリストを読み込む関数 Returns: list: (device_id, area_name) のタプルのリスト。エラー時は空リストを返す &quot;&quot;&quot; try : config = load_config() with open (config[ &quot;device_file_path&quot; ], 'r' , encoding= 'utf-8' ) as file : devices = json.load( file ) # データ構造が期待通りかチェック if not isinstance (devices, list ): raise ValueError ( &quot;JSONデータがリストではありません&quot; ) device_info = [ (device[ 'device_id' ], device[ 'area_name' ]) for device in devices if 'device_id' in device and 'area_name' in device ] return device_info jsonファイルの中身は以下のような形式になっております [ { &quot; device_id &quot;: &quot; xxxx &quot;, &quot; area_name &quot;: &quot; yyyy &quot; } , { &quot; device_id &quot;: &quot; zzzz &quot;, &quot; area_name &quot;: &quot; wwww &quot; } ] 🗣️)ここまでは順調な滑り出しで、コードを書くのは初めてでしたが楽しんでできていました。 「よっしゃーこのままいくぞー」と意気込んでいたのもつかの間、トントン拍子にことが進むわけではありませんでした。 ①-2:Safie APIからの画像取得 🧑‍💻)実際にカメラの画像を取得する部分の作成を行いました。ここでは、Safie APIを用いて、各カメラの静止画を指定した時刻でリクエストします。取得した画像データに対して、どのカメラのものか、どのエリアのものかといった情報も付与し、一緒に保存します。静止画取得の詳細は こちら のリファレンスをご参考ください。 静止画取得のSafie APIは以前に勉強会でコードを見たことがあったので、それを参考に書き始めました。「リファレンスを見て1から書く」ではなかったので、完全停止することなく進めることができました。 🗣️)ここでは忍耐力が大切だと気付きました。最初は意図したタイミングで画像が取得できないことがありました。画像らしきものは入っていたものの、No Imageといった形で中身が空だったり、意図した時間と違う時間の画像が取れたりと苦戦しました。何度か繰り返すと取得自体は問題なくできたので、この時点でもまだ余裕な気持ちがありました。 ①-3:画像の保存と更新 🧑‍💻)取得した画像は一時的にアプリ内に保存されるようにしました。これは「質問を送るたびに画像取得のAPIが走る」という不要なアクセスを減らし、サーバ側の負荷を軽くすることで、アプリケーションの応答性を向上させることが目的です。(リセットボタンを押すまでは、異なる質問を送っても同じ画像に対して分析が行われるようになっています) とはいえ、リアルタイム画像を分析したいという要望もあると思うので、ユーザーの指示によって最新の情報に更新できる動作も加えました。 🗣️)ステップ①で最も大変だったのがこの①-3の部分です。 「画像を更新」ボタンを押してもエラーが返ってきたり、うまく画像が保存されていなかったりと、「どこが間違ってるんだこれ」と思うことも増えました。そこで調べながら試行錯誤を繰り返すうちに、プログラムの間違い探しと修正、いわゆるデバッグ作業をどのように行っていくかが分かってきました。一気にやろうとしすぎずに細かく分けて考える方が自分には合っているなと思いました。この時ほふく前進くらいのスピードですが、まだ前には進めている実感がありました。 ステップ②:LLMのBot作成 次にLLMとの連携の部分の説明をさせていただきます。システム構成の以下の部分です。 🧑‍💻)今回、質問文と画像を組み合わせて分析して回答を出してくれる存在としてLLMを使用しています。 LLMはBedrockを使いました。こちらはAWSが提供する生成AIアプリケーション開発用のサービスです。主要なAI企業やAmazonが提供する高パフォーマンスな基盤モデルを、API経由で利用できます。 まず最初に行ったのは、今回私が使うツール専用のBotを作成することです。 メニューの「ボットコンソール」を開き、「ボットを新規作成」を選択します 次にボットの基本情報を設定します。必要な情報は以下の3つ(ボット名、説明文、インストラクション)です。 今回はシンプルに、ボット名を「Office5S」、説明文を「オフィス5S可視化くん」、インストラクションには「あなたは5S分析のプロフェッショナルだ」といった内容を入れました。 ボットを作成すると、作成したBotのAPIエンドポイント、APIキーを取得することができます。これはBotを使うために必要となる情報です。 最後にアクセスを許可するクライアントのオリジンを入力したら準備完了です! ボットの作成が完了したら、それを動かすためのコードを書く必要があります。 🗣️)このBotをつくる作業は特段大変なことはなかったものの、プラグラムを書く上で重要なことを1つ学びました。それはBotのAPIエンドポイント、APIキーなどの重要な情報は直接書かないという点です。当たり前じゃんと思った方、その通りです。その通りなのですが、私はそんな当たり前を知らず、最初はプログラムにそのままAPIキーやエンドポイントを書いてしまっていました…。 現在はenvファイルに入れ、そのファイルを読み込むような仕様にしたのでご安心ください。(先ほどのSafie APIのキーもこちらに入っています) ステップ③:LLMのAPI連携 🧑‍💻)Botを作成して次に行ったのが、Botと今回のツールをAPI連携することです。リファレンスはAWSが出している書き方をもとにつくった社内用のものを使いました。 まず、いきなり本体ツールにコードを組み込むのではなく、API連携が成功しているかを試す軽めのテストをしました。テストに使用したコードはこちらです。 import boto3 from botocore.exceptions import ClientError # AWS認証情報(セキュリティ上の理由で環境変数やAWS CLI設定を推奨) AWS_ACCESS_KEY_ID = &quot;XXXXXXXXXXXXXXXX&quot; AWS_SECRET_ACCESS_KEY = &quot;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&quot; AWS_REGION = &quot;us-east-1&quot; # 使用するリージョン # Bedrock Runtimeクライアントを作成 client = boto3.client( &quot;bedrock-runtime&quot; , region_name=AWS_REGION, aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY, ) # 使用するモデルID(Claude 3 Haikuなど) model_id = &quot;anthropic.claude-3-haiku-20240307-v1:0&quot; # ユーザーメッセージを定義 user_message = &quot;こんにちは、今日の天気はどうですか?&quot; conversation = [ { &quot;role&quot; : &quot;user&quot; , &quot;content&quot; : [{ &quot;text&quot; : user_message}], } ] try : # Converse APIを使用してメッセージを送信 response = client.converse( modelId=model_id, messages=conversation, inferenceConfig={ &quot;maxTokens&quot; : 512 , &quot;temperature&quot; : 0.5 , &quot;topP&quot; : 0.9 }, ) # conversation_idを取得して表示 conversation_id = response.get( &quot;conversationId&quot; ) if conversation_id: print (f &quot;Conversation ID: {conversation_id}&quot; ) else : print ( &quot;Conversation IDを取得できませんでした。&quot; ) except (ClientError, Exception ) as e: print (f &quot;エラー: {e}&quot; ) ここで大体どんな関数を使い、どのような形でコードを書いていけばうまくAPIが動作するのかを把握することができました。そして、そのコードをもとに質問文と画像を同時にBedrockに投げて分析してもらえるような機能を実装しました。 🗣️)今回の取り組みで一番の山場がこのBedrockのAPI連携です。 あたかも簡単にできたみたいな書き方をしていますが、まずリファレンスを見て最初に思ったことは「何が書いてあるんだこれ」でした。Safie APIのリファレンスはこれまで何回か見たことがあり、意味が分からず固まるということがなかったのですが、今回のBedrockのAPIは初見では何が書いてあるのかほぼわかりませんでした。リファレンスを見て1からコードを書くということが初めてだったのです。そんな困ったときはまずChatGPT先生の出番です。「このリファレンス通りにコード書いて」と指示を送り、出てきたコードを実行してみましたが、結果はAPIエラーと返ってきてしまいました。 エラーを繰り返していくとさすがに自分が行き詰まっていることを感じました。その後開発の方に質問を投げましたが、いただいた回答は「リファレンス通りになっていない」というものでした。「え?これってリファレンスに沿えてないんだ」と思うと同時に今の理解では前進できないと感じ、トレーナーにヘルプを出しました。 一緒に考えてくださり、テスト用のコードをつくっていただきました。これがなければ前に進めていなかったと思います(感謝) 次の質問文と画像を同時にBedrockに投げて分析してもらえるような機能を実装していく過程も非常に大変でした。「一番整理されていないのはどこ?」といった質問を送って回答が返ってくるようになったので「できました!」とトレーナーに自信満々に見せたところ、画像が送られておらず質問に対してそれっぽい回答と解説をただ返してくれる謎ツールが誕生したのはいい思い出です。そんな紆余曲折あり、いろいろな人の力も貸していただき最大の山場を乗り越えることができました。 ステップ④:画面作成(UI作成) 🧑‍💻)UIの作成にはGradioを使いました。Gradioとは機械学習モデルのデモを行うWebアプリケーションを簡単に作ることができるPythonのライブラリです。詳細は こちら をご確認ください。 まず、説明を割愛していましたが、事前に「こんな感じのものをつくりたい」という仕様書を作成していました。それを実際の利用者となる総務の方に見せて意見をいただき、画面構成のすり合わせを行いました。そこで完成した仕様書のイメージに沿うようにコードを書き画面を作成していきました。 いざ完成してみると「やっぱりこっちのほうがいいかも」としっくりこないことも多発し、最終的に5パターンほど検討して今の配置にたどり着きました。 以下が実際のコードの一部の例になります。より細かい部分は用意したCSSファイルを読み込ませて設定しました。 # メインコンテンツエリア with gr.Row(elem_classes= &quot;main-row&quot; ): # 左側カラム(質問入力エリア) with gr.Column(scale= 3 ): with gr.Group(elem_classes= &quot;content-section&quot; ): # 質問入力セクション with gr.Group(): text_input = gr.Textbox( label= &quot;質問テキスト&quot; , placeholder= &quot;ここに質問を入力してください&quot; , lines= 4.7 , max_lines= 20 ) with gr.Group(): with gr.Row(): reset_btn = gr.Button( &quot;会話をリセット&quot; , size= &quot;lg&quot; , elem_classes= &quot;custom-button half-width reset-button&quot; ) analyze_btn = gr.Button( &quot;送信&quot; , size= &quot;lg&quot; , elem_classes= &quot;custom-button half-width&quot; ) 🗣️)ここで面白かったのは、総務の方の「こんなのが欲しい」という意見に対して実現の可否も含めて調整していく過程です。「このやり方なら難しいけどこっちなら希望は叶えられるのでは?」と自分の中で選択肢を吟味して「それいいね」と言っていただいたときは嬉しかったのを覚えています。そしてイメージ通りの画面が作成できた時は非常に達成感がありました。 完成したツールのその後~つくってどうだったの?の巻~ 作成したツールを総務の方に見せたところ、非常に良い反応をいただくことができました! デモも試していただき、使いやすいという声もいただきました。現在はローカルではなく、セキュアな環境での運用に向けて開発の方を巻き込む動きをしていただいています。 今後の可能性~未来に羽ばたけの巻~ こちらのツールはLLM活用の最初の一歩としてシンプルな機能になっていますが、今後の可能性として幅広い展開が考えられます。今回はその中から2パターン紹介します。 5Sツールとしての強みを尖らせる方向 現在の評価は 5S項目のどこに問題があるのかわかりにくい仕様になっていますが、各5S項目を数値化して定量的な評価を導入すれば、より総務の方にとって使いやすいツールに改善できるのではと考えています。 既存のテキスト指示機能を応用する方向 Safie Viewer 上に質問用チャットを開けるボタンを設置することで、カメラ映像を目視する時間を減らし、効率的な映像分析が実現できるのではと考えています。 取り組みを終えて~ちょっと成長しましたの巻~ 今回このツールの作成を振り返ってみると、最初に率直に思い浮かんだ感想は「難しかったなあ...」というものでした笑。 プログラミングやコーディングの知識がまったくのゼロからのスタートだったため、予想以上に多くの壁にぶつかり、挫折しそうになる場面がありました。特に初期段階では、専門用語の理解すらままならず、基本的な概念を把握するだけでも時間がかかってしまっていました。 しかし、日々の継続的な学習と実践を通じて、少しずつではありますが確実に進歩を感じられるようになりました。最初は断片的だった知識が次第につながり、システム全体の構造や各コンポーネントの関係性を理解できるようになっていきました。 特に学べたと思うのは、コードを単に「動かす」だけでなく、その「中身」や「仕組み」を理解できるようになったことです。なぜこの関数がここで必要なのか、このパラメータがどのような役割を果たしているのかといった点まで考えられるようになりました。 そして何より、「次はこんな機能が実装できるのではないか」、「このようなツールがあれば業務がもっと効率化できるだろう」といったアイデアが浮かんだときに、それを実現するための具体的な手段や方法をおぼろげながらも構想できるようになったことが、今回の取り組みで得られた最大の収穫だと感じています。 まだまだ学ぶべきことは山積みですが、今回の経験を足がかりに、今後も新たな挑戦へ挑んでいきたいと思います。
1. はじめに:プロジェクトマネージャーに挑戦するか悩んでいる人へ みなさん、こんにちは。2024年9月にセーフィーに入社して、ハードウェア開発プロジェクトマネージャー(以下、HW開発PM)として働いている松岡です。 それまでは新卒からメカエンジニアとして働いており、セーフィーでHW開発PMに挑戦しましたので、その挑戦の振り返りを記します。 セーフィーのHW開発PMに興味はあるものの、募集要項を見て「自分にはまだ早いかも」「ハードルが高いな」と感じたことがある方もいるかもしれません。 実は私自身も、PMとしての十分な経験がない状態でセーフィーに応募しました。 このブログでは、経験が浅い立場からでもチャレンジできた私の実体験をお伝えします。 1. はじめに:プロジェクトマネージャーに挑戦するか悩んでいる人へ セーフィーのHW開発PMの担当業務とは 2. セーフィー入社前について 私の経歴 転職で実現したかったこと 募集要項のどこにハードルを感じていたのか 3. セーフィーに入社して 入社前の不安、実際にどうだったか 活きた経験 感じたこと 4. 振り返り(9ヶ月働いてみて) 開発PMとして ハードウェアエンジニアとして 5. この先に目指す姿 開発PMとして ハードウェアエンジニアとして その先のキャリアとして 6. おわりに セーフィーのHW開発PMの担当業務とは HW開発PMという職種は、 開発PMとハードウェアエンジニアの両方の役割を担う職種 になります。 ハードウェアエンジニア 開発パートナーと連携して、設計、性能評価、信頼性試験、法規制対応、量産品質の確保までハードウェア開発を推進する 開発PM プロジェクト全体の開発計画を策定し、品質、コスト、納期、リスクなどを管理しながら、開発パートナーや社内のエンジニア、関係部署などと連携して、プロジェクトの目標を達成できるように推進する 2. セーフィー入社前について 私の経歴 私は、これまでのキャリアで2つの会社を経験してきました。 1社目 電機メーカー :R&amp;D部門に所属し、新製品開発に携わっていました。しかし、最後まで製品を作り上げられずに終わり、 「自分の手で製品やサービスを世に送り出したい」 という強い思いから転職を決意しました。 2社目 IT会社 :IoT機器開発を行い、サービス化まで実現するという目標は達成できました。しかし、品質問題によるメンテナンス作業での出張が多くなり、今度は 「品質を意識した製品やサービスを作り上げたい」 という新たな課題意識が芽生え、再度転職を考えるようになりました。 転職で実現したかったこと 前職までの経験を通じて、私は自身のスキル不足を感じ、以下の2点を身につけたいと強く思うようになりました。 ハードウェアエンジニアとして、品質を意識した製品開発を行える人材になりたい ビジネス視点を持ったマネジメント人材になりたい 転職先を調査している際に、セーフィーのHW開発PM職がまさに私の思い描くキャリアにピッタリだと感じました。しかし、同時に募集要項に不安を覚えたのも事実です。 募集要項のどこにハードルを感じていたのか 特にハードルだと感じたのは以下の点でした プロジェクトマネージャー職への不安 役割と責任 :プレイヤーとは異なり、プロジェクトを成功に導くために具体的に何をすればよいのか分からず、漠然とした不安 成果 :これまで経験のない業務で本当に成果を出せるのかという不安 開発メンバーのマネジメント :ソフトウェアエンジニアを含むチームをマネジメントできるかという不安 スキルや知識、経験への不安 具体的なマネジメント手法を知らないこと ソフト開発の理解不足 ハードウェアエンジニアとしての不安 セーフィー製品における法規制対応や量産品質の確保といった特定の専門分野における知識や経験の少なさ そのハードルを乗り越えられそうと思ったのはなぜか そんな不安を抱えつつも、セーフィーへの応募を決断し、最終的に入社できたのは、選考プロセスの中で不安が払拭されていったからです。 面接前のカジュアル面談 :この職種は、エンジニアスキルも重要だが、PM職としての業務に重きを置いていると説明していただき、自分の目指す方向性と一致していることを確認できました。 面接後にメンバーとの入社前カジュアル面談 :面接前カジュアル面談と比べて、より具体的な業務内容やオンボーディング内容に関する詳細な説明を受け、入社後のイメージが明確になり、不安が払拭されました。 3. セーフィーに入社して 入社前の不安、実際にどうだったか 意外とスムーズに適応できたこと 「ソフトウェア開発の知識がない中で開発メンバーをマネジメントできるか」という点については、入社後、想像以上にソフトウェアエンジニアとの連携が密接であることが分かり、そこまで大きな壁ではないと感じています。ソフトウェア開発の深い知識がなくても、PMとして必要なコミュニケーション能力や課題解決能力があれば、円滑な連携が可能です。また、私自身セーフィー独自のファームウェア SafieClient に触れ、ビルドしてテストすることでソフトウェア開発の理解を進めております。周囲には相談しやすいメンバーがいて、疑問点があればすぐに相談できる環境が整っています。 やはり直面した課題、得られた手厚いサポート HW開発PMは非常に幅広い知識と経験が求められる領域だと実感しています。前職では他の部署が担当していたような業務も自分の役割に入ってくるため、経験が少ない分野では戸惑うことも少なくありません。 しかし、社内では、こうした未経験分野のサポートや育成に非常に力を入れています。単に業務を割り振るだけでなく、 「どうすればできるようになるか」を一緒に考えてくれる文化がある ため、安心して挑戦できています。また、オンボーディング面でもサポートが手厚く、開発PMとして、ハードウェアエンジニアとして必要な知識やスキルを身に付けるためのプログラムを用意して頂き、少しずつ担当領域が広がっていることを実感しています。 活きた経験 前職までに培ったメカエンジニアとしての経験は非常に活きていると感じます。特に「ハードウェアの設計・評価に関する経験」と「製造現場での問題解決経験」は、ODMベンダーとの技術的な議論において、相手の言っていることの本質を理解し、的確な質問や指示を出す上で大きな助けになっています。 感じたこと HW開発PMのチームは社内では比較的新しく、現在も発展途上にあるため、私たちは業務手順書の整備を積極的に進めています。私自身もその一端を担い、各メンバーが培ってきた豊富な知識を手順書に落とし込む作業に携わっています。 特に、私がメカエンジニアとして培ってきた経験は、この手順書作成において大いに役立っており、メンバーと活発に議論しながらより良いものを作り上げています。これは、私と同じように経験が浅い方がスムーズにチームに加わり、活躍できる環境を皆で協力して築いている証でもあります。 4. 振り返り(9ヶ月働いてみて) 入社から9ヶ月が経ち、開発PMとしてもハードウェアエンジニアとしても多くの学びがありました。 開発PMとして 身についたこと : 限られたリソースで目標を達成するというコミットメント意識 が身につきました。 成長を実感したこと :メンバーのサポートのもと、計画通りにプロジェクトを推進し、販売を開始することができました。今後は、オンボーディングで得た知識の実践と、メンバーのスキルを吸収し、 独り立ち を目指します。 出来ていないこと :前任者から引き継いだプロジェクトでは、コストを考慮した仕様策定やODMメーカー選定といったプロジェクト序盤の作業に携わる機会がありませんでした。次回のプロジェクトでこれらを実践し、身につけていきます。 ハードウェアエンジニアとして 身についたこと : ユーザー体験を、品質を意識して開発すること ができました。 成長を実感したこと :品質を意識した開発品の評価、評価レポート作成、評価レビュー会を主導し、商品化プロセスのマイルストーンを達成できたことは大きな自信になりました。 出来ていないこと :開発製品における認証や規制に関するODMメーカーとのやり取りは、まだメンバー頼みになっていました。オンボーディングでの学習や、他製品の実施事例を参考に、次回のプロジェクトでは自ら実践して身につけます。 5. この先に目指す姿 開発PMとして 私は、まず「プロジェクトを完遂できるPM」を目指します。計画通りにプロジェクトを進め、目標とする品質・コスト・納期で成果物を完成させ、プロジェクトを無事にクローズできるPMになることです。 ハードウェアエンジニアとして ハードウェアエンジニアとしては、自身の専門性を活かして技術課題を自律的に解決し、周囲と連携しながら品質・コスト・納期を意識してプロダクトの成功に貢献できる状態を目指します。 その先のキャリアとして 将来的に目指す姿は、技術とビジネス、そして人を繋ぐ「ハブ」となる存在です。単なるプロジェクト管理に留まらず、プロダクトの成功に深く貢献できるPMとして、さらにハードウェアエンジニアとしての専門性を深めることで「プロダクト全体を俯瞰し、ビジネス価値を最大化できる技術者ビジネスパーソン」を目指します。 6. おわりに セーフィーでは、私のようにPM経験が浅いメンバーでも、挑戦と成長の機会が豊富にあります。 HW開発PMを現在も募集しておりますので、「望ましい経験/スキル」の全てが揃っていなくても、その一つ一つをセーフィーで極めていきたい!というあなたの意欲を、ぜひ私たちに聞かせてください。 safie.co.jp
はじめに こんにちは、データドリブン推進室でデータエンジニアをしている小宮です。 最近、個人的に気になっていたOSS BIツールのLightdashとMetabaseを社内でPoCする機会があったので、それぞれ使ってみた所感をまとめてみました。 はじめに 背景 比較軸 比較表 特に差が大きかったポイント Lightdashはスペースをネストできない(2025/5時点) ユーザの利用ログの取得 dbtとの連携 選ばれたのはLightdashでした 最後に 背景 データ分析の社内展開をさらに進めていく中で「より多くのメンバーが扱いやすく、かつ既存の分析基盤との親和性が高いBIツール」を検討することになりました。 そこで、分析基盤で導入しているdbtとの連携に強いLightdashと、使いやすさに定評のある Metabaseを候補に選び、PoCを実施しました。 比較軸 BIツールはユーザーの使いやすさと開発業務の効率性のバランスを取る必要があります。 そこで、以下の観点を重視しました。 開発業務の効率性:データモデル開発やダッシュボード作成がスムーズに行えるか ユーザビリティ:非エンジニアでもグラフを簡単に作れるか、日本語対応の有無 運用機能:通知・アラート、権限管理、利用ログの取得といった運用に必要な機能の有無 比較表 どちらもセルフホスティングでローカルに環境を作って触りましたので、その前提での比較表になります。 比較項目 Lightdash Metabase UI/UX ◯ ややエンジニア寄りな印象 ◎ 直感的でビジネスユーザでも使いやすい グラフの自由度 △ 基本的なものはある ◎ 種類が豊富(Tableauに近い) データガバナンス ◎ ymlで定義一元化 △ 属人化しやすい Dashboard as Code ◯ ◯ 機能はあるが有料プランのみ dbt との親和性 ◎ yml定義を直接利用 △ 標準では用意されていない Git 連携 ◎ dbt writeback機能で直接PR作成 × 日本語対応 × ◯ 日本語に違和感あり 学習コスト ◯ dbt経験者には低い ◎ 非エンジニアにも優しい 私たちのチームのデータ分析基盤 ではDWH層に DataVault2.0 、データマート層にディンメンショナルモデリングを採用しており、データモデリングに注力した設計を行っています。 そのため、BIツール上での複雑なロジックは最小限で済み、両ツールとも基本的な分析ニーズを満たすことができそうでした。 特に差が大きかったポイント PoCを通じて使い勝手や運用面など、2つのBIツール間で特に差が際立った点がいくつかありました。 以下に代表的なものを紹介します。 Lightdashはスペースをネストできない(2025/5時点) Lightdashはスペースというフォルダのような概念を持っていますが、このスペース機能は階層構造を持つことができません。 部署ごとにダッシュボードを階層的に整理したいケースはよくあるので、この点は実運用を考えると大きな差になると感じました。Metabaseでは階層構造に対応しており管理がしやすい設計になっています。 ※検証段階では未実装でしたが、新たに スペースのネスト が実装されたようです!!! ユーザの利用ログの取得 どのダッシュボードがどれだけ見られているか、表示速度はどうかなど、日々の運用で改善につながる指標を把握する必要があります。 Lightdashは基本的なインサイトダッシュボードを標準提供しており、セルフホスティングであれば内部のPostgreSQLメタデータに直接アクセスして自由に分析も可能です。 一方でMetabaseで提供している同等の機能のUsage Analyticsは、有償プランが必要でOSS版ではカバーされていません。 dbtとの連携 Lightdashの強みであるdbtとの連携はやはり大きな差を感じるポイントでした。 ymlによる定義でデータガバナンスを担保できる点と、UI上から作成した指標もdbt writeback機能を通してymlに反映できる点もよかったです。 選ばれたのはLightdashでした どちらのBIツールも操作性や導入のしやすさという点では優れており、甲乙つけがたい印象でした。 PoCの定量的な評価以外にも実際に触ってどうだったかの感触など定性的な部分も含めて、 Lightdashの方が以下の理由でフィットしそうだなと思いました。 ビジネスユーザーにも十分扱えるシンプルなUI dbtとの統合によるメトリクス定義の一元管理がしやすい アナリストもGitを使った開発を行っているため、チーム全体でコードベースの運用に慣れている 将来的な拡張性やCI/CDの自動化による運用面のガバナンスを担保しやすい 最後に 私たちのチームではLightdashが合いそうでしたが、BIツールの選定は単なる機能比較だけでなくチームの文化やスキルセットにも大きく依存すると思います。 チームによって最適なBIツールは様々あると思いますので参考になれば幸いです!
はじめに セーフィー株式会社 AI開発部 でテックリードを務めます橋本です。 本記事は、社内勉強会で取り上げた「プログラミングパラダイム」についての内容をまとめたものです。日常的に使っている Python や C++ といった言語でも、「副作用をどう扱うか」という視点の重要性を感じており、それをチーム内でも共有したいと思い勉強会のテーマとしました。 副作用とは、関数や処理が外部の状態に影響を与えたり、外部から影響を受けたりすることを指します。プログラムの規模が大きくなるほど、こうした副作用がバグの原因になりやすく、保守性にも影響します。この問題に対して、宣言型プログラミング、特に純粋関数型プログラミングの考え方は多くの示唆を与えてくれます。 本記事では、Haskell を例にしながら、命令型と宣言型の違い、副作用との向き合い方、そしてそれらの考え方をどのように他の言語に活かせるかを紹介していきます。 はじめに 命令型プログラミングの特徴と課題 命令型の特徴 Pythonによる実装例 オブジェクト指向との関係 命令型が抱える課題 宣言型プログラミングの考え方 宣言型とは何か 宣言型の特徴とアプローチ Haskellによる実装例 Haskell に見る副作用の扱い 副作用とは何か? コンテキストと持ち上げ Functor / Applicative / Monad の枠組み Functor:コンテキスト内の値に関数を適用する Applicative:関数そのものをコンテキストに包んで適用する Monad:コンテキスト間の連続した処理をつなげる 他の言語で宣言型の考え方を活かす Python C++ Rust おわりに 命令型プログラミングの特徴と課題 命令型の特徴 命令型プログラミング(Imperative Programming) は、「どのように処理を進めるか」を具体的な手順として記述するスタイルです。1950年代から1960年代初頭にかけて、コンピュータの普及とともに広まりました。 主な特徴 状態の変化に基づく処理 プログラム内の変数が繰り返し更新され、その変化によって処理の流れが決まります。例えば、ループ内で集計用の変数を加算していくような処理が代表的です。 逐次実行(手続き型) 命令は記述された順に上から下へ実行されます。制御の流れが明示的でわかりやすい一方、状態の追跡が難しくなることもあります。 繰り返しや分岐による制御 for や while によるループ、 if や switch による条件分岐を使い、柔軟な制御フローを実現します。 このような特徴は、 ノイマン型アーキテクチャ (プログラムとデータを同一メモリ空間に格納し、逐次実行するモデル)と親和性が高く、今日でも多くの言語の基本構造として使われています。 Pythonによる実装例 命令型プログラミングの特徴を具体的にイメージするために、Python を使った簡単な例を見てみましょう。 ## 1から10までの合計を計算する total = 0 for i in range ( 1 , 11 ): total += i # 状態(total)がループのたびに更新される このような記述スタイルは、多くのプログラミング言語で標準的に用いられています。 オブジェクト指向との関係 オブジェクト指向プログラミング(OOP) は、命令型プログラミングのサブパラダイムと捉えることができます。なぜなら、OOP でも依然として「状態の変化」と「手続きの実行」によって処理が進むからです。 class Cat : def __init__ (self, color): self.color = color # 状態を持つ def meow (self): print (f &quot;I'm a {self.color} cat. Meow!&quot; ) 命令型が抱える課題 命令型は柔軟で理解しやすい一方で、状態の追跡や副作用の管理に課題があります。 特に、変数の状態が頻繁に変わるようなコードでは、処理の意図やバグの原因を追いにくくなります。また、関数が外部に影響を及ぼす副作用を持つと、テストや再利用が難しくなり、保守性も低下します。 宣言型プログラミングの考え方 宣言型とは何か 宣言型プログラミング(Declarative Programming) は、「どのように処理を行うか」ではなく、「何をしたいのか」を記述するスタイルです。意図が明確で、状態や制御の管理が最小限で済むという特徴があります。 宣言型の特徴とアプローチ 宣言型プログラミングでは、以下のような設計上の特徴・原則が重視されます。 不変性(Immutability) 値や状態を途中で変更せず、すべての変数は定数として扱われます。 純粋関数(Pure Function) 同じ入力に対して常に同じ出力を返し、副作用を一切持たない関数です。外部の状態に依存せず、予測可能な挙動が保証されます。 遅延評価と記述順の自由度 計算は必要になるまで評価されないため、処理の記述順に意味が薄く、より宣言的な記述が可能です。 これらにより、状態や制御の複雑さを抑えながら、保守性・再利用性に優れたコードを書くことができます。 Haskellによる実装例 命令型のスタイルでよく書かれる処理が、宣言型の代表である Haskell でどのように記述されるのかを見ていきます。具体例を通じて、宣言型プログラミングの特徴である 不変性、純粋関数、遅延評価 がどのように現れるかを確認します。 total :: Int -- totalはInt型と宣言 total = sum [ 1 .. 10 ] -- totalを「1から10までのリストをsum関数で合計する関数」と定義 main :: IO () -- エントリポイントは main 関数。戻り値がIO () 型という宣言 main = print total -- main関数の定義 sum 関数を再帰関数を使って書くこともできます。 sumArray :: [Int] -&gt; Int sumArray [] = 0 -- 基底ケース sumArray (x : xs) = x + sumArray xs -- 再帰ケース: 先頭の要素を残りの要素の合計に足す main :: IO () main = print (sumArray [ 1 .. 10 ]) Haskell に見る副作用の扱い ここからは、Haskell を通じて、宣言型プログラミングにおける副作用との向き合い方を見ていきます。 Haskell は、副作用の扱いを明示的に設計に取り込んでいる言語であり、純粋関数型プログラミングの特徴を強く備えています。そのため、副作用を制御・分離する考え方を学ぶには最適な題材といえます。 副作用とは何か? 副作用(Side Effect) とは、関数や処理が、計算結果以外の影響をプログラム内外に及ぼすことを指します。例えば以下のようなものがあります。 非決定性 同じ入力でも結果が変わる処理。例:乱数生成、現在時刻の取得など。 外部状態の変更 例:ファイルへの書き込み、グローバル変数の更新、データベースの変更など。 外部依存の参照 例:ファイルの読み込み、ユーザー入力、環境変数の取得など。 コンテキストと持ち上げ Haskell では、副作用をそのまま実行するのではなく、コンテキストと呼ばれる構造の中に閉じ込めて扱います。これにより、副作用や曖昧さを型レベルで明示し、安全かつ予測可能に制御できるようになります。 以下に、代表的なコンテキストの例を示します。 通常の型 コンテキスト付きの型 説明 Int [Int] リスト。複数の値を1つのまとまりとして扱う。 Int Maybe Int 値がある(Just)か無い(Nothing)かを区別できる型 Int IO Int 外部の副作用を伴う処理で得られる Int 通常の関数や値を、こうしたコンテキストの中でも使えるように変換する操作のことを、 持ち上げ(lift) と呼びます。 この仕組みは、次のような 可換図式(commutative diagram) で表すことができます。可換図式とは、定義域と値域(型)をノード、関数やその関係性を矢印で示し、それらの構造的対応を視覚的に捉える図です。 例えば、以下の図では: 下段が通常の関数適用 g :: A -&gt; B 上段が g を f によって持ち上げた関数 f g :: f A -&gt; f B を表しています。 Functor / Applicative / Monad の枠組み Haskell では、副作用や不確実性を含む値(コンテキスト付きの値)を安全に扱うために、Functor・Applicative・Monad という抽象的な枠組みが用意されています。これらは順にできることが広がっていきます。 Functor:コンテキスト内の値に関数を適用する Functor は、 f a というコンテキストに包まれた値に、通常の関数 a -&gt; b を適用したいときに使います。 -- fmap の定義: (a -&gt; b) と f a を受け取り、f b を出力する fmap :: (a -&gt; b) -&gt; f a -&gt; f b 可換図式を用いると、引数と出力を視覚的に整理できます。 通常の関数 g をコンテキストに包まれた値に適用する具体例を示します。リストに対するブロードキャスト演算は、Haskell 以外の言語でもなじみがあるのではないでしょうか。 g n = n * 2 main = do print $ fmap g [ 1 , 2 , 3 ] -- [2, 4, 6] print $ fmap g (Just 5 ) -- Just 10 print $ fmap g Nothing -- Nothing Applicative:関数そのものをコンテキストに包んで適用する Applicative は、関数そのものがコンテキストに包まれている場合に使います。 f (a -&gt; b) という形の関数を f a に適用するための仕組みです。 pure :: a -&gt; f a -- pure関数は、値aを、コンテキスト f a に持ち上げます -- Applicative 演算子は、Functor の第一引数 a -&gt; b が f (a -&gt; b) に変わっただけです ( &lt;*&gt; ) :: f (a -&gt; b) -&gt; f a -&gt; f b pure 関数で (*2) を持ち上げたあと、コンテキスト付の型に対して、演算子 &lt;*&gt; を使って作用させます。 main = do print $ pure ( * 2 ) &lt;*&gt; [ 1 , 2 , 3 ] -- [2, 4, 6] print $ pure ( * 2 ) &lt;*&gt; Just 5 -- Just 10 Monad:コンテキスト間の連続した処理をつなげる Monad は、値だけでなく処理そのものがコンテキスト付きであるときに、それらを連鎖的につなげるための仕組みです。 ( &gt;&gt;= ) :: m a -&gt; (a -&gt; m b) -&gt; m b -- バインド演算子の定義 このように Monad を使うと、途中で失敗したとき、安全に次の処理に進めるかを自動的に判断できます。 -- xが0ならNothing、それ以外ならJust xを返す関数 safeDiv x y = if y == 0 then Nothing else Just (x `div` y) -- xが負ならNothing、それ以外ならJust xを返す関数 safeSqrt x = if x &lt; 0 then Nothing else Just (floor (sqrt (fromIntegral x))) -- xを2で割る関数 halve x = Just (x `div` 2 ) result = safeDiv 10 2 &gt;&gt;= safeSqrt &gt;&gt;= halve -- Just 1 他の言語で宣言型の考え方を活かす 命令型言語でも、宣言型の特徴が取り入れられています。 Python リスト内包表記は、手続き的にforループを使うのではなく、宣言的にリストを作成します。 squares = [x** 2 for x in range ( 10 ) if x % 2 == 0 ] ラムダ関数とmap add = lambda x, y: x + y result = list ( map ( lambda x: x * 2 , [ 1 , 2 , 3 ])) # [2, 4, 6] C++ C++17 では、値があるかどうかを型で扱える std::optional が導入されています。 std :: optional &lt; int &gt; twice ( int n) { return n &gt; 0 ? std :: optional &lt; int &gt;{n * 2 } : std :: nullopt ; } C++23 では、Optional に対して Haskell のモナド的演算に相当する and_then が追加されました。 int main () { std :: optional &lt; int &gt; o = 2 ; assert (o. and_then (twice). and_then (twice). value () == 8 ); } このように、副作用やエラーの可能性を含んだ処理を安全に連結できる仕組みが取り入れられています。 Rust 近年注目されている Rust も、宣言型プログラミングの考え方を強く取り入れられており、型システムによって副作用の制御が言語レベルで保証する設計がなされています。例えば、 Option 型による明示的なエラーハンドリングによって副作用を局所化することができます。 fn double (n: i32 ) -&gt; Option &lt; i32 &gt; { Some (n * 2 ) } fn main () { let result = Some ( 5 ) . and_then (double) . and_then (double); // Some(20) } このように、 and_then を使って処理を連鎖させることで、状態の変更やエラー分岐を持たずにロジックを構築できます。 おわりに 本記事では、命令型と宣言型という二つのプログラミングパラダイムを比較しながら、特に副作用の扱いに注目してその設計思想を紹介しました。命令型では、状態の変化と逐次実行を通じて柔軟な処理が可能である一方、状態の追跡や副作用の管理が複雑化する傾向があります。 それに対し、宣言型プログラミングは処理の目的を明示的に記述し、副作用を抑えることで、コードの予測可能性と保守性を高めるアプローチです。Haskell を例に、コンテキストや持ち上げといった概念、そして Functor・Applicative・Monad という構造を通して、副作用を型として管理する手法を紹介しました。 こうした考え方は、純粋関数型言語に限らず、Python・C++・Rust をはじめとした他の言語でも応用可能です。日々の開発においても、関数の純粋性や状態の局所化を意識することで、より読みやすく、安全なコードを書く参考になれば幸いです。