こんにちは、エンジニアの太田です。 この記事は Enigmo Advent Calendar 2023 の16日目の記事です。 はじめに TurboはRails7からデフォルトで搭載されており、VueやReactなど javascript の記述が必要だったDOMの更新を javascript を(あまり)書かずに実現させてくれます。 フロントエンドにあまり触れない方にとっては、SPA風のwebアプリへのとっつきやすさが出たと思います。 本記事では、私が初めてTurboに触れて使い方を覚える際に作成したサンプルの一部と使った感想を備忘録的にまとめたものになります。 各公式ドキュメントでも使い方を確認できます。 Rails で JavaScript を利用する - Railsガイド *1 Turbo Handbook *2 サンプルコード 以下は私が主に使用した形です。 ransack *3 とkaminari *4 を使ったリストの更新と追加・編集・削除をしてみます。 controllers class CountriesController < ApplicationController def index @q = Country.ransack(params[:q]) @q.sorts = 'name asc' @countries = @q.result.page(params[:page]) # Request HeadersにTurbo-Frameが設定されているとpartialがreturnされて、 # 対象の要素がpartialに置換される if turbo_frame_request? render partial: 'list', locals: { countries: @countries } end end def show @country = Country.find(params[:id]) end def edit @country = Country.find(params[:id]) end def create @country = Country.create!(country_params) end def update @country = Country.find(params[:id]) @country.update!(country_params) end def destroy @country = Country.find(params[:id]) @country.destroy! end private def country_params params.require(:country).permit(:code, :name) end end models class Country < ApplicationRecord # ransackで検索項目にしたカラムを記載したのみ def self.ransackable_attributes(_auth_object = nil) %w[name] end end views index.html.erb # このフォーム内からのリクエストはヘッダーにTurbo-Frameを設定する # レスポンスのpartialによってid属性がlistのturbo-frame要素が置換される <%= search_form_for @q, html: { data: { turbo_frame: 'list' } } do |f| %> <div> <%= f.label :name_cont, 'name' %> <%= f.text_field :name_cont %> </div> <div> <%= f.submit '検索' %> <%= link_to 'リセット', countries_path, data: { turbo_frame: "_top" } %> </div> <% end %> <%= render 'list', countries: @countries %> <%= render 'form' %> _list.html.erb # id属性がlistのturbo-frame要素 <%= turbo_frame_tag :list, autoscroll: true, data: { autoscroll_block: 'start' } do %> <ul id="countries"> <li> <div>国コード</div> <div>国名</div> <div></div> <div></div> </li> <%= render countries %> </ul> <div> # turbo_frame_tag内は自動的に直近の親を対象としたTurbo-Frameのリクエストになる # なのでkaminariにTurbo用の設定は不要 <%= paginate countries %> </div> <% end %> _country.html.erb # id属性がcountry_{country.id}のturbo-frame要素 <%= turbo_frame_tag country do %> <div><%= country.code %></div> <div><%= country.name %></div> # 直近の親 (id属性がcountry_{country.id}のturbo-frame要素)が対象のTurbo-Frameのリクエストになる # edit.html.erbに置換される <div><%= button_to '編集', edit_country_path(country), method: :get %></div> # GET以外のメソッドではTurbo-Streamのリクエストになる # destroy.turbo_stream.erbの処理を実行 <div><%= button_to '削除', country_path(country), method: :delete %></div> <% end %> edit.html.erb # id属性がcountry_{@country.id}のturbo-frame要素 <%= turbo_frame_tag @country do %> <div> <%= form_with model: @country do |form| %> <%= form.text_field :code %> <%= form.text_field :name %> # 直近の親 (id属性がcountry_{@country.id}のturbo-frame要素)が対象のTurbo-Streamのリクエストになる # destroy.turbo_stream.erbの処理を実行 <div><%= button_to '保存', action: :update %></div> <% end %> # GETメソッドなのでTurbo-Frameのリクエストになる # show.html.erbに置換される <div><%= button_to '中止', country_path(@country), method: :get %></div> </div> <% end %> show.html.erb <%= render 'country', country: @country %> update.turbo_stream.erb # id属性がcountry_{@country.id}のturbo-frame要素をupdate後に置き換える <%= turbo_stream.replace @country %> create.turbo_stream.erb # id属性がcountriesの要素の末尾に_country.html.erbを追加 <%= turbo_stream.append 'countries', @country %> # 登録フォームを入力をリセット <%= turbo_stream.replace 'register' do %> <%= render 'form' %> <% end %> _form.html.erb # 登録フォームのid属性registerを設定しておく <%= form_with model: Country.new, id: 'register' do |form| %> <div><%= form.label :code %><%= form.text_field :code %></div> <div><%= form.label :name %><%= form.text_field :name %></div> <div><%= form.submit %></div> <% end %> destroy.turbo_stream.erb # id属性がcountry_{@country.id}のturbo-frame要素を削除 <%= turbo_stream.remove @country %> turbo-frame turbo-frameは画面内のturbo-frameタグを 一つだけ を対象として置換することができます。id属性必須です。 リストの更新が主な使用ケースでした。 turbo-stream turbo-streamは画面内の 複数 要素をid属性で指定して対象とすることができ、それぞれに対して下記の7つの処理 *5 を実行できます。 先頭追加 末尾追加 直前追加 直後追加 置換(対象要素含む) 更新(対象要素含まず中身だけ) 削除 この時、対象とする要素はturbo-frameタグではなくて普通のdivタグなども指定可能です。注意点はGET以外のリク エス トにする必要があることです。 登録時にリストへ要素を追加すると同時にフォームをリセットするなどが主な使用ケースでした。フラッシュを表示したりで複数箇所の更新が必要になりがちなDB操作が絡むリク エス トでの使用機会が多いと思います。 動きのイメージ リストを更新 検索をリセットして初期化 編集フォームに置換 編集を保存して反映 編集をやめる データを削除 データを登録 細かい話 Hotwireとは、Turboとは 下記の公式の説明やimport文からHotwireという開発アプローチがあって、それを実現させるためのパッケージにTurboというパッケージがあるという感じのようです。 Hotwire is an alternative approach to building modern web applications without using much JavaScript by sending HTML instead of JSON over the wire . *6 import * as Turbo from "@hotwired/turbo" HotwireにはTurbo以外にもStimulusとStradaがあり、この三つの要素から成ります。 Rails7以降ではデフォルトでTurboとStimulusが使えるようになっています。 # Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev] gem "turbo-rails" # Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] gem "stimulus-rails" ReactやVueとの違い 前述の公式説明には「without using much JavaScript by sending HTML instead of JSON over the wire .」とありますが、これがHotwireがReactやVueと異なるポイントです。 Rails でReactやVueを使う場合は、サーバサイドの rails がcontrollerでデータをreturnし、そのデータを取得するようにクライアントサイドのReactやVueの実装をするかと思います。 一方でHotwireでは、上に挙げたサンプルコードのようにリク エス トに対してcontrollerがHTML(render partial)をreturnするだけになります。 確かに、 JavaScript を使わずに JSON ではなくてHTMLを送信するようになっています。 Turboを使った感想 ransackやkaminariといったviewsを構成するファイルに対するgemをそのまま使えて、わざわざフロントエンド用にnpm installとかしなくて済むのがとてもよかったです。 DOM更新はほとんど JavaScript を記述しなくてもよかったのもあり、作業 工数 もそこそこ少なく済むのではないかと思います。 使いづらいところとしては、turbo-frameタグで全体を囲わなければならないのでスタイルの当て方が少し面倒になる場面がありました。 css フレームワーク を使う場合は結構気を使う必要があるかもしれません。 また、各アクションでhtmlを返す必要があるので、必然的にviews ディレクト リのファイルが多くります。jsの記述が必要無くなった分という感じです 。 一般公開する大きなサービスではちょっと頼りなさそうな感じはしましたが、他の業務もしながら開発する社内ツールくらいの規模であれば十分なものだと思いました。 最後までご覧いただきありがとうございました。 明日の記事の担当は、コーポレートエンジニア(コーポレートIT[CO-IT]チーム) の横川さんです。お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co *1 : https://railsguides.jp/v7.0/working_with_javascript_in_rails.html#turbo *2 : https://turbo.hotwired.dev/handbook/introduction *3 : https://github.com/activerecord-hackery/ransack *4 : https://github.com/kaminari/kaminari *5 : https://turbo.hotwired.dev/handbook/streams#but-what-about-running-javascript%3F *6 : https://hotwired.dev/
こんにちは、デザイナーの 細田 です。 この記事は Enigmo Advent Calendar 2023 の 15日目の記事です。 日頃の業務では BUYMA の様々なコンテンツのデザインを担当しています。 中でもHTMLメルマガのデザインに関しては、数年前から改修に携わるようになり、ABテストによるデザイン検証に取り組んできました。 今回はその中でも、改修によって、お問い合わせフォームへの 流入 数が 前月比で140%増加 し、本施策のコンバージョンとなるお問い合わせ数も 約3倍に増えた 成功事例をご紹介します。 改修前のデザイン まずは、今回改修依頼を受けたメルマガの概要をご紹介します。 対象者 高額な商品を複数回閲覧しているユーザー 内容 “ BUYMA コンシェルジュ ” *1 というサービスを紹介し、お問合せフォームへ誘導する デザイン このメールを見て、皆さんはどう感じましたか? 私は、「なんだか唐突だな」と感じました。 例えるなら、お店で洋服を見ていたら突然声をかけられ、 「なにかお探しですか? コンシェルジュ へご相談ください」と名刺を渡され去っていく・・ そのような唐突さを感じました。この内容では、お問合せしてみようと思う人は少ないかもしれません。 それでは、どうすれば唐突な印象を解消し、 BUYMA コンシェルジュ というサービスに興味をもってもらえるのでしょうか。 改修後のデザイン 改修後のデザインは以下のようになりました。順番に解説していきたいと思います。 【💡POINT 1 】ヘッダーに送信理由を記載する 私が、メルマガにおいて1番重要だと感じていることは、受信者に “自分宛” と思わせる ことです。 メルマガは “チラシ” と ”手紙” の両方の役割を担っています。 サッと見てゴミ箱行きのチラシとなるか、じっくり読んで返事を書きたくなる手紙になるか。 それにはそのメルマガが無作為に送られたものではなく、“自分に宛てて送られたものである” と感じてもらえるかどうかが重要となります。 そのために有効な手段として、上記のようにヘッダーに “どういう理由でこのメールを送信したのか” を記載する、という方法があります。 今回であれば、「高額な商品をご検討中の方へ特別なご案内です。」と記載することで、受信者がなぜこのメルマガが自分へ届いたかを理解し、「自分宛だから見てみよう」と感じる可能性を高めることができます。 【💡POINT 2 】タイトルは1番伝えたいことを簡潔に メルマガ内で1番目を引くタイトルには、そのメルマガで1番伝えたいことを簡潔に記載します。 改修前のタイトルでは、「お探しの商品は見つかりましたか?」とありますが、このメルマガで1番に伝えたい事は BUYMA コンシェルジュ の紹介です。 そこで、改修後のデザインでは「 BUYMA コンシェルジュ がバイマ太郎様のお買い物をサポートいたします。」と変更しました。 そうすることで、「 BUYMA コンシェルジュ って何だろう?」と興味を抱いてもらうことができます。 また、タイトルに受信者の名前を挿入し強調することで(例ではバイマ太郎となっています) 【 POINT 1 】で取り上げた“自分宛である” ことの演出を、より一層強めることができます。 【💡POINT 3 】分かりやすい見出しで離脱を防ぐ 改修前のメルマガには BUYMA コンシェルジュ についての説明がなく、「そもそも BUYMA コンシェルジュ ってなに?」と受信者を戸惑わせてしまう可能性がありました。 そこで改修後のデザインでは、 BUYMA コンシェルジュ についての説明エリアを追加したのですが、重要となるのが見出しです。 今回は、「 BUYMA コンシェルジュ とは?」という見出しをかなり目立つ形で挿入しています。 こうすることで、 BUYMA コンシェルジュ の説明エリアであることを明示し、タイトルを見て「 BUYMA コンシェルジュ って何だろう?」と興味を示したユーザーの目線をそのまま説明へと誘導する役割を担っています。 これが見出しのない、ただの文章としてそこに配置されていた場合、 特に スマートフォン の小さい画面で見た時に、反射的に読み飛ばされてしまったり、「読むのが面倒だ」とメルマガ自体から離脱されてしまう恐れもあります。 しっかり読んでもらいたい文章を配置する時こそ、目立つ見出しで分かりやすく区切ったり、文章自体も極力簡潔にまとめるなどの、細かい気配りが必要となります。 【💡POINT 4 】ボタンはベタ塗りで視認性を高める 改修前はボーダーであったボタンを、改修後はベタ塗りに変更しました。 その理由は、 BUYMA 全体のデザインルールに合わせるという目的もあったのですが、 スマートフォン の小さな画面で見た時の視認性の高さが、やはりベタ塗りの方が圧倒的に高いからです。 また、最近ではメルマガをダークモードで閲覧しているケースも増えており、 ダークモードで見ると特に、ベタ塗りボタンの視認性の高さが顕著になります。 まとめ 以上のような改善を行った結果、 BUYMA コンシェルジュ のお問い合わせフォームへの 流入 数が前月比で140%増加し、本施策のコンバージョンとなるお問い合わせ数も約3倍に増えました。 弊社ではデザイン改修に加え、MAツールの導入により、コンテンツ内容のパーソナライズ化や、配信対象の最適化など、よりユーザー1人1人に寄り添ったメルマガ作成を行なっており、確実に成果を生み出しています。 今後も地道なABテストを積み重ねながら、このような成功例を蓄積し、発信していければと考えています。 株式会社 エニグモ すべての求人一覧 hrmos.co *1 : BUYMA コンシェルジュ とは、特別なお客様限定の招待型プライベートサービスです。ご希望商品のお探しからお取引の相談まで、 BUYMA のご利用に関するあらゆるサポートを専任の コンシェルジュ が行なっています。
はじめに こんにちは、株式会社 エニグモ の開発を担当しているグループでエンジニア リングマ ネージャーをしている後藤です。 この記事は Enigmo Advent Calendar 2023 の15日目の記事です。 私は、開発グループでエンジニア リングマ ネージャーという役割をしています。この記事では、チームメンバーとの関わりの中で大切にしている「 心理的 安全性」についてポエムを書かせていただきます。 この記事の対象者 この記事の対象は、「 心理的 安全性」という言葉は聞いたことあるけどモヤッとしかわからないが大切そうだ、みんなが「 心理的 安全性」といっているから上っ面だけでも理解してドヤ顔をしたいという人を対象にしています。 この記事を通して、読者の方に「 心理的 安全性」がどうソフトウエア開発に影響を与えていると感じているのか、どうしたらソフトウエア開発チームにとって良い「 心理的 安全性」のある環境を作れるのかを自分で解釈する手助けになればと思っております。 あくまで、私の解釈ですのでこの記事を参考に自分の考え、理解を勧めていく手助けになれば幸いです。 因みに、私が思っている「 心理的 安全」なチームは以下のイラストのイメージです。 安心できるチーム 心理的 安全性の定義とソフトウエア開発現場における私の理解 まずはじめに、「 心理的 安全性」という言葉ですが、 Google が発表した「成果を出すチーム」の条件に出てきて有名になったと理解しています。私もこの記事で知りました。それからずっとこの「 心理的 安全性」が引っかかっていました。 なぜならマネージャーと名のつく役割を担っている以上、「成果」を出すことを期待されています。それに、最も影響を与えるとなれば理解しないわけにはいきません。 ところがです、「 心理的 安全性(psychological safety)」について調べると、組織の中で自分の考えや気持ちを誰に対してでも安心して発言できる状態のこと、と出てきます。また、そのような環境を作るために、自分の病気のことを話したり、プライベートなことも話せる関係を作っていくと良いと結ばれていました。 そうなのかー、私には無理じゃん、というのが正直な感想でした。エンジニア上がりでエンジニア仕事大好きなどちらかというとコミュ障寄りの私にとって、プライベートな話をしたり雑談をしながらチームを作って行くなんてできっこないと思ったわけです。 それでも、この「 心理的 安全性」を放置しておくわけにもいかないですし、別にプライベートな事が話せる事が必須条件ではないだろうという思いもあり私なりに噛み砕いてみました。 「 心理的 安全性」が低いとどんな問題があるのか 「 心理的 安全性」を理解するにあたり、「 心理的 安全性」が低いとどんな問題があるのかを考えてみました。 いろいろあるのですが、私が考えている一番の弊害は、開発をしていて「この部分少し不安だ」、「あの部分、実は問題がある気がする」といった「もやっと」した気持ちを他のメンバーに伝えることができない、伝えることに抵抗がある、ということではないかと考えました。 なぜなら、「この部分少し不安だ」、「あの部分、実は問題がある気がする」という部分は、なぜだか本番にリリースするとかなりの確率でエラーが起きたりします。そして、分かっていた本人は心のなかで「やっぱりか!」と思いながら修正に追われるわけです。 この「もやっと」を事前に伝えられていれば、トラブル対応に追われるという状況を回避できたはずです。 この、少し不安だが確信が持てない、間違っているかもしれないけど、みたいな事を伝えられるチームである事が生産性に影響を与えるのではないかと考えたのです。 「もやっと」を伝えられるチームにするために何をしているのか 私達のチームでは、毎週金曜日に KPT フレームワーク をつかって振り返りをしています。この場でこの「もやっと」を伝えられる雰囲気を作る事を心がけました。 そうはいっても、「もやっと」を伝えてくださいね、と言ってみたところで意見が出てくるわけではありません。 そのため、1on1で出てきた、困ったことや解決した事を、些細なことでも良いので週次の振り返りに上げてもらうよう伝えることを根気よく繰り返しています。また、私自身が見つけた問題や、解決した事も話すようにしています。 このような事を繰り返していくことで、小さな事でも伝えて良いんだ、という空気ができてくる。また他の人が伝えてくれた小さな事が役に立った!という経験の積み重で、「もやっと」を伝えていこうという空気が出てくるのではないかと思っています。 まとめ ソフトウエア開発の現場で「 心理的 安全性」は、小さな「もやっと」を伝えられるか、に影響すると考えています。 この小さな「もやっと」を意識的に引き出していくことで、ソフトウエア開発で発生する問題の芽を早いうちに摘む事ができ、開発生産性を改善できるのではないかと考えています。 そのために、小さな発見を伝えられる場作りを大切にしています。 おまけ 週末に BIBLIOTHECA というラジオ番組を聞いていたら、この「もやっと」が伝えられないチームの状況を適切に表す言葉に出会いました。この状況は、「集団浅慮(しゅうだんせんりょ)」と呼ばれる状況と一致しているのではないかと思います。 「集団浅慮(しゅうだんせんりょ)」とは、優秀な人達が集まって議論した結果、とても残念な結論を出して大失敗するという状況です。 そのラジオ番組によると、過去に ケネディ 大統領がこの失敗に陥り、そこからどう改善していったのかという事が以下の本に書いてある、と紹介されていました。この本の中に、マネージャーとして気をつけるべきことが潜んでいるのではないかと思っています。もっとも、集団浅慮に陥りやすいのは強いカリスマ的なリーダーがいるとき、というのがあったのでその部分は当てはまらない気がしますが、今度読んでみたいと思っています。 集団浅慮ー政策決定と大失敗の心理学的研究 作者: アーヴィング・L・ジャニス 新曜社 Amazon 一緒に働く仲間を募集しています! 株式会社 エニグモ では一緒に働く仲間を募集しています。興味のある方は以下の求人をご参照ください。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは。株式会社 エニグモ にてフロントエンドエンジニアを務めています新井です。 こちらは Enigmo Advent Calendar 2023 および アクセシビリティ Advent Calendar 2023 の 14 日目の記事です。 はじめに:ウェブアクセシビリティとは? 1. ページ内の見出しの最適化 2. リンクや状態の判別を色の変化だけで行わないようにする 3. キーボード操作時のフォーカスインジケーターは非表示にしない 4. クリッカブルな要素の実装には a タグまたは button タグを使用する 5. 画像の代替テキストの指定と装飾的な画像の取り扱い 6. フォームコントロールには必ずラベルを付与する 7. フォームの入力欄のオートコンプリートを有効にする 8. ラジオボタンやチェックボックスを装飾する際は元の input の隠し方に気をつける 9. アコーディオン UI は可能な限り details 要素を利用する 10. モーダル表示時は背面のコンテンツを読み上げ・選択不可能にする おわりに:BUYMA でウェブアクセシビリティを促進する理由 はじめに:ウェブ アクセシビリティ とは? X(旧 Twitter )や各種テック記事などを見ていると、多くの人がウェブ アクセシビリティ を、障がいを持つ人々、特に視覚障がいのある人々への配慮として認識しています。 これは正しい認識の一部ではありますが、ウェブ アクセシビリティ の範囲はもっと広いです。実際には、全ての年齢層や様々な身体的、精神的条件を持つ人々が、多種多様な環境下でウェブサイトやアプリケーションを利用可能にすることを目指しています。 障がいがない人も、健康問題、マウスや トラックパッド の故障、音声再生の不可能な環境など、様々な状況に直面することがあります。ウェブ アクセシビリティ とは、これら多様なユーザーがウェブサイトやアプリケーションを容易に利用できるように設計することを意味します。 この記事では、ウェブ アクセシビリティ を向上させるための重要な10の項目を紹介します。私たちが運営するショッピングサイト「 BUYMA 」も今年から アクセシビリティ への取り組みを進めており、これらの項目を今後達成することを目指しています。 1. ページ内の見出しの最適化 具体的にはページ内で使用されている h1〜h6 タグを、階層構造の深さに応じて最適化します。 多くの方が見出しの最適化と聞いて SEO の改善を連想するのではないでしょうか?実際に、見出しの整理が SEO に好影響を及ぼすことはあるかもしれません。しかし、もっと注目すべきなのは、この最適化が SEO のみならず アクセシビリティ にも大きく貢献するということです。 スクリーンリーダーなどの支援技術を利用するユーザーは、見出しを通してページ全体の構造を理解します。また、見出しジャンプ機能を使って必要な情報に素早くアクセスすることもあります。WebAIM のアンケートによると、約 7 割のユーザーが見出しジャンプ機能を使用しているとのことです。 webaim.org 見出しを用いてウェブサイトの情報構造を適切に整理することで、ユーザーが情報を迅速に理解できるようになります。 Google Chrome の 拡張機能 「 Web Developer 」を使用することでページの見出しのアウトラインを可視化することができます。見出しレベルを視覚化して検証することで、ページの構造が明確になり、改善点を見つけやすくなります。見出しの正確さや一貫性を確認するために、見出しレベルは可視化して検証することをおすすめします。 参考: 達成基準 2.4.6: 見出し及びラベルを理解する 2. リンクや状態の判別を色の変化だけで行わないようにする リンクや状態(フォーカスやホバーなど)の判別を色のみでデザインしてしまうと、 色覚異常 を持つユーザーにとって判別が困難になる可能性があります。 この問題に対処するためには、色だけでなく形状やその他のビジュアル変化を用いて区別ができるようにすると良いでしょう。 具体的な対策としては次のようなものがあります。 テキストリンクは色の変更に加えて、下線を表示する 状態が変化する際に、アイコンを変更する ボタンがホバーされた時は、背景色を変えるだけでなく枠線も表示する 特にホバーの状態は、透明度の変更のみでデザインされることが多いので、デザイナーと協力して状態の変化が明確に分かるデザインを作成することが望ましいです。 また、以下の CSS コードを デベロッパ ーツールで適用することで、ウェブサイトをグレースケールに変換し、色に頼った UI がないかどうかを検証するのに役立ちます。 body { filter : grayscale( 1 ); } ただし、濃い赤のような色はグレースケールへの変換では確認しにくい場合があるため、検証のコストとのバランスを考慮しながら、色覚特性のタイプごとに見え方をシミュレーションすることも有効かもしれません。 参考: 達成基準 1.4.11: 非テキストのコントラストを理解する 3. キーボード操作時のフォーカスインジケーターは非表示にしない リンクやボタンがクリックされた際に表示されるフォーカスインジケーターは、しばしばデザイン上の理由で非表示にされがちです。しかし、フォーカスインジケーターを非表示にしてしまうと、キーボードでウェブサイトを操作するユーザーはコンテンツの利用が困難になってしまいます。 クリック時のフォーカスインジケーターを非表示にしつつ、キーボード操作時のフォーカスインジケーターを維持するには、以下のような CSS をベースで指定しておくと良いでしょう。 : focus : not( :focus-visible ) { outline : none ; } :focus-visible 擬似クラスはキーボード操作によりフォーカスされた場合に適用され、マウス操作によりフォーカスした場合には適用されません。 もしサイト内に複数の CSS が読み込まれ、各要素に outline:none が適用されているような場合、以下のような CSS を指定することでキーボード操作時のフォーカスインジケーターを復活させることができます。 @layer focus { :focus- visible { outline: revert; } } この方法では、 Cascade Layers という機能を用いて優先度を高めています。 このスタイルは ID セレクタ で定義されたスタイルよりも優先されますが、インラインスタイル( style 属性)や !important 指定されたスタイルよりは優先度が低いため、 !important を使用するよりも安全です。 ただし、 outline プロパティを上書きする場合は新たに @layer を設けて上書きする手間があるので、あくまで応急処置として扱うのが賢明です。 Cascade Layers は、現在すべてのモダンブラウザで使用可能です。 さらに、フォーカス時の状態をより分かりやすくするために、インジケーターに加えてホバー時のスタイルも適用しておくとフォーカスの判別がよりわかりやすくなります。 .button : focus -visible { /* ホバー時のスタイル */ } @media (hover: hover) and (pointer: fine) { .button :where(:any-link , :enabled , summary):hover { /* ホバー時のスタイル */ } } タッチデ バイス でのホバーは動作に支障が出る場合があるため、hover メディアクエリを利用してタッチデ バイス でのホバーは無効化しておくと良いでしょう。 参考: 達成基準 2.4.7: フォーカスの可視化を理解する 4. クリッカブル な要素の実装には a タグまたは button タグを使用する クリッカブル な要素を div タグや span タグを用いて実装すると、支援技術を使用するユーザーやキーボード操作に依存するユーザーがその要素を選択できなくなることがあり、結果としてコンテンツの アクセシビリティ が損なわれる可能性があります。 リンクは a タグを、ボタンは button タグを使用して、適切に マークアップ しましょう。支援技術は a 要素を「リンク」、button 要素を「ボタン」と読み上げます。 既存のコードで a タグや button タグに変更するのが難しい場合、tabindex 属性と keydown イベントを使って クリッカブル な振る舞いを実装することができます。以下は、 BUYMA で使用されている React のコードの一例です。 <tr key= { item.id } className= "catalogs-table__row" onClick= { () => handleSelectItem(item) } onKeyDown= { (e) => { if (e.key === "Enter" || e.key === " " ) { // Enter or Space で実行 handleSelectItem(item); } }} tabIndex= { 0 } > ... </tr> このコードでは、tr要素に onClick イベントと onKeyDown イベントが設定されており、tabIndex={0}によってキーボード操作で選択可能になっています。 5. 画像の代替テキストの指定と装飾的な画像の取り扱い 原則的にコンテンツ上意味のある画像には必ず代替テキストを指定し、装飾的な画像は読み上げしないようにします。 img 要素の代替テキストは alt 属性で指定します。 < img alt = "BUYMA" src = "logo.png" width = "130" height = "25" decoding= "async" /> アイコンフォントや SVG 要素に代替テキストを指定する際には、role="img" と aria -label 属性を併用して指定します。span や div、 svg に aria -label を指定すると、支援技術によっては適切に読み上げられない場合があります。また、 span や div の暗黙のロールが WAI- ARIA 1.2 より generic となったことで aria -label を付与するのは仕様違反となっています。安定した読み上げを確保するためにも、必ず role="img" を指定しましょう。 < span role = "img" class = "fab-icon fab-icon-facebook" aria-label = "Facebook" ></ span > < svg role = "img" aria-label = "X" viewBox= "0 0 20 20" > … </ svg > 装飾的な画像として img 要素を使用する場合は、alt 属性を空にします。alt 属性を省略すると、支援技術は URL を読み上げる可能性があります。 < img alt = "" src = "crown.svg" width = "20" height = "20" decoding= "async" loading= "lazy" /> 装飾的なアイコンフォントや SVG 要素は、読み上げから除外するために aria-hidden="true" を指定します。 < span class = "fab-icon fab-icon-information" aria-hidden = "true" ></ span > < svg aria-hidden = "true" viewBox= "0 0 20 20" > … </ svg > 代替テキストの作成時には、画像が伝える情報を正確かつ適切にユーザーに届けることを心掛けるようにします。また、画像に関連しない情報を SEO のために加える行為はNGです。代替テキストはコンテンツの重要部分であり、他のテキストと同様、コンテンツ設計の初期段階で検討すると良いでしょう。 さらに、意味を持つ画像は CSS の background-image を使って背景として設定しないようにします。背景画像として設定してしまうと、支援技術を使用するユーザーが画像を認識できなくなります。また、background-image は loading 属性や decoding 属性を指定できないため、ウェブサイトのパフォーマンスに影響を及ぼす可能性があります。 参考: 達成基準 1.1.1: 非テキストコンテンツを理解する 6. フォームコン トロール には必ずラベルを付与する フォーム内の入力欄やテキストエリア、セレクトボックス、 チェックボックス 、 ラジオボタン などのフォームコン トロール には、label 要素を使って明確にラベルを付けるようにします。 label 要素の使用には アクセシビリティ の観点から大きな利点があります。フォームコン トロール にフォーカスが合わされた際に要素の名前を読み上げることが可能になり、またクリックやタップの範囲が広がり、利用しやすくなります。 フォームコン トロール とラベルを関連付ける方法としては、ラベルとフォームコン トロール を label 要素で包むか、フォームコン トロール の id 属性を label 要素の for 属性に紐付ける方法があります。 < label > ニックネーム < input type = "text" name = "nickname" /> </ label > < label for = "nickname" > ニックネーム </ label > < input id = "nickname" type = "text" name = "nickname" /> デザイン上、フォームコン トロール のラベルが UI 上に表示されていない場合でも、支援技術利用者のために aria -labelledby 属性や aria -label 属性を使用してラベル付けを行うことが望ましいです。 aria -labelledby 属性を使用した例 < form > < p id = "keyword_label" style = "display: none" > 検索キーワード </ p > < input type = "search" name = "keyword" aria-labelledby = "keyword_label" /> < button > 検索 </ button > </ form > aria -label 属性を使用した例 < form > < input type = "search" name = "keyword" aria-label = "検索キーワード" /> < button > 検索 </ button > </ form > また、 ラジオボタン や チェックボックス など、複数のフォームコン トロール が意味的に一つのグループを形成する場合、fieldset 要素を使用してこれらをグループ化するようにします。この方法を採用すると、支援技術を使用してフォームコン トロール を操作する際に、そのコン トロール が属するグループの名前も読み上げられるようになります。 以下は、fieldset 要素を使用して ラジオボタン をグループ化した例です。性別のコードは ISO 5218 で定められたものに従っています。 < fieldset > < legend > 性別 </ legend > < ul > < li >< label >< input type = "radio" name = "gender" value = "1" /> 男 </ label ></ li > < li >< label >< input type = "radio" name = "gender" value = "2" /> 女 </ label ></ li > < li >< label >< input type = "radio" name = "gender" value = "9" /> その他 </ label ></ li > < li >< label >< input type = "radio" name = "gender" value = "0" /> 不明・回答しない </ label ></ li > </ ul > </ fieldset > また、ラベルを プレースホルダ ーで代用するUIも時折見かけますが、以下の観点から避けたほうがいいでしょう。 記入時にラベルが消えてしまうためユーザーの短期記憶に負荷をかけることとなる フォームの送信前にユーザーがどの項目に何を書いたのか判別できなくなる可能性がある キーボード操作でフォーカスが当たるとラベルが消えることによる利便性の低下 参考: 達成基準 2.4.6: 見出し及びラベルを理解する 7. フォームの入力欄のオートコンプリートを有効にする オートコンプリートを有効にすることにより、ブラウザに保存された補完機能を利用できるようになり、結果としてユーザーは素早くフォームの入力を完了することができます。 < label > 電話番号 < input type = "tel" name = "tel" placeholder = "(例) 090-0000-0000" autocomplete = "tel" pattern = "\d{2,4}-?\d{2,4}-?\d{3,4}" title = "電話番号は正しく記入してください" /> </ label > サービス内でよく使用する autocomplete 属性の値は以下の通りです。 値 補完する要素 on autocompleteを許可する off autocompleteを許可しない name 氏名 given-name ファーストネーム (名前) additional-name ミドルネーム family-name ラストネーム(名字) tel 電話番号 email メールアドレス username ユーザー名orアカウント名 postal-code 郵便番号 address-level1 都道 府県 address-level2 市区町村 address-level3 町域 address-level4 番地など organization 企業・団体・組織名 cc-name クレジットカード登録名 cc-number カード番号 cc-exp カードの有効期限 多くのサービスでは、電話番号やクレジットカード番号などでオートコンプリート属性を活用します。一部の UI デザインでは、電話番号やクレジット番号の各部分を別々の入力欄で要求することがありますが、これは単に使いにくいだけでなく、オートコンプリートの利用を妨げる可能性があるため、デザイナーはこれらの情報を一つの入力欄で収めるよう配慮すると良いでしょう。 8. ラジオボタン や チェックボックス を装飾する際は元の input の隠し方に気をつける display:none や visibility:hidden されている input はキーボード操作でのフォーカスが不可能になってしまうため、 ラジオボタン や チェックボックス を装飾する際は別のアプローチで隠すようにしましょう。 < label class = "checkbox" > < input class = "checkbox__input" type = "checkbox" /> < span class = "checkbox__icon" aria-hidden = "true" ></ span > < span class = "checkbox__text" > 規約に同意する </ span > </ label > .checkbox__input { height : 1px ; opacity : 0 ; position : absolute ; width : 1px ; z-index : -1 ; } さらに、装飾された チェックボックス や ラジオボタン がフォーカスされた際には、その状態がユーザーに明確に認識できるようにしましょう。 以下の CSS は、フォーカスされた際に チェックボックス の周りにアウトラインを表示し、関連するテキストを下線で強調する方法の例です。 .checkbox__input { height : 1px ; opacity : 0 ; position : absolute ; width : 1px ; z-index : -1 ; } .checkbox :has( .checkbox__input : focus -visible) { outline : 1px solid blue ; } .checkbox__input : focus -visible ~ .checkbox__text { text-decoration : underline ; } 9. アコーディオン UI は可能な限り details 要素を利用する アコーディオン UI を details 要素で実装すると、非アクティブな要素内のテキストもページ内検索で見つけることができ、キーボード操作や支援技術によるナビゲーションも可能になります。さらに、 JavaScript を使わないためパフォーマンスが向上し、 JavaScript が動作しない場合でも アコーディオン が利用可能になります。 < details > < summary > 見出し </ summary > < p > コンテンツ </ p > </ details > 例えば、 BUYMA では スマートフォン 表示のフッターメニューに details 要素が使用されています。 ただし、 jQuery の slideToggle のようなアニメーション効果を追加したい場合、閉じる際に CSS の transition が効かないため別途 JavaScript が必要になります。 余談ですが、 jQuery の slideToggle のようなアニメーションを実装する際、現代では折りたたむ要素に display: grid と transition: grid-template-rows .3s ease-out (所要時間とイージングはお好みで調整してください) を指定し、 開閉時に grid-template-rows の値を 0fr ↔ 1fr に変更するだけで対応が可能です。 参考: detailsとsummaryタグで作るアコーディオンUI - アニメーションのより良い実装方法 - ICS MEDIA 10. モーダル表示時は背面のコンテンツを読み上げ・選択不可能にする モーダルコンテンツ(ダイアログやドロワーメニューなど)が表示されている際には、背後にあるコンテンツがキーボードや支援技術からアクセスされないよう設定します。 背面コンテンツに inert 属性を付与することで実現できます。inert 属性があると、ブラウザはその要素に対するユーザーの入力イベント(フォーカスイベントや支援技術からのイベントを含む)を無視します。 < body > < div id = "wrapper" inert> < header > ... </ header > < main > ... </ main > < footer > ... </ footer > </ div > < div role = "dialog" aria-modal = "true" aria-labelledby = "modal_1_title" tabindex = "-1" > < h2 id = "modal_1_title" > モーダルタイトル </ h2 > < p > モーダルコンテンツ </ p > </ div > </ body > inert 属性は多くの現代のブラウザで利用可能ですが、未対応の環境での使用を検討している場合は、Polyfill が利用可能です。 www.npmjs.com 参考: HTMLElement: inert プロパティ おわりに: BUYMA でウェブ アクセシビリティ を促進する理由 ウェブ アクセシビリティ を考慮することにより、サービスをより広範なユーザーに提供することが可能になります。これにより、 アクセシビリティ の問題からサービスの利用を諦めていた人々を取り戻し、機会損失を防ぐことができます。 さらに、使いやすいサービスを提供することで、全体的なユーザー体験を向上させることにもつながります。 弊社では、 アクセシビリティ チャンネルを Slack で設立し、 アクセシビリティ に関する Redmine チケットを作成するなど、取り組みを進めています。まだ完全に対応しきれていない部分は多いですが、近い将来、これらの「必ず達成しておきたい アクセシビリティ 対応」を実現する目標に向けて努力を続けています。 明日の記事の担当は SELL チームの後藤さんです。お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、サービスエンジニアリング本部の寺田です! この記事は Enigmo Advent Calendar 2023 の13日目の記事になります。 私はエンジニアとして BUYMA の決済システム・配送システムの保守/運用や、またこれらのシステム領域を中心としたサービスの新規開発などを主に担当しています。 この記事では Emacs Orgmode を使ったタスク管理の方法について紹介していきたいと思います。 みなさんはタスク管理に何を利用しているでしょうか?? 紙とペン、OS 標準の Reminder アプリ、 Google Calendar など、人それぞれ普段お使いのものがあるかと思います。 私は実際に普段の業務で Emacs Orgmode を利用したタスク管理を行っています。 これを使っていく中で私は以下のようなメリットを感じています。 仕事の抜け・漏れを起こさなくなる。 自分のキャパを最大限活かせる。(キャパオーバーになるのも避けられる。) 日々の仕事に達成感を得られる。 今現在のタスク管理の方法がしっくりこない...と感じている方は、 ぜひこの記事をご覧いただき、一つ選択肢に加えてみてはいかがでしょうか? Emacs Orgmode とは Orgmode は Emacs の 拡張機能 の一つです。独自の マークアップ 言語を利用できるシンプルな テキストエディタ ですが、 非常に高機能でタスク管理などに便利な数多くの機能をサポートしています。 https://orgmode.org/ja/ 当然 Emacs を必要とするので、Emacser ではない方には関係ない...とお思いかもしれません。 しかし私も普段の開発では VSCode + Vim キー バインディング を使っており、 Emacs は何もかもが素人中の素人です。 そういった方におすすめしたいのが Spacemacs です。 https://www.spacemacs.org/ Emacs ベースのエディタですが、デフォルトで Vim の キーバインド が利用できます。 私も Spacemacs を利用していますが、ほぼほぼ Vim と同じ操作感で扱うことができています。 Orgmode を利用したタスク管理の流れ ここからは私が実際に行っているタスク管理の流れを、Orgmode の機能の紹介とともに説明していきます。 ファイル構成 私はタスク管理のために3種類のファイルを用意しています。 . ├── TODO.org └── weekly └── YYYYMMDD.org └── daily └── YYYYMMDD.org TODO.org 自分が担当することになったタスクはひとまず全て TODO.org に追加します。 まさしく TODO リストの役割を果たしており、こちらにタスク全量がリスト化されることになります。 weekly/YYYYMMDD.org 1週間分のタスクリストを表すファイルです。 TODO.org からタスクをピックアップして作成します。 daily/YYYYMMDD.org 1日分のタスクリストを作成する場所です。 weekly/YYYYMMDD.org からさらにタスクをピックアップして作成します。 (随時)TODO リストを作成する 何かタスクに アサイ ンされたらまずは TODO.org に追加していきます。 合わせてここで作業時間の見積もりを行います。 Effort Estimates を利用すると見積もった時間をタスクに対して簡単に設定できます。 https://orgmode.org/manual/Effort-Estimates.html また、TODO リストの未済管理には Multi-state Workflow を利用してステータス管理を行うと良いでしょう。 https://orgmode.org/guide/Multi_002dstate-Workflow.html あらかじめ TODO , DONE ステータスを設定しておき、コマンド一発で遷移できるようにしておきます。 見積もり、ステータス設定を行った後の TODO.org は以下のようになります。 #+SEQ_TODO: TODO(t/!) | DONE(d) // Multi-state Workflow で遷移可能なステータスの定義。タスクに対して C-c C-t でステータスを変更できる。 #+PROPERTY: Effort_ALL 1:00 2:00 3:00 5:00 8:00 13:00 // Effort Estimates で設定可能な見積もり時間。タスクに対して C-c C-x e でここで定義した時間を設定できる。 *** 新規配送システム導入 **** TODO 購入者は新規配送方法を指定して商品購入を行うことができる :PROPERTIES: :Effort: 5:00 :END: **** TODO 出品者は新規配送方法に利用できる配送用ラベルを発行できる :PROPERTIES: :Effort: 8:00 :END: **** TODO 出品者は新規配送方法を利用して配送を実施することができる :PROPERTIES: :Effort: 13:00 :END: (毎週月曜日)1週間のやることリストを作成する 週初めの業務開始のタイミングで1週間のやることリストとして weekly/YYYYMMDD.org を作成します。 まずは自分が1週間で作業に充てられる時間を計算していきます。 ただし1週間のうち全ての時間を作業時間に使えるわけではないのでこれを考慮しましょう。 私の場合、勤務時間のトータルは 40 時間です。(週5日、1日8時間勤務で 5 * 8 = 40 時間)しかし定例や打ち合わせの時間を除くと大体 20~30 時間くらいが作業に充てられる時間となることがほとんどです。 当然休みの日や早退の日があればこれも考慮します。 計算が終わったらファイルにこの時間をメモしましょう。 次に TODO.org から計算した作業時間内でこなせるだけのタスクを選んで weekly/YYYYMMDD.org に追加します。 ここでのポイントですが、 週はじめの段階では作業時間の 80 % 程度でできるタスク量にしておいた方が良いです。 エンジニアは不具合の修正対応など突発的なタスクが発生しがちです。 こういった事態も吸収できるようにバッファを持たせておくことが重要です。 Work time: 20 hrs (80 %: 16 hrs) // 純粋に作業に充てられる時間だけを見積もる。 // また、突発的なタスクに対応できるよう、80%程度の時間でできることに収めた方が良い。 *** 新規配送システム導入 **** TODO 購入者は新規配送方法を指定して商品購入を行うことができる :PROPERTIES: :Effort: 5:00 :END: **** TODO 出品者は新規配送方法に利用できる配送用ラベルを発行できる :PROPERTIES: :Effort: 8:00 :END: **** TODO 出品者は新規配送方法を利用して配送を実施することができる :PROPERTIES: :Effort: 3:00 :END: ここに追加したタスクの見積もりの合計が、作業時間内で収まっているかどうかを確認するには Column View を活用すると良いでしょう。 https://orgmode.org/manual/Column-View.html 親タスクに対して C-c C-x C-c とすると簡単に子タスクの見積もり時間の合計時間を表示することができます。 #+COLUMNS: %ITEM %Effort{:} // ITEM: タスク名, Effort: 見積もり時間 を表示 | *** 新規配送システム導入 | 16:00 | # 子タスクの合計時間が表示されている | **** 購入者は新規配送方法... | 5:00 | | **** 出品者は新規配送方法... | 8:00 | | **** 出品者は新規配送方法... | 3:00 | 1週間分のやることを週初めの段階で決めることで次のようなメリットがあります。 まずキャパオーバーになることを避けられます。 既に予定しているタスクで今週分の作業時間が埋まっているならば、 これ以上の仕事を受けて、早急に対応することは難しいとすぐに判断できます。 逆にこの段階で作業時間が余るようであれば、 新しいタスクを拾ったり、忙しい時にはできないような カイゼン 系のタスクなどに取り組もうと、 週初めの段階で決めてしまえば良いのです。 後者は暇になってから考えても同じじゃないの?とも思いますが、 無計画に暇を迎えるとただただ時間を潰すだけになりがち。人間とは弱い生き物なのです... こうして無理のない範囲に収めながらも、 自分の時間を最大限活用することが可能になっていきます。 (毎日朝)今日やることのリストを作成する 毎日業務を開始するタイミングで今日一日でやることのリストを daily/YYYYMMDD.org に作成します。 週初めの weekly/YYYMMDD.org の作成と同じように一日の作業時間を計算して、 その日に行えるだけのタスクを weekly/YYYYMMDD.org からこちらにコピーして追加します。 1週間分のやることリストの作成と同じように、 ミーティングの時間などは除いて、純粋に作業に充てられる時間に対してタスクを当てこみましょう。 Work time: 5 hrs *** 新規配送システム導入 **** TODO 購入者は新規配送方法を指定して商品購入を行うことができる :PROPERTIES: :Effort: 5:00 :END: (業務時間中)作業時間を記録する 作業した時間は原則タスクに対して記録していきます。 Orgmode には Clocking commands というものが用意されています。 https://orgmode.org/manual/Clocking-commands.html 作業開始時には C-c C-x i 、終了時には C-c C-x o を入力することで、各タスクに対して作業時間の記録が行えます。 *** 新規配送システム導入 **** TODO 購入者は新規配送方法を指定して商品購入を行うことができる :PROPERTIES: :Effort: 5:00 :END: :CLOCKING: CLOCK: [2023-12-04 月 09:30]--[2023-12-04 月 10:00] => 0:30 // 作業時間が記録される :END: また ポモドーロテクニック を使いながら作業時間を記録できる org-pomodoro という サードパーティ のライブラリもあります。 https://github.com/marcinkoziej/org-pomodoro こちらは作業開始時に C-c C-x p を実行すると、 あらかじめ指定した時間が経過したら自動的に作業終了となり時間が記録されます。 作業終了時にはチャイムが鳴ってお知らせもしてくれます。 作業時間中はタスクに全集中で取り組みましょう。X のタイムラインを見ながらダラダラと...なんてのはダメです(笑)。休憩する時は Clocking commands を停止してメリハリをつけましょう! 予定していた1日の作業時間を消化できたら、業務は一旦おしまいにします。おつかれさまでした! (業務終了時)1日の作業を振り返る 予定していた作業時間に対してどのくらい自分がコミットできたかは、前述した Column View で簡単に確認することができます。 #+COLUMNS: %ITEM %Effort{:} %CLOCKSUM // CLOCKSUM: 記録した時間の合計 を表示 | *** 新規配送システム導入 | 5:00 | 5:12 | | **** 購入者は新規配送方法... | 5:00 | 5:12 | 次に daily/YYYYMMDD.org に記録した作業時間を、 weekly/YYYYMNDD.org に転記していきます。 こうすることで1日を経る毎に1週間分の作業時間の記録が溜まっていきます。 *** 新規配送システム導入 **** TODO 購入者は新規配送方法を指定して商品購入を行うことができる :PROPERTIES: :Effort: 5:00 :END: :CLOCKING: CLOCK: [2023-12-04 月 17:00]--[2023-12-04 月 18:12] => 1:12 CLOCK: [2023-12-04 月 13:00]--[2023-12-04 月 15:00] => 2:00 CLOCK: [2023-12-04 月 10:30]--[2023-12-04 月 12:00] => 1:30 CLOCK: [2023-12-04 月 09:30]--[2023-12-04 月 10:00] => 0:30 :END: **** TODO 出品者は新規配送方法に利用できる配送用ラベルを発行できる :PROPERTIES: :Effort: 8:00 :END: :CLOCKING: CLOCK: [2023-12-05 火 15:30]--[2023-12-05 火 18:00] => 2:30 CLOCK: [2023-12-05 火 10:00]--[2023-12-05 火 12:00] => 2:00 :END: このように日々、予定に対してどのくらい実際にコミットできたかを時間で確認できると、自分の仕事にかなりの達成感が出るのではないでしょうか?? モチベーションを維持することは、業務に対するパフォーマンスを最大化するためにはとても重要です。 (毎週金曜日)1週間の作業を振り返る 毎日の振り返りのタイミングで weekly/YYYYMMDD.org に作業時間の記録をしています。 なので週末の業務終了時点では1週間分の作業時間の合計が記録されているはずです。 大元の TODO リストとなっている TODO.org に1週間の作業結果を反映していきましょう。 作業時間の記録を転記して、もし完了になったタスクがあればステータスを変更します。 #+SEQ_TODO: TODO(t/!) | DONE(d) #+PROPERTY: Effort_ALL 1:00 2:00 3:00 5:00 8:00 13:00 *** 新規配送システム導入 **** DONE 購入者は新規配送方法を指定して商品購入を行うことができる :PROPERTIES: :Effort: 5:00 :END: :CLOCKING: CLOCK: [2023-12-04 月 17:00]--[2023-12-04 月 18:12] => 1:12 CLOCK: [2023-12-04 月 13:00]--[2023-12-04 月 15:00] => 2:00 CLOCK: [2023-12-04 月 10:30]--[2023-12-04 月 12:00] => 1:30 CLOCK: [2023-12-04 月 09:30]--[2023-12-04 月 10:00] => 0:30 :END: **** DONE 出品者は新規配送方法に利用できる配送用ラベルを発行できる :PROPERTIES: :Effort: 8:00 :END: :CLOCKING: CLOCK: [2023-12-07 木 09:00]--[2023-12-07 木 10:00] => 0:30 CLOCK: [2023-12-06 水 16:00]--[2023-12-06 水 18:00] => 2:00 CLOCK: [2023-12-06 水 11:00]--[2023-12-06 水 12:00] => 1:00 CLOCK: [2023-12-05 火 15:30]--[2023-12-05 火 18:00] => 2:30 CLOCK: [2023-12-05 火 10:00]--[2023-12-05 火 12:00] => 2:00 :END: **** TODO 出品者は新規配送方法を利用して配送を実施することができる :PROPERTIES: :Effort: 13:00 :END: :CLOCKING: CLOCK: [2023-12-08 金 17:00]--[2023-12-08 金 18:00] => 1:00 CLOCK: [2023-12-08 金 11:00]--[2023-12-08 金 12:00] => 0:30 CLOCK: [2023-12-07 木 13:00]--[2023-12-07 木 14:30] => 1:30 :END: DONE になったタスクをそのまま TODO.org においておくと、 ファイルがどんどん巨大になってしまいます。Orgmode にはこれを Archive として他ファイルに移動してくれる機能がありますのでこちらを利用しましょう。 https://orgmode.org/guide/Archiving.html タスクに対して C-c C-x s を実行することで、あらかじめ指定したファイルにタスクを移動させることができます。 #+ARCHIVE: ~/dev/orgmode/archives/myArchive.org:: # Archive 先の指定。 *** 新規配送システム導入 **** DONE 購入者は新規配送方法を指定して商品購入を行うことができる まとめ 以上が Emacs Orgmode を利用したタスク管理の紹介になります。 冒頭にも話したことになりますがこのタスク管理を行うことで、 仕事の抜け・漏れを起こさなくなる。 自分のキャパを最大限活かせる。(キャパオーバーになるのも避けられる。) 日々の仕事に達成感を得られる。 こういった恩恵を受けることができると私は感じています。 本記事を読んで気になった方は一度試してみてはいかかでしょうか?? 明日の記事の担当は UXD(UI, UX の改善に主に取り組んでいるチーム)の新井さんです。お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは!UIUXデザイナーの和田です。 こちらは Enigmo Advent Calendar 2023 の12日目の記事です。 この記事では、 BUYMA のサービスやアプリ・WebサイトのUIUXをより良くすることを目的に進めているUXリサーチ(ユーザ調査)についてご紹介します。 1.なぜUXリサーチ(ユーザ調査)をするのか? より多くの方に BUYMA のファンになっていただけるように【真にユーザの体験価値にマッチしたお買い物体験を実現する】ためには、より深い顧客理解に基づいた PDCA が必要だと考えています。 そのために、現在 BUYMA をご利用いただいているお客さまの利用文脈・背景(ユー ザコン テキスト)を深掘りして、ユーザ インサイト についての理解を深められるような調査を心がけています。 「どのようなお客さまなのか?」 → ユーザ属性に加えて、家族構成や生活スタイルなど 「お客さまがどのような価値観・嗜好をお持ちなのか?」 → ファッションアイテム全般についての価値観・潜在ニーズや行動特性 「どのようなことを BUYMA に期待してくださっているのか?」 → BUYMA に対するニーズや期待値 「 BUYMA について期待と現状のギャップはどのようなところにあるか?」 → BUYMA 利用における AS-IS(現状)とTO-BE(理想)のギャップ 上記のような点について、 定量 的・定性的それぞれの側面から現状把握と改善のヒントを得られるように、 ユーザアンケートとユーザインタビューを組み合わせて調査を実施しています。 2. BUYMA のUXリサーチ体制 弊社では、UIUXデザイナーとデータアナリストの調査チームでUXリサーチを企画・進行しています。 またテーマによって、他部署のメンバーと連携・協働することで調査に幅広い視点が盛り込めるように工夫を進めています。 複数の職種からなるチームで調査に取り組むことで、UXリサーチの企画→実施→分析→施策化→デザイン・実装→効果検証・・・のように調査メンバーが主体的に PDCA を回しやすい体制ができていると感じています。(UXリサーチ専任ではないため、各自がその他の業務を調整しながらUXリサーチを進行するため、調査期間中はどうしても忙しくなってしまうこともあります。) 3.調査テーマに沿ったUXリサーチ企画・進行のながれ 弊社では、設定した調査テーマに沿ってユーザ調査を実施しています。 1回のユーザ調査につき、約3〜4ヶ月で企画から分析までが完結するようなスケジュール感で予定を立てて進めています。 以下、調査の流れをご紹介します。 1.企画・準備(1.5ヶ月) 調査テーマは、社内で相談して関心の高いテーマを設定するようにしています。 テーマに沿って対象者の選定や、 ヒアリ ングポイントを整理して、 アンケート設問やインタビューシナリオについて、レビューを繰り返して準備を進めています。 2.アンケート・インタビュー実施(1ヶ月) アンケートはテーマや対象者に合わせて、メールのほか、LINEやアプリからも回答いただけるようにしています。 アンケート設問の中でインタビューにご協力いただける方を募る形で、インタビューにつなげられるようにしています。 アンケート回答期間が約1週間、インタビューセッティング期間が1週間、インタビュー実施期間が約2週間と、とにかく忙しい1ヶ月間です。 3.分析・まとめ(1.5ヶ月) インタビューの実施が完了した後に、分析とまとめ作業をしています。 アンケートとインタビューそれぞれについて、分析結果・ ヒアリ ング結果を記事にまとめて社内で共有するようにしています。 最後にチームで KPT の形式で調査のふりかえり会を実施して、調査の1サイクルが完了となります。 4.UXリサーチに利用しているユーザセグメントの分類軸 UXリサーチでは、ユーザや利用傾向を把握・グルーピングをしやすくするために、独自のユーザセグメント分類軸を定義して利用しています。 アンケート配信対象者を選定する際にも、 調査テーマに沿って「直近利用(R)・利用頻度(F)・価格志向(M)・年代(U)」の分類軸を選定条件に利用しています。 直近利用(R) 最終購入日時(直近購入)または会員登録日時を基準にした直近利用に関する軸 利用頻度(F) 最終購入から遡った1年間における購入頻度の軸 価格志向(M) 生涯購入における最高購入金額の軸 ※どんな価格帯のアイテムに関心の高いユーザなのか分類するために生涯購入金額ではなく最高購入金額で分類しています 年代(U) ライフスタイル変化に合わせた年代グループ ※ご興味のある方は、ぜひ昨年のAdvent Calendar 2022の記事もご覧ください! tech.enigmo.co.jp 5.傾向・改善ヒントを掴むためのユーザアンケート 前述した通り、RFMUのユーザセグメント分類軸を利用して、調査テーマに沿ったアンケート配信対象者を選定してアンケート配信をしています。 アンケートの設問は以下のような形式の設問を利用しながら、 定量 的な傾向、定性的なご意見をキャッチアップできるような設問体系にしています。 アンケートツールは、 Google フォームを利用しています。 単一選択形式/複数選択形式の設問 仮説から選択肢を用意できるような場合は選択形式を利用して 定量 的に傾向を掴めるようにしています。 フリーアンサー(自由記述)設問 選択肢に挙げられていなかったユーザ インサイト を掴むために、積極的にフリーアンサーを用いてユーザのリアルな声をいただけるようにしています。 仮説として全く挙げられていなかったご意見を多数いただくこともあり、フリーアンサー回答の集計も非常に重要だと考えています。 段階評価の設問 尺度を測りたい場合に段階評価を用いています。 アンケート回答は、回答全体の分析に加えて、セグメント別の分析や回答を掛け合わせたクロス集計による傾向分析も実施しています。 6.ユーザ インサイト を深掘るためのユーザインタビュー アンケート集計後は、回答者のユーザセグメント傾向を分析して、インタビューしたいユーザグループをいくつかに分類するようにしています。 それぞれのユーザグループについて数名ずつインタビューを実施できるように、アンケート回答や BUYMA のご利用傾向をもとに候補者の方にお声がけをさせていただくようにしています。 弊社ではZoomを利用した60分のオンラインインタビューを実施しています。 オンラインでご参加いただけるため、地方にお住まいの方やテレワークで休憩中の方、お仕事先からご参加いただける方など・・・、本当にさまざまなご職業・ライフスタイルの方にご参加いただけています! インタビューの内容は主に以下のセクションごとに進行しています。 また、後述するユーザプロフィールシートに基づいて注力したい ヒアリ ングポイントを中心にお話をお聞きするようにしています。 ユーザーインタビューの内容 ユーザ自身について 日中の過ごし方やファッション情報のチェック先などをお聞きしています。 ファッション全般の嗜好やお買い物について 好きなブランドや普段のお買い物の傾向についてお聞きしています。 BUYMA 利用について BUYMA 認知・利用のきっかけやご利用の体験談などをお聞きしています。 調査テーマ ヒアリ ング 調査テーマに沿った内容について、価値観やニーズ、普段の行動、 BUYMA におけるペイン・ゲインなどをお聞きしています。 コンセプト ヒアリ ング Figma のプロトタイプを利用して新機能やUIのコンセプトについての ヒアリ ングを実施しています。 7.ユーザープロフィールシートの作成と活用 インタビュー対象者ごとにユーザの情報がまとまったユーザプロフィールシートを作成するようにしています。(個人が特定されるような情報は含まれていません。) また、ユーザプロフィールシートに対象者のRFMUユーザセグメント分類軸も付与しておくことで、近しい傾向を持つユーザを探しやすくする工夫をしています。 ユーザプロフィールシートの内容 ユーザ基本情報:ユーザ属性やRFMU分類軸 BUYMA 利用情報: BUYMA における利用・購入に関する情報 アンケート回答:アンケートのご回答まとめ インタビューレポート:インタビュー議事録・記録など ユーザインタビューの前に、あらかじめユーザプロフィールシートを作成することで、 ヒアリ ングポイントを精査して対象者の方に合わせたインタビューができるようにしています。 また、ユーザ インサイト について深掘りしたくなった際に、対象となるユーザプロフィールシートを探して活用できるように、 UXリサーチ資産としての整備を進行しています。 8.UXリサーチから得るもの 調査が完了した後、チームメンバーで分析結果・まとめをもとに施策ブレストを実施しています。 そこから、サービス・UIUX改善につながる施策化を進めるかたちで調査を役立てるようにしています。 実際に、UXリサーチで得られた インサイト から生まれた機能やサービスも BUYMA にどんどん盛り込まれています! さらなる BUYMA のUX向上に期待していただけますと幸いです! 明日の記事の担当は・・・ 【 BUYMA のサーバーサイドアプリケーション開発をされている寺田さん】です! お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、今年の4月に新卒で入社したデータエンジニアの中村です。 この記事は Enigmo Advent Calendar 2023 の11日目の記事です。 入社してからは、社内のデータを利用者がより使いやすくなるように、データ基盤の整備・データ連携を進めております。 最近弊社のデータ基盤をTerraformによる管理に移行するタスクに取り組んでいるので、どのような ディレクト リ構成を採用したか説明していこうと思います。 データ基盤をこれからTerraform化しようとしている方に有益な情報となれば幸いです。 目次 背景 ディレクトリ構成 構成の説明 各ディレクトリの役割 stateファイルの分割粒度 工夫したポイント 環境差分の切り出し BigQueryのリソースはyamlで管理 今後の展望 最後に 背景 弊社のデータ基盤は GCP 上にあり、BigQueryをはじめとした様々なサービスを活用してデータを収集しています。 GCP のサービスを利用する際に、これまではコンソール画面からボタンをポチポチしてリソースを作成していたのですが、それだと以下のような課題があります。 作業ミスによって誤ったリソースが作成されてしまう。 GUI での設定だと再現性が低くなってしまう。 リソースの管理が大変。(このリソース消しちゃって大丈夫だっけ?いつ誰が作ったんだ??みたいな状態が起こる。) 同じような設定のリソースを繰り返し作成するのに効率が悪い。 これらの課題を解決するため、IaCサービスであるTerraformを活用し、 GCP 上のサービスをコードベースでの管理に移行することに決めました。 ディレクト リ構成 結論から行くと、色々試行錯誤した末に決定した弊社データ基盤の ディレクト リ構成のベストプ ラク ティスはこちらになります。 terraform ├── root (ルートモジュール) │ ├── bigquery │ │ ├── environments │ │ │ ├── development │ │ │ │ ├── terraform.tfbackend (開発環境のstateファイルのパス) │ │ │ │ ├── terraform.tfvars (開発環境の変数) │ │ │ │ └── dataset │ │ │ │ ├── <dataset>.yaml (yamlでデータセット毎に管理) │ │ │ │ ・・・ │ │ │ └── production │ │ │ ├── terraform.tfbackend (本番環境のstateファイルのパス) │ │ │ ├── terraform.tfvars (本番環境の変数) │ │ │ └── dataset │ │ │ ├── <dataset>.yaml │ │ │ ・・・ │ │ ├── main.tf │ │ ├── variables.tf │ │ └── versions.tf │ ├── datastream │ ├── <service_name> │ ・・・ └── modules(子モジュール) ├── bigquery ├── datastream ├── cloud_monitoring ├── secret_manager ├── <service_name> ・・・ 構成の説明 各 ディレクト リの役割 今回作成した構成は、大別するとroot ディレクト リとmodules ディレクト リに分かれており、各役割は以下の通りです。 root: modules ディレクト リで定義したモジュールを呼び出す modules: 再利用性の高いモジュールを配置 datastreamの例だと、以下のように、modulesで定義した複数のモジュールをrootから呼び出すことでリソースを定義しています。 module "secret_manager" { source = "../../../modules/secret_manager" ・・・ } module "monitoring" { source = "../../../modules/monitoring" ・・・ } module "data_stream" { source = "../../../modules/datastream/postgresql" ・・・ } stateファイルの分割粒度 この構成では、stateファイルはリソース毎に分割しています。 stateファイルの粒度が大きすぎると、その分リリース毎の影響範囲が広くなってしまいます。 現状そこまで管理対象が多くないので、1環境1stateファイルのようなモノリシックな構成でも良かったですが、今後スケールすることを想定して、管理が煩雑にならないようにリソース粒度で分割しました。 工夫したポイント ここまで聞くと、意外とスタンダードな構成だと感じた方も多いのではないでしょうか。 しかし、いくつか工夫した点があるのでそちらも説明していきたいと思います。 1. 環境差分の切り出し リソース毎に分割する構成の場合、よくあるのが以下の構成です。 ├── root │ ├── bigquery │ │ ├── development │ │ │ ├── main.tf │ │ │ ・・・ │ │ └── production │ │ ├── main.tf │ │ ・・・ │ ├── <service> │ │ ├── development │ │ └── production この構成だと、「developmentをコピペしてproductionを作成して、差分だけ修正して・・」と二度手間になってしまいます。 その無駄な作業を省くために、環境毎で中身の変わらないファイルは共有する構成にしました。 ├── root │ ├── bigquery │ │ ├── environments │ │ │ ├── development │ │ │ └── production │ │ ├── main.tf │ │ ├── variables.tf │ │ └── versions.tf この構成のメリットは、環境毎の設定値差分だけenvironments ディレクト リに記載すればその他共通のファイルを繰り返し作成する必要がないので、コーディング量が減らせるところです。 また、開発環境だけあるリソースを作成したいといった場合は、tfvarsとモジュール側でうまく吸収することで環境差分にも対応できます。 Workspaces の利用も考えましたが、公式では開発・本番環境毎に分けるような利用方法は非推奨だったので、今回は利用を見送りました。 2. BigQueryのリソースは yaml で管理 データ基盤の中でも中核をなすBigQueryの管理についてですが、一つのtfvarsで管理したいとなるとファイルが膨大になってしまします。 そこで、デー タセット 毎に yaml ファイルを作成することで見通しをよくしました。 本来tfvarsで全てのデー タセット を管理すると数千行は当たり前に行くところを、以下のようにデー タセット 毎に設定ファイルを分割することで管理が非常に楽になりました。 dataset_id : "hoge" description : "hogehoge" location : "US" access_roles_group_by_email : "hoge@hoge.co.jp" : [ "READER" ] access_roles_user_by_email : "hogehoge@hogehoge.co.jp" : [ "READER" ] Terraformでは、 yamldecode 関数が用意されているので、main.tf側で以下のように記述することでTerraformのmapとして扱うことができます。 locals { yaml_files = fileset("./environments/${var.env}/dataset", "*.yaml") datasets = { for file in local.yaml_files : element(split(".", basename(file)), 0) => yamldecode(file("./environments/${var.env}/dataset/${file}")) } } 今後の展望 まだTerraform化できていないサービスがあるので、引き続き完全Terraform化を目指していきたいと思います。 また、リリース作業は現在手動で行なっているので、CI/CD環境も整えて作業効率を高めていきたいところです。 最後に データ基盤のTerraform ディレクト リ構成に関する記事はネット上を探しても事例が少なく、ゼロから作るのは大変苦労しました。少しでも参考になった部分があれば幸いです。 最後までご覧いただきありがとうございました! 明日の記事の担当は、UIUXデザイナーの和田さんです。お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、データサイエンティストの髙橋です。業務では企画/分析/ 機械学習 モデル作成/プロダクション向けの実装/効果検証を一貫して行っています。 この記事では 2023/11/01 に公開した AIでさがす 機能の実現にあたり工夫した点について紹介します。本機能では OpenAI の Chat Completions API ( Web 版の ChatGPT の API 版のようなもの)を活用しているため、同 API 関連での工夫が中心となります。 この記事は Enigmo Advent Calendar 2023 の 9 日目の記事です。 AIでさがす機能の紹介 素早い機能公開とコスト削減の工夫にフォーカスした理由 素早い機能公開のため工夫 おすすめ商品以外の質問には回答できなくする RAG は行わずシンプルに検索結果を返す仕組みとする API に渡す過去のメッセージの組み立てをアプリ側でやらないこととする コスト削減のための工夫 一部のメッセージ群を API 呼び出し時は除外する プロンプトに質問と回答の例を含めないこととする まとめ 追記:2024/03/13 AWS Eコマース x 生成AI イベントに登壇しました! 追記:2025/12/18 AI でさがす機能をリニューアルしました! AIでさがす機能の紹介 AIでさがす機能は BUYMA の Web サイト・アプリの検索機能の 1 つとしてボタンを追加しており、Web サイトの場合は以下のボタンを押下すると AIでさがす画面に遷移します。 ユーザーは事例から質問するか、アイテムのカテゴリを選択した上でフリーワードで質問するかのどちらかからチャットを始めることができます。事例は、アイテムのカテゴリとフリーワードの文章をあらかじめ弊社で作成したものです。 例えば、レディースアイテムカテゴリを選択した上で「30代におすすめの ハイブランド レディースバッグ教えてください」と入力してメッセージを送信すると、以下のようにいくつかの商品が表示されます。 上記画面において、商品画像を押下すると該当の商品詳細画面が開き、「もっと見る」を押下すると表示されているおすすめ(シャネル チェーンバッグ など)を検索キーワードとした検索画面が開きます。 さらに、表示された商品群の末尾に以下のように「選んだ理由を聞く」ボタンが表示されており、押下すると選んだ理由が表示されます。 理由も表示できるようにすることでユーザーに納得感を得てもらったり、理由からこういう商品も良いかもと思い検索してもらったりすることを狙っています、また、弊社では FISS Project というファッションのプロが一緒に服を選んでくれるサービスも運営しており、より要望に特化した商品を選んでほしいと思われた方々の同サービスへの 流入 増加も狙いの1つです。そのため、以下のように理由表示後に同サービスの紹介も表示されます(カテゴリとしてメンズアイテムを選択した場合のみ表示されます)。 素早い機能公開とコスト削減の工夫にフォーカスした理由 目次を見ていただくと分かるように素早い機能公開とコスト削減に関する工夫が中心です。この2つにフォーカスした理由は以下のとおりです。 AIでさがす機能は他社の類似事例が少なく、どの程度の効果が見込めるか予測が難しかった このような機能はどういったものであれば BUYMA の会員にとって有益であるのか予測が難しかった そこで、可能な限り低コストで素早く公開し 分析→改善 のサイクルを回すことにより、どのようなものが有益か、どの程度効果が出そうなのかを予測しやすくしようと考えました。 結果として、本格的に開発を開始してから約6ヶ月で公開することが出来ました。なお、この期間にはソフトウェアエンジニアの方によるチャット画面周りのフロント/バッグエンド開発も含んでいます。 また、具体的な数値は出せませんがコストもかなり抑えた状態で公開できました。 素早い機能公開のため工夫 まずは素早い機能公開のための工夫を3つ紹介します。 おすすめ商品以外の質問には回答できなくする 企画の初期段階では商品のおすすめ以外の質問にもある程度対応できるようなチャットボットにすることを考えていました。例えば、 BUYMA 利用時の QA でよくある 「関税がかかるのか?」 という質問に回答できるようにするなどです。しかし、それを実現しようとすると誤った情報を回答させないために BUYMA 公式の QA ガイド を embedding 化し、ユーザーの質問と類似した文書を抽出、その文書をプロンプトに組み込み回答させる、などが必要だろうと考えました。しかし、この作業自体に時間がかかる上に様々な質問に対して誤った情報を回答しないかを検証する時間もかかるため、思い切っておすすめの商品以外の質問には回答できないようにしました。 これを実現するために、ユーザーからのメッセージに対して Chat Completions API により回答を生成させる前に、メッセージがおすすめの商品に関するものかどうかを分類しそれ以外の質問の場合は以下のように固定したメッセージを返すようにしました。 なお、分類自体も Chat Completions API により行うこととしました。これは分類モデルを作成する手間を削減できると考え検証したところ、Chat Completions API による分類でも十分な精度が得られたためです。そのため、ユーザーのメッセージ 1 つに対して最大で 2 回 Chat Completions API を呼び出しています。 RAG は行わずシンプルに検索結果を返す仕組みとする RAG (Retrieval-augmented Generation) とは、外部データを ChatGPT などの大規模 言語モデル に与えることでそのデータに基づいた回答を生成させる手法です。 記事執筆時点(2023/12/09)で採用している方法はシンプルで、 ChatGPT におすすめ商品をいくつか生成してもらい、それぞれを検索キーワードとした検索結果内の複数個の商品 URL と検索結果の URL を表示するというものです。 企画初期段階では RAG のようなやり方(例えば商品情報を embedding 化し、回答の embedding と類似する商品を取得して画面に表示するなど)を検証していたのですが、以下の課題から RAG の方向性を突き詰めると時間がかかりそうなことを実感し、シンプルな方法を採用することとしました。embedding 化には OpenAI の text-embedding-ada-002 を利用しました。 商品の メタデータ (商品名やカテゴリ、ブランド名など)を embedding 化したものと、ChatGPT の回答を embedding 化したものの類似度を取って上位の商品を見ると、回答とずれた商品がそれなりに出現する。 例えば、ChatGPT の回答では「オックスフォードシャツ」と記載があるが、類似度上位にはTシャツが出現することがありました。 上記のような問題があった場合に原因を特定することが難しい。 商品名やカテゴリ、ブランド名などの複数の商品 メタデータ を embedding 化しているため、どの メタデータ が原因なのか、特定の メタデータ の組み合わせが原因なのかなどを特定することが難しく、また特定できたとしても今度は別のパターンで問題が発生することがありました。これを対応していると、この部分の開発のみでそれなりの時間を要してしまうと考えました。 また、仮に embedding 化がうまくいったとしても、それをシステム化することにも時間がかかるだろうと考えました。具体的には、 BUYMA には 600 万品以上の商品があり、この数の embedding と類似度を高速に計算するには Vertex Matching Engine などの利用が必要であり、弊社ではまだ利用実績もなかったため検証・構築に時間を要すると考えました。その上、コストもそれなりにかかりそうであったためシンプルな方法を採用するに至りました。 なお、記事執筆時点(2023/12/09)では RAG を利用した回答生成を高速に実現できそうな機能が OpenAI の Assistants API や Google Cloud の Grounding for PaLM API としてアナウンスされており、今後はユーザーの利用状況を見ながらこれらの活用も検討予定です。 API に渡す過去のメッセージの組み立てをアプリ側でやらないこととする これは弊社のソフトウェアエンジニアとの連携にかかる時間を削減するための取り組みです。Chat Completions API では過去のユーザーと AI のメッセージのやり取りを API の呼び出し側で組み立てる仕様になっています。具体的には、 Open AI のドキュメント に記載のように API 呼び出し時の messages パラメータに過去のユーザーと AI のやり取りを渡す必要があります。 curl https://api.openai.com/v1/chat/completions \ -H " Content-Type: application/json " \ -H " Authorization: Bearer $OPENAI_API_KEY " \ -d ' { "model": "gpt-3.5-turbo", "messages": [ { "role": "system", "content": "You are a helpful assistant." }, { "role": "user", "content": "Who won the world series in 2020?" }, { "role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020." }, { "role": "user", "content": "Where was it played?" } ] } ' これを実現するための単純な方法は、アプリ側で画面に表示しているユーザーと AI のメッセージを取得し、 API 呼び出し時に渡すようソフトウェアエンジニアの方に依頼することです。しかし、そうすると過去のメッセージ群の一部を意図的に変更したい場合に、こちらの意図しないメッセージ群が渡されるるなどしてソフトウェアエンジニアの方との連携に時間がかかってしまうだろうと想定しました。 そこで、Chat Completions API をラップする独自 API を作成し、その API のレスポンスとして次の呼び出し時に渡すべきメッセージ群を返す仕様としました。具体的には、先程の Open AI のドキュメントの例を用いると Who won the world series in 2020? というメッセージで呼び出したときのレスポンスに、以下のように次の呼び出し時に渡すべきメッセージ群を含め、 { " past_messages ": [ { " role ": " user ", " content ": " Who won the world series in 2020? " } , { " role ": " assistant ", " content ": " The Los Angeles Dodgers won the World Series in 2020. " } ] } 次の呼び出し時は以下のようにユーザーのメッセージと過去のメッセージ群をパラメータとして渡す仕様の独自 API を作成しました。 { " message ": " Where was it played? ", " past_messages ": [ { " role ": " user ", " content ": " Who won the world series in 2020? " } , { " role ": " assistant ", " content ": " The Los Angeles Dodgers won the World Series in 2020. " } ] } この独自 API を作成したことでこちらの意図しないメッセージ群が渡されてしまうことを防ぐことが出来ました。また、後述しますが過去のメッセージ群の一部を意図的に変更することを行っており、それを独自 API のレイヤーで行うことでソフトウェアエンジニアの方に意図的な変更の実装を依頼しこちらで確認するなどの連携が不要となり、開発期間の短縮に繋がりました。 コスト削減のための工夫 次にコスト削減のための工夫を 2 つ紹介します。ここで紹介するものは 1 回の Chat Completions API 呼び出し単位で見れば小さいものですが、 BUYMA の会員数は 1000 万人以上 であり 1 回の API 呼び出しが全体のコストに与える影響は大きいものとなります。どの程度効果が出るのか予測が難しく、また企画当初から全会員への公開を目指していたため少しのコスト削減でも検討することとしました。 一部のメッセージ群を API 呼び出し時は除外する Chat Completions API の料金は input tokens による従量課金性 であり、選んだ理由の回答が長文になりがちであるためそれに関するメッセージ群を API 呼び出し時は除外するようにしました。 例えば、ユーザーが「30代におすすめの ハイブランド レディースバッグ教えてください」というメッセージを送り、「シャネル チェーンバッグ / グッチ トートバッグ / プラダ ショルダーバッグ」というメッセージが返された後に、「選んだ理由を教えて」とメッセージを送ると以下のようなメッセージが返されます。 シャネル チェーンバッグ シャネルのチェーンバッグは、エレガントで上品なデザインが特徴です。高級感溢れる素材とクラシックなチェーンストラップが、大人の女性にふさわしい印象を与えます。また、シャネルのバッグはブランドの人気アイテムであり、長く愛用することができるため、コストパフォーマンスも高いです。 グッチ トートバッグ グッチのトートバッグは、洗練されたデザインと高品質な素材が魅力です。ブランドのアイコンであるGG柄やロゴプリントが施されたデザインは、一目でグッチと分かる存在感を持っています。また、トートバッグは容量も大きく使い勝手が良いため、日常使いから旅行まで幅広く活躍します。 プラダ ショルダーバッグ プラダ のショルダーバッグは、モダンで洗練されたデザインが特徴です。シンプルながらも上品さを兼ね備えたデザインは、幅広いスタイルに合わせやすく、長く愛用することができます。また、 プラダ のバッグは高品質な素材と丁寧な作りが評価されており、耐久性も抜群です。 このメッセージの token 数は約 448 であり、以降の Chat Completions API 呼び出し毎に約 $0.00067 のコスト *1 がかかることとなります。先程述べたように BUYMA の会員数は 1000 万人以上のため 1 呼び出しあたり約 $0.00067 のコスト増加でも大きな影響となります。そこで、過去の選んだ理由に関するメッセージ群を API 呼び出し時に除外することとしました。 具体例として、ユーザーが以下の順番でメッセージを送ったとし 30代におすすめの ハイブランド レディースバッグ教えてください 選んだ理由を教えて 小旅行で使えるおすすめレディースバッグは? この時のメッセージ群が以下のようになったとします。 { " messages ": [ { " role ": " user ", " content ": " 30代におすすめのハイブランドレディースバッグ教えてください " } , { " role ": " assistant ", " content ": " シャネル チェーンバッグ / グッチ トートバッグ / プラダ ショルダーバッグ " } , { " role ": " user ", " content ": " 選んだ理由を教えて " } , { " role ": " assistant ", " content ": " シャネルのチェーンバッグは、... / グッチのトートバッグは、... / プラダのショルダーバッグは、... " } , { " role ": " user ", " content ": " 小旅行で使えるおすすめレディースバッグは? " } , { " role ": " assistant ", " content ": " トートバッグ / ショルダーバッグ / リュックサック " } ] } ' この時、次の API 呼び出し時に渡す過去のメッセージ群を以下のように理由に関するやり取りを除外したものとしました。 { " messages ": [ { " role ": " user ", " content ": " 30代におすすめのハイブランドレディースバッグ教えてください " } , { " role ": " assistant ", " content ": " シャネル チェーンバッグ / グッチ トートバッグ / プラダ ショルダーバッグ " } , { " role ": " user ", " content ": " 小旅行で使えるおすすめレディースバッグは? " } , { " role ": " assistant ", " content ": " トートバッグ / ショルダーバッグ / リュックサック " } ] } なお、上記の除外処理はアプリ側でも可能ですが、「 API に渡す過去のメッセージの組み立てをアプリ側でやらないこととする」にも記載したようにデータサイエンティストとソフトウェアエンジニア間の連携にかかる時間を削減するために、独自 API のレイヤーで行う仕様としました。具体的には、「選んだ理由を教えて」というメッセージで独自 API を呼び出した時のレスポンスは、以下のように理由に関するメッセージ群を除外したものになる仕様としました。 { " past_messages ": [ { " role ": " user ", " content ": " 30代におすすめのハイブランドレディースバッグ教えてください " } , { " role ": " assistant ", " content ": " シャネル チェーンバッグ / グッチ トートバッグ / プラダ ショルダーバッグ " } , ] } そして、次に「小旅行で使えるおすすめレディースバッグは?」というメッセージで独自 API を呼び出す時は、上記の理由に関するメッセージ群が除外された過去のメッセージ群をそのまま渡す仕様としました。 { " message ": " 小旅行で使えるおすすめレディースバッグは? ", " past_messages ": [ { " role ": " user ", " content ": " 30代におすすめのハイブランドレディースバッグ教えてください " } , { " role ": " assistant ", " content ": " シャネル チェーンバッグ / グッチ トートバッグ / プラダ ショルダーバッグ " } , ] } プロンプトに質問と回答の例を含めないこととする この機能ではユーザーからのメッセージに対して弊社システム側でプロンプトを追加し、Chat Completions API にリクエストを送信しています。そのプロンプトについて、開発初期は以下のようにユーザからのメッセージと AI からのメッセージの例をいくつか含めることでこちらの意図したフォーマットで回答を生成させ、複数のおすすめを分割していました(以下のプロンプトは実際に利用しているものとは異なります)。 「おすすめ1 / おすすめ2 / おすすめ3」というフォーマットで回答してください。 以下が質問と回答の例です。 質問:30代におすすめのハイブランドレディースバッグ教えてください 回答:シャネル チェーンバッグ / グッチ トートバッグ / プラダ ショルダーバッグ 質問:小旅行で使えるおすすめレディースバッグは? 回答:トートバッグ / ショルダーバッグ / リュックサック しかし、ある程度開発が進んだ時点でコストを見積もったところ、予想以上にコストがかかる結果となりました。そこで、上記のプロンプトの 以下が質問と回答の例です。 以降を除外することを検討しました。 以下が質問と回答の例です。 以降の token 数は約 140 であり、 Chat Completions API 呼び出し毎に約 $0.00021 のコストがかかることとなります。先程述べた通り BUYMA の会員数は 1000 万人以上のため 1 呼び出しあたり約 $0.00021 のコスト削減でも見積もり上のコスト削減は大きなものでした。 一方で、実際に上記のプロンプトの 以下が質問と回答の例です。 以降を除外し、様々なユーザーのメッセージパターンで検証してみたところ、おすすめされるものにはほとんど変化がないもののフォーマットにブレが生じ、おすすめがうまく分割出来ないことがありました(半角の / が全角の / になるなど)。 そこで、うまく抽出できなかったパターンに対し 正規表現 などの後処理を利用して対応しました。 結果として、公開から1ヶ月程度が経過した時点でフォーマットのブレによりおすすめが抽出出来ない問題は1件も発生しておらず、コストを削減しつつ安定して回答を生成することが出来ています。 まとめ 先日公開した AIでさがす機能実現のための工夫を紹介しました。特に素早い機能公開とコスト削減にフォーカスすることで安価に早く公開することが出来ました。今後は、利用状況を分析し RAG などにより BUYMA データとの連携を強化したり、Fine-tuning などによりさらにユーザーにとって有益な回答を生成させたりなどを検討予定です。 追記:2024/03/13 AWS Eコマース x 生成AI イベントに登壇しました! AWS の招待制のイベントにてこのブログの内容を発表いたしました。 https://x.com/enigmo_tech/status/1776132049579262461 ブログには記載していないプロジェクトの進め方や体制についても記載しております。 speakerdeck.com \イベント登壇しました💁♂️/ 弊社データサイエンティストが AWS 社主催のEコマース×生成AIイベントに登壇し、 生成AIの ユースケース として、 BUYMA の商品検索の新機能「AIでさがす」を紹介しました✨ pic.twitter.com/aRGnxI9Psx — EnigmoTech (@enigmo_tech) 2024年4月5日 追記:2025/12/18 AI でさがす機能をリニューアルしました! より良い検索体験提供のために、AIテクノロ ジー グループの吉田さんに機能をリニューアルいただきました。以下記事に内容が記載されていますため、ぜひご覧ください! 株式会社 エニグモ すべての求人一覧 hrmos.co *1 : 当機能の作成期間(2023/05 ~ 2023/11)では gpt-3.5-turbo の 1K tokens あたりの料金は $0.0015 であったためその数値を用いて計算しました。
こんにちは、エンジニアの川本です。 主に BUYMA の決済・配送を担当しているチームでバックエンドの開発をしています。 この記事は Enigmo Advent Calendar 2023 の 6 日目の記事です。 昨今の生成AIブームでChatGPTが注目を集めておりますが、私もChatGPTを積極的に業務に活用しています。毎日ChatGPTに質問することが当たり前になりつつあります。 今回は、実際の業務でChatGPTと Google Apps Script(GAS)を組み合わせてデータ収集を効率化できた事例を紹介します。 背景 弊社では各 バッチ処理 の実行時間が規定の時間よりも長い場合に Gmail でアラートメールを送信するシステムが稼働しているのですが、ある案件で過去数ヶ月のアラートデータを参照する必要が生じました。 しかし、アラートデータはDBに保存されておらず、 Gmail に直接送信されているため、 SQL で直接データを参照することが難しい状況でした。 DBに新しいテーブルを作成してアラートデータを保存する案も検討しましたが、データが十分に蓄積されるまでには半年近く時間がかかりそうでした。 長期的な解決策としては有用かもしれませんが、今回は案件の都合で即座にデータが必要でした。そこで、 Gmail に送信されたアラートメールからGASを利用してデータを抽出し、 スプレッドシート にまとめる方法を考えることにしました。 とはいえGASは未経験だったので1から調べながら実装するのは手間だなと感じ、物は試しにChatGPTに実装させてみました! ChatGPT✖︎GASの活用 実際にChatGPTに入力したプロンプトと、ChatGPTが出力したGASについて紹介します。 ChatGPTに入力したプロンプト あなたのGmailのメールボックスには以下のフォーマットのメールが届いています。 2023年1月1日から2023年3月30日までに届いた下記メールからバッチ名(batch_id)、実行状態(run_status)のデータを抽出し、 スプレッドシートにまとめるためのGoogle Apps Scriptを作成してください。 # メールのフォーマット ## メールタイトル `[CRITICAL]過去1時間止まっているバッチを検出` ## メール本文 過去1時間エラーで停止しているか、実行し続けているバッチを検出しました。 状態を確認してください。 | batch_id | start_date | run_status | | Batch1 | Aug 17 2023 04:51PM | ERROR | | Batch2 | Aug 17 2023 10:01AM | RUNNING | | Batch3 | Aug 17 2023 01:04AM | RUNNING | | Batch4 | Aug 17 2023 15:04AM | ERROR | ChatGPTが作成したGAS function extractStopBatchIdStatusPair() { // メールタイトル var subjectQuery = "[CRITICAL]過去1時間止まっているバッチを検出" ; // 対象期間 var startDate = "2023/01/01" ; var endDate = "2023/03/30" ; // 対象期間のアラートメールを取得するクエリ var query = "subject:" + subjectQuery + " after:" + startDate + " before:" + endDate; // メールスレッドを取得 var threads = GmailApp.search(query); // スプレッドシートを指定 var spreadsheet = SpreadsheetApp.openById( "スプレッドシートID" ); var sheet = spreadsheet.getSheetByName( "シート名" ); sheet.clearContents(); // 抽出したアラートデータ var values = [] ; // メールスレッドごとに処理 for ( var i = 0; i < threads.length; i++) { var thread = threads [ i ] ; var messages = thread.getMessages(); // メッセージごとに処理 for ( var j = 0; j < messages.length; j++) { var message = messages [ j ] ; var body = message.getPlainBody(); var subject = message.getSubject(); // メール本文から正規表現で対象データ抽出 var tableMatches = body.match(/ \ | ( [ ^ \ | ] +) \ | ( [ ^ \ | ] +) \ | ( [ ^ \ | ] +) \ |/g); if (tableMatches) { for ( var k = 0; k < tableMatches.length; k++) { var tableRow = tableMatches [ k ] .split( "|" ).map(item => item.trim()); // batch_id var batchId = tableRow [ 1 ] ; // run_status var runStatus = tableRow [ 3 ] ; values.push( [ batchId, runStatus ] ); } } } } // データをスプレッドシートに一括で書き込む if (values.length > 0) { sheet.getRange(2, 1, values.length, 2).setValues(values); } } ChatGPTが作成したGASの実行結果 以下のように期待通りに スプレッドシート にデータが出力されました! まとめ ChatGPTが生成するGASの精度は期待以上であり、ゼロからGASを調査・実装する手間を考えると、非常に時間を節約でき、効率的であると感じました。 また、高い精度の出力を得るためには、入力するプロンプトの内容が重要であることがわかりました。詳細に要件を整理し、適切なプロンプトを作成することで、ChatGPTによる出力もより精度が向上します。 初めての試行で高い精度を達成できなかったとしても、修正や リファクタリング の要望を的確に伝えることで、最終的には期待通りの結果を得ることができます。 今後は、プロンプトの適切な書き方を学びつつ、より高い精度の出力を得る方法を探求していきたいと考えています。 明日の記事はT&Sチームの杉山さんが担当しますので、お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、データアナリストの井原です。 この記事は Enigmo Advent Calendar 2023 の 5日目の記事です。 この記事では、不正対策のデータ分析から 機械学習 モデルの導入を決定した経緯についてご紹介します。 私は普段、データアナリストとして BUYMA の様々な業務にデータ分析担当としてかかわっています。 分析内容は施策の効果検証、ユーザーの行動分析、売れ筋商品の分析など マーケティング 的な要素が多く、ビジネス判断の正確性を上げることを目的とした業務が多いです。 機械学習 については、ある程度の知識と経験もありますが、本職のデータサイエンティストが社内にいることもあり、どちらかといえば、統計検定や因果推論などの手法を使用することが多いです。 ただ、今回は、不正対策をしているチームからの不正検知の精度を上げたいという相談があり、検証の結果として 機械学習 モデルを導入しようという結論になりました。 本記事では、その経緯について詳しくお伝えしたいと思います。 なお、この記事では、立ち上がり~検証内容の決定~検証実行と、検証を進めていったプロセスにフォーカスして記載します。 分析内容の詳細(プログラム、 アルゴリズム の説明など)は記載しませんので、ご了承ください。 依頼の経緯 近年、多くの ECサイト で決済の不正利用の被害が増えています。 決済の不正利用とは、フィッシングなどの悪質な手段によって他者のクレジットカード情報やサービスのログイン情報を不正に取得し、それを利用して換金性の高い商品を購入しようとする犯罪行為です。 BUYMA では、高額なブランド品が新品で購入できるということもあり、一 定量 、こういった不正利用による被害が発生してしまう状況にあります。 対策として、不正利用を防止するためのチームが社内に存在しており、日々対応を行なっています。 今回、そのチームから以下の依頼をデータ活用推進室にいただきました。 これまで、チームの知見を元に、不正疑いのある取引を検知する条件をルールベースで設定し、条件に該当した取引を一時的に止めて確認する(以下、買付保留 *1 )という対応を行っていた。 近年、不正利用のパターンが多様化してきており、不正の事前検知の難易度が上がってきている。 蓄積したナレッジとデータを活用し、データから不正な取引を止めることが出来ないか? ということで、こちらの依頼に自分が アサイ ンされ、データから不正検知を行うことが出来るか?を検証してみることになりました。 分析の進め方の策定 最初に実施したこととしては、検証内容を整理することです。 データ分析に関わっている方は想像つくと思いますが、分析のステップの中でおそらく一番重要なステップになります。 ここで、何を知りたいのか?それを知るための適切な方法は何なのか?を徹底的に考え、ビジネス側(今回は不正対策のチーム)と合意する必要があります。 目的は不正の早期検知ができることと明確でしたので、ここでは、検証方法について以下の3点を整理し、ビジネス側に提示しました。 精度の測り方 分析手法 検証に使用するデータ ひとつづつ、見ていきます。 1.精度の測り方 今回の案件では、精度をどのように定義するのか?が要所の一つでもありました。 単純に考えると、不正な取引を何件検知できたか?が指標になるように思われます。 この考え方であれば解決は簡単です。全ての取引を買付保留にしてしまえばよいのです。 全ての取引を買付保留にして、全て不正かどうかをチェックすれば、理論上、不正はすべて止められます。 しかし、当然ながらこの方法は取れません。 全ての取引を不正疑いとしてチェックすることはコスト的に現実的ではないですし、ユーザー からし ても取引完了までの時間が長くなるため、 ユーザビリティ ーが下がってしまいます。 今回のような検証内容の場合、見るべき指標は二つあります。 不正と予測した取引が実際に不正である確率(以下、買付保留的中率) 不正である取引を不正と検知できる確率(以下、検知成功率) ちなみに、 機械学習 の二値分類タスクでこの指標はよく出てきます。 (それぞれの指標は 機械学習 の言葉では適合率、再現率と言います。詳細は書きませんが二値分類の指標は他にも色々あったりしますので、知っておくだけでも便利です。) 図にしてみると以下のようになります。 買付的中率と検知成功率 基本的に、この指標は片側を改善すると片側が悪化する傾向があります。 例えば、検知成功率を上げるためには、買付保留の数を増やす必要がありますが、(きれいに不正だけを検知することは基本ないので)母数が増えて買付保留的中率は下がります。 検知成功率をあげようとしたときのイメージ 逆も同様で買付保留的中率は、基本的に母数を減らすことになるので検知成功率が下がります。 買付保留的中率をあげようとしたときのイメージ このあたりをどのラインで許容できるかは、ケースバイケースになります。 本ケースでも、二つの指標を総合的に見ながら、ビジネス側と方針を詰めていくこととしました。 2.分析手法 指標を決めましたので、どのように精度を改善していくか?という手法の選択に移ります。 選択肢としては、以下の二つの手法を考えました。 決定木分析で重要な要素を抽出し、ルールベース案を検討する。 LightGBMで 機械学習 モデルを構築し、不正予測スコアから推定する。 検証内容的に 機械学習 モデルを使った方が、精度は改善するだろうという感覚がこの時点でありました。 しかし、それでもルールベース案の検討を入れたのは、コスト的な観点からです。 経緯にも記載した通り、これまでも不正対策のチームではルールベースで買付保留を行うという運用を行ってきていました。 つまり、ルールベースであれば、すでにあるその運用に乗せることができ、開発などのコストも少なく短い期間での対応が可能でした。 一方の 機械学習 は全社での活用歴はいくつもあるものの、不正対策への適用は初めてでしたので、検証はともかく実行環境の構築にはコストも時間もかかります。 そのため、ルールベースでそれなりの精度改善が見込めるのであれば、わざわざ 機械学習 環境を構築する必要がなくなるというコスト的メリットがあるのです。 3.検証に使用するデータ 最後に検証に使用するデータの決定です。 幸いなことに、これまでの不正対策の取り組みのおかげでログは蓄積されていましたので、どの取引が不正だったのかという実データを使うことが出来ました。 過去ログは「学習用データ」と「検証用データ」に分割しました。 これは、学習させたデータでそのまま精度を検証すると精度が過剰に高く出て検証にならないので、学習させていないデータで検証を行う必要があるためです。 また、最新の1ヶ月のデータは、完全に学習用データから除外しました。 不正というのは一人の不正者が不正を一つだけ行うというものではないため、過去のデータ同士だとランダムに分割しても類似パターンを学習してしまう可能性があります。 そのため、未知のデータに対応しても精度がよいか検証する必要があり、最新1ヶ月分はそれ用の検証用データとしました。 まとめると以下のようなイメージになります。 データの用意 分析の実行 検証方法をビジネス側と合意しましたので、ここからは分析を実行していきます。 前項で書いた通り、「決定木分析からルールベース案の検討」と「LightGBMを使った 機械学習 モデルの構築」を行っていきます。 1.決定木分析からルールベース案の検討 途中の作業は省略しますが、決定木分析を行うと以下のようなアウトプットが出てきて、どのような変数・ 閾値 で分割すると、不正とそうではない取引をうまく見分けることが出来るかを可視化してくれます。 ※変数名や結果はダミーです。 決定木分析のイメージ 決定木の結果をそのまま使ったわけではないですが、重要そうな要素と 閾値 を見ながらルールベース案を4つほど作成しました。 また、決定木自体も予測を行うことが出来ますので、決定木によるモデル推定というパターンも用意しました。 これら5つのパターンと既存のパターン、合計6パターンについて、全組み合わせ(63パターン)での予測結果と精度を算出しました。 具体的な数字は出せませんが、この時点でモデル推定の精度が突出していました。 単純なモデル推定と既存パターンの比較では、買付保留的中率が数倍レベルで大きく改善し、検知成功率もかなり改善していました。 つまり、より少ない買付保留の数で多くの不正を検知することが、(シミュレーション上ですが)可能になったということになります。 また、モデル推定に既存ルールベースを組み合わせると、買付保留的中率は多少下がりますが検知成功率が大きく改善しました。 上記3つだけのイメージ図を書くと以下のようになります。 モデル推定と既存パターンの比較イメージ この結果からは、 機械学習 モデルの導入に対する期待値が高まりました。 決定木分析は精度があまり高くないと言われている手法ですので、この後実施するLightGBMならより改善するのでは?とも思えました。 また、指標を事前にすり合わせていたことで、この時点で数字を使った会話ができるようになりました。 数字は仮のものですが、例えば、以下のように整理ができます。 既存パターンでは1,000件買付保留を行い100件の不正検知が出来ている。 モデル推定+既存パターン+新ルール案すべてでは、5,000件買付保留を行い、200件の不正検知が出来る。 モデル推定+既存パターン+新ルールA案のみでは、2,000件買付保留を行い、195件の不正検知が出来る 数字でイメージがつくので、例えば、買付保留が5倍になっても不正検知を最大化するのか?多少の不正検知数が減っても、買付保留は2倍までに抑えたいか?などの議論を行うことができました。 2.LightGBMを使った 機械学習 モデルの構築 続いて、LightGBMを使って、 機械学習 モデルを構築しました。 ※LightGBMとはなんぞや?という説明は省略します。非常にメジャーな 機械学習 モデルですので、興味ある方は調べてみてください。 こちらは、シンプルに(LightGBMによる)モデル推定、既存パターンのみ、モデル推定+既存パターンで精度を比較しました。なお、決定木と比較するとやはりLightGBMのモデル推定の方が精度は高くなっていました。 ※予測スコアの 閾値 の検証なども行っていますが、これも省略しています。 最終的な結果だけ記載しますが、モデル推定+既存パターンでは既存パターンと比較して不正検知率が大幅に改善し、買付保留的中率もそれなりに改善することが確認できました。 買付保留件数も増加していたのですが、ビジネス側で許容範囲と確認いただきました。 コスト面を考えても大きな改善が見込めると検証できたので、 機械学習 モデルの導入に向けてプロジェクトが動くことになりました。 まとめ 本ケースで行った検証の大まかな流れは以上になります。 機械学習 モデルを実際に導入するには時間がかかるため、検証したルールベースの中で精度のよいものをまず導入し、 機械学習 モデルの導入は順次進めている状況になります。 今回は、明白な結果の差が出たため、導入の判断をすぐに行うことが出来ました。 機械学習 の精度がすごいというのはありますが、それとあわせてログとナレッジが大量に溜まっていたことが成功要因でした。 「Garbage in, garbage out」という言葉があります。 精度の高い アルゴリズム でも、ゴミデータを入れればゴミデータしか出てこないという意味ですが、今回は良質なデータのおかげで良質な結果がアウトプットされたと思います。 過去の取り組みまではデータアナリストはなかなか介入できません。 データアナリストとしては、これまでのビジネス側の蓄積された過去のナレッジをうまく ヒアリ ングして、データに変換していくスキルが重要かと思いますが、それらがうまくマッチした事例になれたのではないかと思いました。 本日の記事は以上になります。最後まで読んでいただきありがとうございました。 明日の記事の担当はエンジニアの川本さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co *1 : ※ BUYMA はCtoCの買付代行サイトのため、多くの場合、注文が入ったあとに出品者が商品を買い付けています。万が一、不正利用だった場合、商品買い付け後にキャンセルになると余分な在庫が発生してしまうため、取引のチェックを行うまで、出品者の方に一時的に買い付けを待ってもらう対応が「買付保留」という仕組みです。買付保留となる取引は確定した不正とは別物です。買付保留は不正疑いというニュアンスで読み進めていただくと分かりやすいと思います。
こんにちは、SE本部の hashino です。 現在、2年目の新卒入社のエンジニアです。BUY Domainに所属しており、 BUYMA の購入者向け機能を開発しています。 この記事は Enigmo Advent Calendar 2023 の 4 日目の記事です。 みなさんは振り返りをしていますか? 週ごとや月ごと、年単位......振り返りの頻度や粒度は人によってさまざまだと思います。 仕事の進捗や自己成長を確認し、改善点を見つけるためには振り返りは不可欠です。 エニグモ の開発部門の新卒やジュニアは、YWT方式で毎週振り返りをおこなっています。 詳しくは以下の記事をご覧ください。 tech.enigmo.co.jp この記事では、振り返りが苦手で週の振り返り記事の作成にとても時間がかかっていた私が実践した振り返りの手法とその結果とともに紹介していきます。 前提として、できるだけ技術的なこと以外にも仕事のやり方などを振り返ることが推奨されています。 例えばわかったことに Rubyにこのような仕様があることを知った。 だけ書いてしまうのは次のアクションにつながりにくく非推奨です。 振り返りの作成をその都度していく。 振り返り記事を週末に一気に書くのではなく、日々気づいたことをその都度まとめておくことで、時間の節約になります。 私の結果: 最初は順調でしたが、徐々に習慣が薄れ、日報との二重作業に疲れてしまいました。 私には微妙でした。 第 三者 目線になる。 自分ごととして捉えると何に気付けたのか、何が悪かったのかがわからなくなりませんか? 自分の仕事を客観的に見ることができるようになるために、第 三者 目線で振り返ります。 同じ悩みをもった後輩に今週の仕事をより良くするためのアド バイス を自分だったらどう声を掛けるかなどと考えるといいかもしれません。 私の結果: 成功や良い点を記述することが難しく感じましたが、失敗に焦点を当てると自分に合っているやり方でした。 振り返りの目的を忘れない 何か書かなければいけない〜!と焦ってしまい、余計になにも書けないことはありませんか? 振り返りは書くことが目的ではなく、自己成長や仕事の進捗を把握する手段です。 私の結果: 焦らず、冷静に振り返りの目的を忘れないように心がけました。 何を伝えたいかを明確にし、それに基づいて振り返りを進めることで、メンターとの振り返りでは有益な情報を得られるようになりました。 たくさん書いてフィードバックをもらう 振り返りの内容を先輩やチームの人と共有し、フィードバックを得ることが重要です。 新たな気づきや改善点が見つかります。 よかったことも悪かったこともどんなことでも書いてみましょう! 私の結果: もう少し書いてほしい!と言われても、最初は何を書けばよいのか戸惑いました。 しかし、小さな工夫や日々の取り組みを積極的に記述するようになりました。 私がどういう取り組みをして結果がどうであったかという記録が残っていくので、自分の傾向がわかるようになりました。 少しずつ振り返りが早くなり、自分の仕事の仕方の傾向を理解できるようになりました。 振り返りははじめこそ手間がかかるかもしれませんが、継続することで大きな成果を生むことができます。 同じような悩みを抱える方には、この経験が少しでも参考になれば幸いです。仕事の振り返りは、自分の成長を促進するための重要なステップであることを肝に銘じています。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、エンジニアの岡本です。 主に BUYMA の出品者向け機能のサーバーサイドの開発を行っています。 この記事は Enigmo Advent Calendar 2023 の1日目の記事です。 今年の アドベントカレンダー は生成AIの話題が各所でたくさん提供されると思いますが、私はソフトウェア開発における見積り、そして私がプロジェクトの計画を立てる上で実践していることについてご紹介しようと思います。 プロジェクト当初になんとなく立てた見積りが甘くて、リリース予定日が近づいているのに差し込みタスクが発生したり不備が発覚したりで、当初のスケジュールから遅れを出し、精神的に苦しい思いをしてしまうという経験が私にはあります。 見積りと計画立てに科学的なアプローチを取り入れたいという思いから、まず先人の知恵を求めました。 見積りを科学的に解説する本を探していたときに「ソフトウェア見積り 人月の 暗黙知 を解き明かす」という名著と出会いました。 ソフトウェア見積り 人月の暗黙知を解き明かす 作者: スティーブ マコネル 日経BP Amazon 通読した時点で、自身の経験と照らし合わせた上で以下の感想を持ちました。 100%正確な見積もりは不可能 3点見積りなどはちゃんとやろうとするのは難易度が高そう… なんでも良いので、各タスクの規模感をカウントできるようにすると良いらしい これらの感想を持った上で、以下では実際に現場で工夫して取り組んでいることを紹介します。 作業スケジュールを立てる上での工夫 タスクを切ってコードを書くだけではないので、以下の工程もスケジュールに組み入れるようにしています。 プロジェクト開始時点で「QA」「QA過程で判明した不具合修正」「リリース作業」もサイズを見積もっておく 遅れることはあっても、早まることは基本的にないということで1~2週間程度作業バッファを初めから入れておく 見積もりに慣れていなかった頃、この二つをやっていなかったために失敗したことがありましたが、これらを意識するようになってからは大幅にスケジュールがずれることは無くなった感覚があります。 ストーリーポイント 人月などを試したことがありますがいい感触でなかったので、ストーリーポイントを使ってみることにしました。 プロジェクト管理方法はJiraで行っています。Jiraは見積もりという観点においては「ストーリーポイントが定義できる」という点が良いと思っています。 基準となるタスクを決めて、それを2ポイントと定義しています。フィボナッチ数に基づいて、振り返りミーティングにてスプリント計画を行うときに各タスクにポイントを割り振っています。基準タスクよりもすぐ完了できそうなら1ポイント、それよりも1日くらいプラス必要なら3ポイント、1スプリント(今のチームでは1週間)内で完結できるなら5ポイントという感じです。 1個のタスクに当てられるポイントの最大値は13と決めているのですが、大体のタスクは2~5ポイントくらいになるように分割されています。 私がいるチーム(squad)ではおよそ1年以上試行錯誤をしながらこのような方針でスケジュールを管理するようになっています。もしかしたら他にもいい方法があるかもしれないですが、その場合は適切にアップデートしていきたいと思っています。 現時点での結論 100%正確な見積りは不可能であると割り切った上で、各タスクの規模を数えられる形で単位化する という考えのもと、スプリントを計画・実行しています。 見積りの重要性を理解しつつも、実装の正確性やスピードの向上に焦点を当てることがソフトウェアエンジニアにとって重要な課題であると考えています。 私からは以上です。お読みいただきありがとうございました。 明日の記事は採用チームの 廣島 さんが登場します。 株式会社 エニグモ すべての求人一覧 hrmos.co
~「Committee・新開発体制」導入後の成果と魅力~ 目次 新開発体制について 新開発体制導入の背景/なぜこのような体制にしたのか 新体制導入後に感じている進化や手ごたえ 当社エンジニア組織の魅力・キャリア まとめ エニグモ のエンジニア組織の新しいマネジメント体制と、新開発体制についてCOOの安藤、エンジニア リングマ ネージャー(EM)の木村・山本にインタビューした記事の続編です。 前編は、Committee体制(エンジニア組織をCTOや部長を頂点とするワントップ型ではなく、EM陣で構成する「Committee」という合議体を意思決定のトップ機関)ができた経緯や、どのような体制でエンジニア組織を運営しているかを中心にお話ししました。 後編は、エンジニア組織全体がどのような体制になり、 エニグモ のプロダクトを開発しているのか、実際に新しい体制になりどのような効果があったのか、当社エンジニア組織の魅力やキャリアなどについてお話しします。 前回の記事はこちらです。 tech.enigmo.co.jp 新開発体制について 大谷: Committee体制の取り組みの第一弾として、エンジニア組織・開発体制をDomain、Squad、Chapter体制(以下、新開発体制)に移行したかと思います。後編ではまずは、新開発体制の概要についてお聞きできればと思います。 木村: エンジニアの新開発体制は、実態としては チームトポロジーの概念( Spotifyモデル) を参考に組織設計しました。 プロダクトの開発を担う機能別のDomain( ドメイン )と呼ばれるチームがあり、それをさらに細分化・構造化する形で、縦割りチームのSquad(スクワッド)や、横串チームのChapter(チャプター)があります。 Squadは事業的なミッションが与えられ、そのミッション達成に向けたシステムについてオーナーシップを持ちます。システムの全開発ライフサイクル(設計、開発、テスト、本番化、運用、不具合対応など)を担当します。 Chapterは、Squadの中でも特定の専門分野を担当するメンバーを横串に束ねた組織です。Squadに所属しながらSquad外でも、テクニカルな改善・基盤整備を独自に実施します。 ※新開発体制の詳細は下記記事でまとめていますのでご覧いただければと思います。 tech.enigmo.co.jp 新開発体制導入の背景/なぜこのような体制にしたのか 大谷: Committee体制の中で、なぜこのような新体制を構想したのかについて背景をお聞かせください。 木村: トップの役割をCommittee体制に引き継ぐ際に、開発組織はトップに依存せず、各チームに裁量を持たせ、自律的に成長・進化し続けることで、ビジネス課題を迅速に解決できる組織としたいという考えがありました。 各組織・チームに裁量を持たせるためには、各組織・チームが担うべきロール(役割)を改めて明確に定義し直す必要があると考えました。 その中で最適な組織の形は何かを考えた結果、この新開発体制が上手くフィットしました。 安藤: 以前から エニグモ では、ビジネスメンバー、ディレクター、エンジニアが、Oneチームで同じ事業課題解決に向かってプロダクト開発する「ユニット」体制がありました。昔からどういった体制がベストであるかを模索していた中で、この1、2年でDomainやSquadとして再定義し、メンバーを アサイ ンして、継続的にプロダクトを磨いていこうという体制になったと記憶しています。 山本: そうですね。Squadはもともと Spotify のSquadモデルを参考にしたり、「ユニット」の考え方を応用して、一部のチームでは導入していました。Squadの領域を定義し、リーダーを決め、縦に割って効率化しつつもSquad間でも横で連携して取り組もうという体制がありました。 また、ミッションが類似・関連しているSquadを束ねたDomainという考え方も以前からありました。 Committee体制を導入するタイミングで、木村さんがトップの役割を棚卸した際に、以前からあったDomainやSquadをより 言語化 ・明確化することで、上手くかみ合ったのかなと思います。 もともとはChapterの概念は省いていましたが、専門のロールをChapterとして整備し取り入れました。 安藤: Committee体制で進めることで、組織的にどのような体制で進めるのが自然であるかを改めて考える良い機会となりました。 トップの仕事は属人的であるということに気づき、トップが変わったとしても、組織を存続させるためには、こういうCommittee体制というハコ(仕組み)でやるのが自然だよねとなり、新開発体制へと昇華されました。 新体制導入後に感じている進化や手ごたえ 大谷: Committee体制、新開発体制の導入後に感じる手ごたえはどうでしょうか? 木村: 手ごたえとしては、各メンバーが裁量を持ち、且つ負担がかかりすぎない良い体制で、元々のコンセプト通りに運営できているように感じます。 私はCommittee Headですが、HeadというのはあくまでCommitteeの中の役割で私がタッチしていない部分も多いです。そんな中でも、トップ(Head)に依存しすぎず、各メンバーが決めた持ち場でしっかり役割を担っていただいていると感じます。 山本: マネージャーやSquadリーダー同士での話し合う機会が増えてました。 例えば「開発で、今こういうことをやっていて、ちょっと相談したいんですけど」みたいな話は、Squad Weekly(Squadのリーダー以上が参加するミーティング)でしており、他のチームリーダーにも意見や相談ができます。 開発に関わること以外でも、「採用はどうしましょうか?」「組織をもっとこうしたい」「チームのマネジメントについて相談したい」といった話題はCommittee体制になりより話すようになりました。 大谷: 経営側からみて、安藤さんが感じる「ここらへんが変わり始めたな」というところはありますでしょうか。 安藤: すでに明確に変わっていると感じるところはあり、今まで 言語化 されていなかった組織やチームの役割やミッションが「こういうチームを目指していきたい」としっかりと 言語化 され、クリアになりました。そのことで、経営やビジネス的な視点で意思決定する際も説得力が持てるようになりました。 例えば、本来は優秀なメンバーは機能開発に アサイ ンしたいところです。直近立ち上がった(技術的な課題を解決する)チームも「Squadメンバーの生産性を最大化させるためにあるチーム」と定義することで、これからのエンジニア組織やサービス成長において重要な役割であることが明確になり、メンバーの アサイ ンにおいて納得感あるコンセンサスになりました。 採用に関しても、新しい体制下でのスピーディーでチームが一体となった採用活動ができるようになったことも、大きな進化だと感じています。 大谷: 新開発体制に移行したことによる、プロジェクトの進め方やメンバー間コミュニケーションの変化について、さらに詳しくお聞きしたいです。 木村: 以前は、プロジェクトが始まるタイミングで手が空いているメンバーを優先的に アサイ ンしていました。新開発体制のSquadではエンジニアとして担当する機能が固定化され、システムについてのオーナーシップを持つことができるようになりました。この変化により、技術領域と ドメイン 領域の両方に精通することができ、非常に効果的であると感じています。 以前は、「今日からあなたは、この機能を開発してください」となったときに、都度キャッチアップしながら探り探り進めることが多くありました。その結果、コードを壊してしまったり、元々の設計志向を理解せずに違う方向に修正してしまうことがありました。しかしこのような事態は新開発体制の導入によって解消されました。 山本: Squadは毎回新しいメンバーと組むわけでなく、2〜3名の小さいチームで同じメンバーで開発・振り返りを行うことができるため、チームごとにノウハウが蓄積し、息の合った開発・プロジェクト進行が可能となります。さらに、蓄積されたノウハウは他のチームにも共有され、良いア イデア が取り入れられる好循環が生まれています。 安藤: 新開発体制となり、エンジニアと企画•ビジネスメンバーが同じOKRをディスカッションして決めて、良い形で一体となってプロダクト・サービスを作っているように感じます。これは2人にとってはどう映っていますか? 木村: エニグモ の企画・ビジネスメンバーはエンジニアへの理解が強く、理解しよう・寄り添おうとしてくれるメンバーが多いように感じます。 エンジニアが強すぎるというわけでも、ビジネス側が強すぎるわけでもなく、言われた物を言われたままに作るというわけでもなく、バランスのとれた形で開発が進められていると思います。 エンジニアも、長く働くメンバーが多いのは(10年以上在籍するメンバーもいる)のは、そういう環境にやりがいや面白みを感じているからかもしれません。 山本: もともとはビジネス側から、こういう企画がやりたいという話があり、さらに間にディレクターが入り要件定義し、エンジニアに下りて来るという形で、比較的受託開発のような開発の流れがあり、ビジネス側との距離感が少し遠いと以前は感じていました。 新開発体制になった後は、ビジネス側のメンバーが同じチームに所属することで、エンジニアが直接コミュニケーションを取れるようになりました。 エンジニアも参加するMonthly定例で、ビジネス側で持っている課題感や数値の進捗共有があり、「こういう数値を見てたんだ」といった新しい発見もあります。ビジネス側との距離がより近くなったと感じます。 さらに、SquadでOKRを決めて開発を進めるため、ビジネス的な目標についてもエンジニアとしてコミットし、プロダクトへの意識が変わってきています。 OKRを設定するためにはエンジニアも含めて多くのディスカッションが必要なため、ビジネス側の視点を持って開発をしたい方にとっては面白い環境であると思います。 当社エンジニア組織の魅力・キャリア 大谷: エニグモ ではエンジニアとして、どのような経験やスキルを身につけたり、キャリアを積むことができますか? 木村: エンジニアは事業志向と技術志向の方がいると思っています。 事業志向の方は事業のミッションと向き合えるようなポジションに配置されるため、プロダクトの成長にどう貢献するかを考える機会があり、そういった方向でスキルを伸ばし、キャリアが積めると思います。 技術志向の方は、Squadで開発の一連のサイクルを経験できますし、またChapterやその他専門チームに所属することで、専門的なスキル・技術を深めることができます。 また、マネジメントを志向する方にも、DomainやChapterごとにマネージャーが配置されているため、そういったマネジメントのポジションを目指すこともできます。 山本: 実際のユーザーとの距離が近く、フィードバックをダイレクトに受け取ることができます。ユーザーのフィードバックを受けながらビジネス側のチームと協力して、エンジニアの視点でサービスやプロダクトに関する課題解決に取り組むことができます。 技術的な面では、大量の トラフィック を想定した開発など、大規模なアプリケーションの開発についての一通りの経験が得られるのが魅力です。 また、 BUYMA はユーザーも機能もすごく多く、機能も複雑に絡み合っているため、コードの一部を変更すると他の機能に影響が及ぶ可能性があります。そのため、自然と質の高い開発をする意識が養われます。 安藤: 確かに、プロダクト志向やビジネスに興味がある人も、技術的に突き詰めたい人にも、マネジメント志向の人にも様々なキャリアの可能性がありますね。 まとめ エニグモ にマッチするエンジニアとは? 大谷: 最後に、さらなる組織進化にむけて今後どのような人材に加わっていただきたいか、こういう方は エニグモ で活躍できる(マッチする)などお話ください。 山本: 事業や、組織的、システム的なところなど課題をどんどん見つけて、もっとこういう風にした方が良いという思い(改善思考)を持ち、周囲を巻き込んで行動し、挑戦できる人が一番かと思います。 木村: どういう方が楽しめるのかなと考えると、世の中一般的にこうあるべきみたいなのがあって、そこに向かって今のその会社の形を無理やり変えていくみたいな人というよりは、会社それぞれで最適な形というのは当然あるため、会社や事業、組織、人のリアルな課題に向き合い最適解を探せる方ではないでしょうか。 安藤: エニグモ が求めている人物像は、基本的には エニグモ のバリューに沿った方が良いと思っています。 ※バリューについての詳細はこちら チームワークで解決できる人、物事をよりよく・より深く考えることのできる人。現状に満足せずに変えることができる人ですね。 変革の過程でギスギスすることやハードなこともあるため、そういった場面でも楽しみながら業務に取り組み、最終的には「夕日を見て肩を組めるような人、いろいろあったけど一緒にやれて良かった」と感じられる人が良いと思います。そんなに熱くなくてもいいですけどね笑。 大谷: 夕日を見て肩を組む、、、いいフレーズですね(笑) エニグモ が求める人物は「変革期で逆境やカオスな状況を乗り越えた先に、夕日を見て肩を組める人」ということで今回のインタビューは締めといたします。ありがとうございました。
こんにちは。 インフラグループ Kubernetes チームの福田です。 先日、 こちら でPrometheus Stackを使った監視構成の概要を紹介させていただきました。 本記事はそれに関連して、NodeGroup単位でのメトリクスの取得に関してハマりポイントがあったので、それを紹介できればと思います。 NodeGroupとは EKSにはNodeGroupという機能があります。 これは名前の通り、Nodeをグループとしてまとめる機能で、NodeGroup単位でマシンスペックを指定したりすることが可能です。 NodeGroupの詳細は こちらの記事 が特に参考になります。 また、NodeGroupの名前等の情報は各nodeにラベルとして付与することが可能です。 やりたいこと 我々の場合、EKS上に複数のプロジェクトを ホスティング しており、これらのプロジェクトに1:1でNodeGroupが存在しています。 そして、プロジェクト単位(NodeGroup単位)でnodeに関するメトリクスを収集したい要件があります。 例えば、あるプロジェクトのnodeの負荷を収集して、そのプロジェクトが設定しているEC2( Kubernetes node)マシンスペックが適切かどうか判断したい場合です。 言い換えると、あるnodeに関するメトリクスがどのNodeGroupに属するものか知りたいのです。 問題点 Prometheus metricsにはラベルが付いていて、このラベルを使ってメトリクスをフィルタしたり、グルーピングしたりできます。 我々の監視構成ではnodeのメトリクスはNode Exporter経由で収集していますが、Node Exporterが提供するメトリクスにNodeGroupラベルは付与されていません。 また、 relabel_configs 等のPrometheusが提供するラベル付与機能では決まった文字列のラベルを付与することはできても、「メトリクスの送信元ラベルからNodeGroupラベルを マッピング して付与する」といった動的なラベル付与はできません。 解決策 nodeにラベル(メトリクスのラベルではなく、 Kubernetes nodeに付与されているラベル)として付与されているNodeGroupの名前を Kubernetes API 経由で取得し、メトリクスにNodeGroupラベルを付与するプロキシ(Proxy Container)をNode Exporter Podにアダプタとして追加しました。 Proxy ContainerはGoで実装していて、以下のように動作します。 NodeGroupラベルの取得の挙動 自分自身がデプロイされている kubernetes nodeのhostnameを取得。 前のステップで取得したhostnameに対応するnodeのラベル一覧を Kubernetes API 経由で取得。 取得したnodeのラベル一覧にNodeGroupラベルが含まれる。 Proxyとしての挙動 Prometheusからリク エス トを受信する。 Node Exporterコンテナにメトリクスのリク エス トをする。 Node Exporterコンテナからレスポンスとしてメトリクス情報を受信する。 取得したメトリクス情報にテキスト処理を行い、メトリクスにNodeGroupラベル付与する。 Prometheusにレスポンスを返す。 まとめ NodeGroup単位でメトリクスを収集する方法について紹介させていただきました。 本記事で紹介したProxy ContainerはPrometheus形式のメトリクスに対して、任意のラベルを挿入できるように実装しています。 動的にメトリクスのラベルを弄りたくなる状況が同じようにあれば、これを再利用していきたいと思います。 エニグモ ではエンジニアを含む各種ポジションで求人を募集しております。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは。 インフラグループ Kubernetes チームの福田です。 みなさん、システムの監視はどうしているでしょうか? 弊社では幾らかのサービスをEKS上で動かしており、その監視にはDatadogとPrometheus Stackを使っています。 この記事では特にPrometheus Stackを使ったEKS周りの監視構成について、その概要を紹介したいと思います。 クラスタ 毎の構成 まず、我々は以下の Kubernetes クラスタ を運用しております。 社内サービス用 クラスタ 商用サービス クラスタ integration環境 商用サービス クラスタ production環境 構成は下図のようになっています。 Grafanaだけ中央集権的に社内サービス クラスタ にのみ配置し、それ以外のPrometheus等の監視 コンポーネント は クラスタ 毎に配置しています。 また、各 クラスタ には Prometheus Operator をインストールしており、Prometheus自体の構築やPrometheusのコンフィグの管理はPrometheus Operatorを介して行っています。 監視項目 ここからは監視項目とその方法について紹介したいと思います。 外形監視 社内サービス用 クラスタ 上にある Blackbox Exporter から必要なエンドポイントへ外形監視をしています。 各 クラスタ の AWS アカウントはそれぞれ分かれているため、商用サービス クラスタ への通信は AWS アカウントを跨ぐようになっています。 worker nodeの監視 node単位でのリソース使用量等の情報はDaemonSetとしてインストールしている Node Exporter からメトリクスを収集しています。 コンテナの監視 コンテナ単位でのリソース使用量等の情報は cAdvisor からメトリクスを収集しています。 cAdvisorは自前でデプロイしたものではなく、kubeletに組み込まれているものをそのまま利用しています。 各種アプリケーションの監視 自社で開発したアプリケーションについては原則はPrometheusではなく、Datadogで監視を行っています。 ただし、Argo CD等の OSS についてはそれらのアプリケーションが公開しているmetrics用エンドポイントからPrometheusメトリクスを収集しています。 Logの集約 Logの集約は Grafana Loki で行っています。 DaemonSetとして各nodeにインストールしている Promtail が各 クラスタ にあるLokiへログを送信する構成です。 また、ログデータは永続化するためにLokiの機能を使ってS3に保存するようにしています。 まとめ 本記事では、Prometheus Stackを使ったEKS周りの監視構成について概要を紹介させていただきました。 ここでは構成の概要を紹介しただけですが、ハマりポイントや改善ポイントについても知見がある程度溜まった段階で、今後紹介できればと思います。 また、 エニグモ ではエンジニアを含む各種ポジションで求人を募集しております。 株式会社 エニグモ すべての求人一覧 hrmos.co
エニグモ のエンジニア採用担当の 廣島 です。 エンジニア組織が昨期から新体制になりました。今回は、エンジニア組織のトップが全社会議で新体制について説明した内容をまとめた記事となります。 エニグモ はCtoCグローバルECの BUYMA というサービスを運営していますが、どういう体制で開発しているのかというお話です。 ぜひご覧ください! 目次 Committeeとは? 新組織体制、新チーム、新ロールの概要 「Squad」「Domain」「Chapter」体制について Squad(スクワッド)とは Domain(ドメイン)とは Chapter(チャプター)とは 開発組織を支える5つのチーム あるプロジェクトの編成例 新体制の狙い この体制のメリット/Squad、Chapterの導入が納期短縮になる理由 まとめ Committeeとは? Committee Headの木村です。現在、 BUYMA のエンジニア組織のマネージャーおよび、エンジニア組織の意思決定機関であるCommitteeのHeadを担っています。 今回は、新組織体制となったエンジニア組織について説明します。 まず、私がHeadを務める Committee とは何かについてお話しします。 Committeeはエンジニア組織の意思決定を行うトップ機関です。 CTOや部長を頂点とするワントップ型ではなく、複数名のエンジニア リングマ ネージャーで構成する合議体です。トップは輪番制となっており、現在私が担っています。 エンジニアの意思決定機関は、Committeeの他、優先順位が高い組織課題は分科会を設置して議論・意思決定のスピードを上げています。分科会にはマネージャーの他、分野ごとの適任のエンジニ アメンバー が参加しています。 分科会は現在、 アーキテクチャ 分科会、KPI分科会、採用分科会などがあります。 Committee体制についての詳細は下記記事でも説明していますのでご覧ください。 tech.enigmo.co.jp 新組織体制、新チーム、新ロールの概要 こちらの図を用いて、まずはエンジニア組織の全体像を話します。 実態としては チーム トポロジー の概念 ( Spotify モデル)を参考に組織設計しました。 Spotifyのスケーリングアジャイル – 部隊、分隊、支部やギルドと共に歩む(Spotifyモデル) | 『リーン開発の現場』越境せよ! 図の左側は主にプロダクトの機能開発を担うチームであり、 Domain と呼ばれるチームが3つあります。それをさらに細分化・構造化する形で、縦割りチームの Squad や、横ぐしチームの Chapter があります。 右側の図は Domain/Squadを支援する位置づけのチーム であり、5つのグループ・チームがあります。 「Squad」「Domain」「Chapter」体制について 前段で登場した「Squad」「Domain」「Chapter」がどのようなチーム単位で、実際に エニグモ ではどのように運営されているかをお話しします。 Squad(スクワッド)とは 図の中でも一番多くあるチーム単位の Squadは、事業的なミッションが与えられ、そのミッション達成に向けたシステムについてオーナーシップを持ちます。 システムの全開発ライフサイクル(設計、開発、テスト、本番化、運用、不具合対応など)を担当します。 1つのSquadで開発者は2-5人程度のチームです。さまざまな技術領域・職能のメンバーが集まっています。 各Squadにはリードするメンバー1名がおり「Squad Lead」と呼びます。開発プロジェクトで設計・タスクへの落とし込み、タスクの アサイ ン、プロジェクトマネジメントを担います。 事業的なミッションの例としては、「魅力的な商品を増やす」や「競争力のある出品者さんにご活用いただける配送サービスを拡充する」「ユニークユーザー数・セッション数の改善を行う」などがあります。 エンジニアであってもビジネス的視点を持って開発を行い、メンバーに裁量や自律性を持たせた組織になっています。 他のSquad以外のチームの基本的なミッションはSquadチームを支援することと定義されています。 Domain( ドメイン )とは Domainとはミッションが類似・関連している複数のSquadの集まりです。 Domainのマネージャーを「Domain Head」と呼び、配下のメンバーのピープルマネジメント、リソース配分、Domain間の連携を担当します。 Domainは、ディレクター、データアナリスト、デザイナーなどエンジニア以外のメンバーも所属し、職能横断型組織となりプロジェクトを進めます。Domainはいわゆる事業部のような単位となっています。 BUYMA は現在、3つのDomainがあり、それぞれBUY Domain、SELL Domain、SI(SERVICE INFRA)Domainと呼びます。 3つのDomainがどういった開発をしているのかを説明します。 BUY Domain BUYMA の購入者向け機能を開発するチームです。主なカバー範囲としては、会員登録、商品詳細、購入リスト、レコメンド、クーポン、広告、特集ページ、カート、商品レビュー等を開発します。 SELL Domain BUYMA の出品者向けの機能を開発するチームです。主なカバー範囲としては、出品ページ、出品リスト、受注リスト、商品画像、お問い合わせ、タイムセール、ショップ連携等を開発します。 SI(SERVICE INFRA)Domain BUYMA サービスの基盤となる決済連携、物流(配送・倉庫連携)、入金関連、セキュリティ関連等の機能を開発します。 ※大型キャンペーン対応、商品画像施策など複数Domain横断のプロジェクトもあります。 Chapter(チャプター)とは Squadの中でも特定の専門分野を担当するメンバーを横ぐしに束ねた組織です。 Squadに所属しながらSquad外でも、テクニカルな改善・基盤整備を独自に実施します。 具体的な特定の専門分野は現在、フロントエンド、モバイルアプリ、QAがあります。 Frontend Chapter フロントエンド独自の戦略にもとづき横ぐしで施策を実行し、プロダクトの品質向上やパフォーマンス改善、計画的なバージョンアップや リファクタリング 、エンジニア組織全体の活性化や技術力の向上に取り組みます。 モバイルアプリ Chapter iOS ・ Android アプリの開発を担当するChapterです。 モバイルアプリの iOS やアンドロイドはそれぞれのチームとして独立していましたが、事業的なミッション達成の為には、どのDomainにおいても iOS やアンドロイドの協力は絶対必要不可欠です。とくに iOS は 流入 UUの約半分以上を締めておりユーザーとの重要なコミュニケーション接点です。企画段階から開発に参加することが健全のため、横ぐしのChapter組織としました。 QA Chapter サービス品質の維持・改善をミッションに持ち、各プロジェクトの上流工程から関わり、QAを担当します。 プロジェクト以外でも自動テストの導入、本番で検出された不具合分析など横ぐしでプロダクト全体の品質向上を目指した取り組みを行っています。 開発組織を支える5つのチーム 続いて右側の図の開発組織Domain/Squadを支援する5つのチーム(Application Platform、Data Technology、Infra、DX、DevRel)について説明します。 これらのチームはソフトウェア基盤の構築・改善、開発環境の整備、組織開発などエンジニア組織を下支えしています。 ミッションは「Squadの生産性向上」とすることでSE本部組織全体としてのベクトルを揃えています。 各チームがそれぞれどのような役割を担っているかを説明します。 Application Platform Application Platformは新組織体制に移行した際に、新設したチームです。Squadが単独では担いきれなかったり、 各Squadで横断的に共通するようなテクニカルな課題を解決し、Squadの生産性を向上させることがミッションです。 具体的な取り組み例として、言語・ライブラリ・ フレームワーク のバージョンアップ、 AWS 化に向けたアプリケーション修正、 疎結合 化・モジュラー化・マイクロサービス化などの アーキテクチャ のモダナイズ、サービスの信頼性強化(SRE活動)などがあります。 Data Technology Domain/Squadでは担いきれない データ関連の専門知識(データ基盤・ 機械学習 ・検索)が必要とされるシステムを設計・開発から運用までを担当します。 初期の設計や開発フェーズではSquadと密接にコラボレーションします。 データサイエンティスト、検索・MLOpsエンジニア、データエンジニアなどデータに関わる スペシャ リストが所属します。 Infra Domain/Squadでは担いきれない インフラ・セキュリティ関連の専門知識が必要とされるシステムを設計・構築から運用・監視までを担当しています。 現在の取り組み例として、 BUYMA の環境をオンプレミスから AWS への移行、 Kubernetes クラスタ の構築・運用および開発支援、サービスのセキュリティの強化などを行っています。 ※次に紹介するDXチームとDevRelチームの2つのチームは、SE本部の組織の中でも人を集め、エンゲージメントを向上させる重要な役割を持ちます。専任メンバーでのチーム組成ではなく、他チームのメンバーが兼務するパートタイム組織となることも特徴です。 DX DXはDeveloper Experienceの略で、日本語では開発者体験という意味です。 開発者体験の維持向上を行うことがミッションとなります。 主に、 BUYMA のローカル開発環境やCI/CD ツールの整備をしています。 DevRel DevRelはDeveloper Relations の略で、 社内エンジニアの技術力向上・知識共有や交流促進の為、社内向けに勉強会、開発合宿などを企画します。 また、テックブログや対外イベント・カンファレンス協賛などの企画により、 対外的に社内の技術力の広報活動を実施するチーム です。 あるプロジェクトの編成例 今までは、開発組織がどういうチームで構成されているかをお話ししましたが、実際にプロジェクトベースでどのような流れ、関わり方になるかをお話しします。 プロダクトマネジャー(PdM)が企画をまとめプロジェクトがスタートし、PdMとSquad Leadやビジネスサイドのメンバーでどのようにシステムに落とし込むのかを要件定義します。その後、Squad Leadが設計を担い、タスク分解して、エンジニ アメンバー に アサイ ンします。 他にも、Data Technology、Application Platform、Infra などのこれらのチームはSquadの開発プロジェクトがいかに早く終わるかを支援していく立ち位置となります。 例えばプロジェクトで検索 API が必要だとなれば、データテク ノロ ジー グループの検索チームが開発したり、インフラ構築が必要であればインフラがサポートする。そういうプロジェクト編成になります。 新体制の狙い 先ほどからも狙いを織り交ぜながら話していますが、 新体制の一番の狙いとしては「案件(プロジェクト)の納期短縮によるユーザへの価値創造・事業への利益創出」です。 納期短縮により、案件のリリース回数が増えれば、その分 PDCA がまわって、ABテストなど試行錯誤が進みUXが改善され、ユーザーへの価値創造になります。 案件1つ1つリターンを得るものだとすると、一定の 工数 でリリースされる案件を増やせば増やすほど、組織のROIが改善されるので、利益創出になると思っております。 この体制のメリット/Squad、Chapterの導入が納期短縮になる理由 新体制が具体的にどのように納期短縮なるか、その他新体制のメリットについて5つ紹介します。 ①Squadはいわば、ミッション特化型職能横断チームで、ミッションが共通しているので職能間のベクトルが揃い開発スピードが上がります。 ②職能間のタスクの受け渡しやすり合わせ MTG を個別で実施する必要がなくなります。 以前だと、例えばフロントエンドとバックエンドで作業をお願いするミーティングがありましたが、全部1つのチームとなるので、最初の企画ミーティング以降はチャットベースで仕事が進み、ミーティングが減るのではないかと考えています。 ③異なる職能間でお互いの進捗が見えやすくなり、メンバーが進んで仕事を取りにいけ、 アサイ ン待ちや依頼待ちが発生しづらくなります。 ④職能間でお互いの理解が進み、技術としては1人1人専門分野があるが、知識は フルスタ ック化していきます。例えば、フロントエンド側もバックエンド側の負荷を自然に気遣えるようになると考えています。 ⑤Chapterはそれぞれの専門で実績のある新しいツール、ライブラリ、 フレームワーク 、プ ラク ティスを常にChapterでフォローし、基盤やツールを整備しておけば、すぐにSquadへ展開可能になります。 まとめ 今回お話ししたエンジニア組織体制は目標とする体制で現在進行形の箇所も含まれますが、新体制のキックオフから約1年が経ち、目標の体制にかなり近づき、且つ全社に浸透し機能してきました。 ただ、チームとして組織として成果を出し・生産性を高めていくためには、まだまだ改善の余地があり、道半ばです。さらに、この体制は完成形ではなく事業や組織の状況によってアップデートしていければと思っています。また、Committee(組織運営や意思決定)に関わる人も今後、さらに増やしていきたいです。 よりよい価値のあるプロダクトをユーザーに届けられるよう、そして エニグモ がさらなる成長を遂げられるよう、これからも精進していく次第です。
こんにちは、エンジニアの片桐です。 エニグモ では、社内サーバインフラの構成管理に Ansible を採用しています。 Ansibleの前任の構成管理ツールには Chef を使用していました。 Chefと比較して、Ansibleで使用するファイルは YAML ベースの記述が可能で可読性が高いこと、エージェントレスで管理対象サーバに何もインストールする必要が無いこと等、Ansibleを導入することで様々なメリットを享受しています。 (Ansibleの導入話は以下の弊社ブログ記事でも話されている為、合わせて是非ご覧下さい!) tech.enigmo.co.jp 社内でのAnsible活用が進む一方、他社のAnsible活用事例にはどんな物があるのか知りたいなと思っていた所、 Ansible Night 2023.07 現場を支えるPlaybook編 というイベントが開催されることを知りました。 折角の機会ということで、聴講側で参加しましたのでレポートを残したいと思います。 Ansible Nightとは Ansibleのユーザコミュニティである、 Ansibleユーザー会 様主催の技術イベントです。 技術的な話題は勿論のこと、自動化を実装する為の組織作りの話題まで、Ansibleを使用した幅広いテーマを主体として開催されています。 イベント情報は Ansibleユーザー会のConnpass で公開されていますので、興味があれば覗いてみてください。 1. Ansibleによる、ネットワークOSに依存しないコマンド作成・設定変更 (APコミュニケーションズ 川名さん) 公演資料 github.com 公演内容 業務内容と現状業務の問題点 NW機器のサブインターフェースに設定する IPアドレス の計算と設定コマンドの生成を Excel で実施していた。 しかし、この作業には以下の問題点があった。 使用するネットワークOSが2種類混在しており、それぞれのOSでサブインターフェースを設定するコマンドが異なる為、コマンド生成と確認に必要なスキルレベルが高くある。 作業時に誤って Excel の計算式を削除してしまうケースがある。 そもそもの Excel への数値入力が大変。 問題点に対する改善 Ansibleとjinja2テンプレートを使って、 IPアドレス の計算と設定ファイルの生成を自動化した。 自動化実装の結果、作業者・レビュー者ともに作業負担が非常に軽くなり、作業効率も上がってミスも減った。 Ansible実装時に発生した問題 開発ツール(エディタ)を統一していなかった。 エディタ依存のインデント差分等が生まれ、レビューに時間が掛かった。 開発ツールは Visual Studio Code に統一し、使用する 拡張機能 もチーム内で共有した。 Playbookのコーディングルールが無かった。 変数名などの表記に揺れが生じ、レビューが困難だった Playbookの管理にGit等の構成管理ツールを使用していなかった。 Playbookの重複編集や、 誤爆 削除が発生していた。 自動化によって発生した課題 「手動での設定ファイル生成作業を経験したことの無い人」が発生するようになった 新しく着任した人は、既に自動化された作業をなぞるだけになる為、作業内容のイメージがしづらい。 検証環境で手作業での作業も実行させると理解が深まりやすい。 Ansibleで実行エラーが起きるたびに開発者に連絡が来るようになった。 既知のエラーについてのドキュメントの整備と エス カレーションフローを整備して、問い合わせの件数を軽減した。 感想 Ansibleでサーバに対して設定ファイルを展開するのではなく、テンプレートエンジン的な使い方をして設定ファイルの生成のみを行うという、自分の知らないAnsibleの使い方だと感心しました。 ネットワークOSによるサブインターフェースの設定コマンドの差異は以下の様になるとのことで、これだけ異なれば差分を吸収するのは確かに大変そうだな...と思います。 Junos: set interfaces fe-0/0/0 unit 11 vlan-id 11 set interfaces fe-0/0/0 unit 11 family inet address 192.168.0.1/28 IOS : interface GigabitEthernet1.11 encapsulation dot1Q 11 ip address 192.168.0.1 255.255.255.240 ! 各種言語を使用した スクリプト でも同様の設定ファイル生成処理は実現可能とは思いますが、OSによるホストの出し分けやPlaybookの再利用性など、Ansibleならではのメリットが享受できそうな使い方だと思いました。 また、実装時の問題点では「開発環境(エディタ)を Visual Studio Code に統一する」対応を取られていたとお話されていました。 Vim 使用者や サクラエディタ 使用者等、幅広いエディタ使用者が居たと言う事だったので、エンジニア間で揉める所があったんじゃないかなと思い、ハラハラしながら聴講しておりました...。 2. NetBoxを利用した IPアドレス の自動払い出し (APコミュニケーションズ 宮下さん) 講演資料 github.com 公演内容 業務内容と業務の問題点 社内にある VM 基盤上に VM を作成する業務が多く発生する。 VM の作成時には IPアドレス を アサイ ンする必要が有るが、以下の問題点がある。 IPアドレス の アサイ ン状況の管理を Excel ファイルで行っている。 作業担当者は、 アサイ ン可能な IPアドレス を Excel ファイル上から見て探して、手作業で アサイ ンした後 Excel ファイルを更新する。 Excel ファイル自体の管理が必要な他、棚卸しが大変だったり、 アサイ ンオペレーションミスが発生したりする。 改善内容 IPAM/DCIM機能を持つ OSS である、NetBoxを導入した。 IPAM(IP Address Management): IPアドレス 、 プレフィックス 、VLAN, VRF等の管理サービス DCIM(DataCenter Infrastracture Management): サーバ、回線、ラック、電源等の管理サービス AnsibleとNetBoxを連携することで、以下のフローを実現した。 AnsibleのPlaybookからNetBoxの API を叩き、指定ネットワークPrefix上で空いている IPアドレス を仮採番する。 仮採番された IPアドレス の利用状況を確認するため、AnsibleのCommandモジュールから Ping を実行して疎通確認を実行する。 Ping 応答が無ければ空きIPとして判断し、AnsibleのNetBoxモジュールを通して、NetBox上に IPアドレス と紐づいた VM 情報を登録する。 VM 構築にかかる稼働時間やリードタイム削減、オペミス頻度の低下、棚卸しの容易化などのメリットが生まれた。 自動化で生まれた課題 NetBox自体を管理するコストが増加した 社内の別のシステム上で VM の IPアドレス を管理しているものがあるらしく、NetBoxに集約していく作業が必要 感想 Ansibleを利用して人の手が必要な部分を上手く自動化し、業務の効率化だけではなく、業務の質も向上出来ているアプローチだなと感じました。 また、NetBoxと言う OSS はこの公演を通して初めて知りました。 VM だけでなく、物理サーバの管理やNW機器の管理も可能で、それぞれの管理項目もかなり詳細に設定出来る様です。 NetBoxのデモサイト が公開されている他、 Docker版のNetBox も公開されている為、是非社内でも導入検証してみたいと思いました。 NetBox デモサイト上でのIPAM管理ページ表示例。欲しい項目は一通り揃ってそうで良い。 3. チケット運用とAnsible連携による変更管理の自動化( RedHat 小野さん) 講演資料 公開なし 公演内容 業務内容と問題 あるインフラ関連作業について、メールや台帳で作業依頼を受けていた。 アサイ ンされた作業者が各々で手順書を作成して実行し、作業後に各人が依頼主に返信して対応完了にしていた。 チケット管理システム等による作業管理がされておらず、ナレッジの蓄積と共有がし辛い状態であった。 業務改善までのフロー 標準化: ServiceNowによるチケット管理の導入 あるインフラ関連作業について、メールや台帳で作業依頼を受けていた。 依頼によって発生したタスクを一元管理する為に、チケット管理システムであるServiceNowを導入した。 タスクをチケットとして一元管理できるようになり、依頼フローなどが整備された。 自動化: Ansible Playbookを用いた作業手順の統一化 作業手順の不均一性を解消するために、AnsibleのPlaybookに作業手順をコーディング。 担当者による作業の差異がなくなり、一貫性のある作業が可能になった。 人の手による作業からAnsibleが自動で作業を実行する方式に変更することで、作業効率が向上した。 サービス化: AWXによるフルオートメーションの実現 AWX(Ansible Tower)の API を活用して、作業依頼のチケットが承認されると自動で作業が開始されるように設定した。 作業フローは完全に自動化され、人の介入が不要になった。これによって作業のスピード、精度、効率が向上した。 仕組みづくりの為の3ステップ 標準化 ルール作成、 ガイドライン 策定 作業ミス低減、トレーサビリティ向上を目指す。 自動化 標準化された作業を、ソフトウェアやシステムで自動的に行う ユーザへの価値向上、プロセスタイムの削減を目指す。 サービス化 自動化されたプロセスを相互連携させて、ユーザに対して価値をもたらす。 リードタイム削減、価値最大化を目指す。 インフラエンジニアとして伝えたいこと インフラエンジニアは、自分に与えられた作業を正確にこなす内向きな作業者視点から、ユーザ視点の価値にフォーカスすべきである。 自分の作業効率を上げるための作業者視点の自動化から、ユーザへ価値を届ける仕組みづくりへの視点への転換が重要。 効率的な業務を行う為の仕組みづくりを行う、サービスプロバイダになるべき。 感想 完全手動かつ無管理だったワークフローを、チケット管理システムを使用して管理化した後、Ansible Tower(AWX)を使用して完全自動化するまでのステップを紹介していました。 標準化によって作業の品質が保たれ、自動化によって効率が上がり、最終的にはサービス化を通じてエンドユーザーに直接的な価値が提供されると言う一連のフローが、技術だけでなく組織全体での価値創出に繋がるという点は非常に参考になりました。 また、インフラエンジニアが単に内向きな作業にフォーカスするのではなく、ユーザ視点での価値を考える重要性が強調されていたのは、今後の業務においても非常に参考になると感じました。 自分自身の作業効率を上げることも重要ですが、それをどのようにサービスとして外部に提供するか、という視点も持つべきだというメッセージに、大いに共感しました。 最後に 久しぶりに技術イベントに参加して、新たな知見や視点に触れる充実した時間を過ごしました。 今回のイベント参加に使用した Connpass では、インフラに限らず多様なテーマで日々イベントが開催されています。特にリモート参加が可能なイベントも増えており、時間や場所に縛られることなく学びを広げられて良さを感じました。 気軽に参加出来ることもあり、自己研鑽も兼ねて今後も興味を持ったイベントのレポート出来れば良いな、と思いました。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは。 サービスエンジニアリング本部の寺田と橋野です。 こちらの記事は、RubyKaigi 2023に参加しました!<後編>です。 前編は以下からどうぞ! tech.enigmo.co.jp 後編では、RubyKaigi2023で印象に残った講演の内容を紹介をしていきます。 1日目 Matz Keynote Youtube : [JA][Keynote] Matz Keynote / Yukihiro "Matz" Matsumoto @yukihiro_matz - YouTube Slide: 30 Years of Ruby - Speaker Deck [内容] 今年で Ruby は30周年を迎えました。 この講演では、「 Ruby 」の生みの親である まつもとゆきひろ (Matz)氏が、 Ruby の30年を時系列順に振り返っていました。 学んだ教訓や主要なイベントを含め、段階に分けて説明していました。 年代 トピック 1993-02 ~ 1994-12 始めは一人での開発 1994-12 ~ 1995-12 Alpha Release 共同開発を開始 1997-08 ~ 1999-11 Ruby v1.0 リリース 1999-11 ~ 2004-10 初の Ruby の技術書 2000-10 初の英語の Ruby の技術書 2001-09 JAOO ( Java And Object Oriented Language) 2001-10 初めての Ruby のカンファレンス 2004-10 ~ 2009-01 Ruby on Rails の登場 2009-01~2013.02 Ruby1.9 のリリース 2013-02 ~ 2015-11 Ruby2.0のリリース 2015-11 ~ 2020-02 Ruby 3x3 2022 ~ Ruby 3.0 それぞれ紹介したテーマからMatz氏が学んだことや教訓で締めていました。 例えば、海外の人から本を書きたいとMatz氏に連絡がきたというお話しがあり、 まつもとゆきひろ 氏は「この本がなかったらいまの ruby がなかっただろう」という学びを得たことをおしゃっていました。 [感想] Matz氏の学びの中に特に興味深かったものがありました。 「いい名前を選ぶ。」という教訓です。 Ruby の名前の候補に、「Coral」や「Tish」などがあったそうです。もしかしたら、Tish Kaigiになっていたかもしれませんねとお話しされていました。 TishKaigiであると言語のカンファレンスではなく、製紙会社のカンファレンスのようにみえていたかも?と思いました。 また、実務でもさまざまな場面でいい名前を選ぶということを感じています。 例えば、メソッドの 命名 でも、どのような役割をもったメソッドなのかをわかりづらいというレビューを受けたり、また、役割がややこしい 命名 をされた変数を見たりします。 「いい名前を選ぶ。」ということは私も大切であり、大きな意味と役割をもつと思っています。みなさんも大切だと感じることが多いでしょう。 この講演に参加することによって、 Ruby の30年もの歴史を一気に振り返ることができました。 知らない歴史も多く、この講演を通してさらに Ruby のことが好きになりました。 The future vision of Ruby Parser Youtube : [JA] The future vision of Ruby Parser / Yuichiro Kaneko @spikeolaf - YouTube Slide: The future vision of Ruby Parser - Speaker Deck [内容] この講演では、 Ruby のparser実装の話を聞くことができ、3つの課題を説明します。 以下がその問題です。 Usability (Error-tolerant parser) Maintainability Universal Parser Usability 今までのparserはとてもシンプルであったが、Language Server Protocol(LSP)の場合それだけでは十分ではなく、責任が増えてきています。 トーク ンを追加もしくは削除して リカバリ ーをおこなったらどうだというア イデア があります。 Insert/Deleteのオペレーションの組み合わせをやることで プログラマ が構文を考慮することなくError Recoveryが実装できた。 Error Recoveryを実装するためにLrama LALR parser generatorを実装をした。 Maintainability メンテナビリティの解決方法について、 Ruby の do を中心に密結合しているという根深い問題に関しても他の既存のテクニックと 組み合わせることで解決ができた。 Universal Parser CRuby以外でもparserを使いまわしをするためにUniversal Parserが必要になってくる。 CRubyの提供する関数やマクロへの依存を"parse.y"から剥がしていくことで問題を解決していく。 [感想] BisonをLramaに置き換え、 Ruby のmasterにマージされたそうです。 これでBisonのバージョンの依存関係がなくなりましたね:yatta: Kanekoさんは、今後LALR parserの可能性を引き出していくことをしていきたいと講演でお話しされていました。 まだまだ進化していきそうな Ruby のParserを watch をしていきたいと思います。 この講演の内容を詳しく知りたいという方は、Kanekoさんのブログに丁寧に解説されているのでおすすめです。 かねこにっき また、parserを書いてみたいけどどこから始めればいいかわからないという方向けに Ruby で電卓をつくる チュートリアル をKanekoさんが書いてくださってので、興味を持った方はこの講演と合わせて試してみましょう! github.com Make Regexp #match much faster Youtube : [JA] Make Regexp#match much faster / Hiroya FUJINAMI @makenowjust - YouTube Slide: Make Regexp#match much faster - Speaker Deck [内容] この講演では、 正規表現 マッチングの実装と、 Ruby 3.2.0 で実装された 正規表現 マッチングの最適化の詳細について説明します。 流れとしては、 Ruby 正規表現 がいかにパワフルかの説明 ReDos 正規表現 マッチングには 脆弱性 がある。 ReDos を防ぐために高速化することによって防ぐことができる(メモ化による最適化) 正規表現 マッチングの今後の展望 の順にお話をされていました。 Ruby の 正規表現 マッチングはパワフルだけど、ReDosと呼ばれる 脆弱性 があり、その例をスライド内で紹介していました。 しかし、 正規表現 マッチングの実装の改善をし、待ち時間の短縮をさせ、ReDosが起こらないようにRuby3.2からではおこなわれています。 また、 正規表現 の拡張的な機能などはメモ化による高速化はおこなわれていないそうです。 [感想] ReDoSの起きてしまう理由やどのような手順で高速していったかを、ひとつひとつ丁寧に解説されていました。 しかし、 Youtube の内容を見直してみたのですが、本題であるメモ化による最適化の話のあたりが難しくまだ完璧には理解ができていません。 スライドの中に実行時間の比較があったのですが、Ruby3.2からでは時間がかなり短くなっていました。 Ruby3.2の変更を追えていなかったので、とても勉強になりました。 今後、理解していけるように 正規表現 について少しずつ学んでいきたいです。 Power up your REPL life with types Youtube : [JA] Power up your REPL life with types / tomoya ishida @tompng - YouTube Slide: rubykaigi2023_tompng.pdf - Google ドライブ [内容] この講演では、型定義の情報を使用して irb の補完機能を強力なものにするためのgem katakta_ irb について理解ができます。 irb の現状として、メソッドチェーンをすると正しい補完方法を出せなく、ありとあらゆるクラスのすべてのメソッドを出してしまったり、それ がパフォーマンスに問題があるということで最新の irb ではメソッドチェーンの補完は出さないようになってしまいました 。 katakata_ irb は、メソッドチェーンの補完をしていきます。 また、型定義の情報を使って補完をしてくれるので、かなり便利です! katakta_ irb は、タップル型やレコード型、.is_a?などは未対応で、今後実装されていくそうです。 [感想] 私もさっそく入れてみました。 Integerと出ていますね! gemをいれるだけで使えるようになるので、気になった方は是非使ってみましょう! github.com 2日目 Build a mini Ruby debugger in under 300 lines Youtube : [EN] Build a mini Ruby debugger in under 300 lines / Stan Lo @_st0012 - YouTube Slide: slides/2023-05-11-rubykaigi/Build a mini Ruby debugger.pdf at main · st0012/slides · GitHub [内容] たった 200 行で Ruby だけで実装した Debugger を紹介しますという講演。 単 純化 した pry の再実装のようなものですが、 このコードを読むことで Ruby で実装された Debugger のエッセンスが簡単に理解できます。 Ruby で何か開発をしている人は、何かしらの Debugger を使っているはずです。 ただ、普段使っている Debugger が中でどのようなことしているか知っている人は少ないですよね。 しかし Debugger の中身を深く知っていれば、必要に応じて自分で Debugger をカスタマイズしたりできたりと応用が効くようになります。 Ruby の Debugger の基本的な機能はたった3つの コンポーネント の組み合わせから成っています。 Binding object and Kernel#Binding Tracepoint ruby/reline Library これだけで簡単な デバッグ に必要な、以下のようなことが実現できます。 Breakpoint を設定して対話的にコードを実行する(REPL) Step-in , Step-over を使ってコードを実行する Breakpoint を新たに追加 / 削除する [感想] Debugger に限らず普段使っている gem の ソースコード を読むと、 動いていることとは裏腹に実装がめちゃくちゃシンプルだな... という風に思うことが Ruby だとちょくちょくありますよね。 やはり Ruby の魔術って強力だなあと改めて思いました。 Yet Another Ruby Parser Youtube : [EN] Yet Another Ruby Parser / Kevin Newton @kddnewton - YouTube Slide: YARP - Speaker Deck [内容] Yet Another Ruby Parser は通称 YARP と呼ばれている、 Ruby の次世代のパーサーの一つです。 次に求められるパーサーを作るには主に3つの課題をクリアする必要があり、 YARP においてもこれらの点を解決するための取り組みがなされています。 エラートレラントであること 高いポータビリティを持つこと メンテナンスしやすいこと エラートレラントであるというのは、構文にエラーがあるコードが与えられたときでも、 何かしら意味のある結果を返すことができるということです。 エラートレラントであるパーサーを持つことで、コードの補完などを開発者に提供することができます。 IDE が充実している現代ではこういった機能は言語として必須といってもいいでしょう。 また、 Ruby にはさまざまな構文ルールがあり、実行ランタイムもさまざまです。 このような世界では何か特定の実装に依存した仕組みがあると、 パーサーは限定した環境でしか使えないもの、すなわちポータビリティが低いということになってしまいます。 YARP では CRuby や、特定のパッケージの利用を前提とした実装がありません。 あらゆる環境でも利用できるパーサーとなるとのことでした。 最後にメンテナンスしやすいという点では、YARP は拡張性が高いということを挙げています。 テストコードも十分にあり、 デバッグ の難易度も低く、比較的簡単に新たな構文の追加ができるようになっているとのこと。 Gem としてのリリースはまだですが Ruby 3.3.0-preview1 以降のバージョンであれば実際に使うことも可能。 既に ruby / ruby のコードベースを初めとして、多くの主要な gem においても動作することが確認できているようです。 講演では Ruby のパーサーを開発することが如何に難しいかということにも触れられています。 [感想] Ruby は開発者にとって自由度の高い書き方ができたり、 コミュニティによって多くのライブラリが産まれることで大きく発展してきた一方で、 それらを包括しなくてはならないパーサーの開発は非常にチャレンジングなんだなあと感じされられます。 The Resurrection of the Fast Parallel Test Runner Youtube : [JA] The Resurrection of the Fast Parallel Test Runner / Koichi ITO @koic - YouTube Slide: The Resurrection of the Fast Parallel Test Runner - Speaker Deck [内容] サービスの巨大化に伴い、テストの実行時間が肥大化するということはあらゆる場面で問題になっています。 実行時間を短縮するアプローチはさまざまありますが、その一つにテストの並列実行があります。 並列で実行することにより、直列で実行した時と比べて理論上は実行環境の CPU のコア数倍テストは早くなるはずです。 Minitest には parallelize_me! という機能があり、マルチスレッドでの並列テストの実行が可能です。 また、 Rails 6 以降ではテストの数によって Minitest を自動的に並列的に実行するようになっています。 しかし、 Minitest 以外のテスティング フレームワーク では並列テストは有効になりません。 そこで注目したのが test-queue という Gem です。 test-queue が優れている点はまず、実行時間の最適化を図れることです。 並列実行において問題となるのは特定のワーカーの処理時間だけが長くなってしまい、 全体の実行時間がそれに引きずられて遅くなるという部分です。 同じように並列テストを行うための Gem で parallel_tests というものがありますが、 こちらはテスト開始時にあらかじめワーカーにタスクを割り振るのでこういった問題が発生してしまいます。 これに対して test-queue ではテストを実際に実行するワーカーが空いたタイミングでタスクを逐次 pop するため、 こういった問題が起こりにくくなっています。 次に優れている点が、Pluggable であるという点です。 基本的な処理の実装は共通であるものの、 テストランナーの部分は各テスティング フレームワーク の API を実行する形になっています。 これにより test-queue は RSpec を初めとして、 Minitest , Cucumber などさまざまな フレームワーク で使用が可能です。 [感想] テストコードの存在が重要だというのはもはや常識であり、 CI ツールを用いて全テストをパスさせてからリリースを行う、 というパイプラインを構築するのは一般的なプ ラク ティスとなっていますよね。 ただこれにより、テストの遅さがリリースまでのリードタイムを遅くする要因となってしまうことになります。 我々のプロダクトでもテストを如何に早くするか、ということは改めて考えないとなと思います。 Multiverse Ruby Youtube : [JA] Multiverse Ruby / Chris Salzberg @shioyama - YouTube Slide: Multiverse Ruby - Speaker Deck [内容] 登壇者の @shioyama さんが作成した gem である im の紹介となる講演です。 Ruby においてプロダクトを拡張する場合 require が利用できます。 また Rails でも採用されている Zeitwerk などの autoload を利用する場合もあるかもしれません。 この場合は明示的にファイル名を指定しなくてもモジュール名からファイルを探索してロードを行ってくれます。 ただこれら両方において問題となるのが 名前空間 の衝突です。 ロードされたモジュールは共通の permanent root となる Object の 名前空間 に置かれます。 Foo というモジュールをロードした場合、 厳密には Object::Foo になり、 Bar なら Object::Bar となります。 モジュールの開発者は自由に 命名 を行うことができませんし、 モジュールを利用する方も名前が衝突しないように気を使いながら開発する必要があります。 im では匿名モジュールの仕組みを使ってこれを解決しています。 匿名モジュールは作成された時点では 名前空間 を占有しません。 mod = Module .new => #<Module:0x000000015316e1b8> mod:: Foo = Module .new => #<Module:0x000000015316e1b8>::Foo 匿名モジュールに定数を与えた時点で名前が設定されるのですが、 この時 Object ではなく、自身で設定した独自の名前が root になります。 irb(main):019: 0 > MyFoo = mod:: Foo => MyFoo このツリーの中でモジュールを拡張しても共通の 名前空間 は占有しません。 もし MyFoo という 名前空間 で Bar をロードすると MyFoo::Bar となり、 他に MyBaz という 名前空間 があれば、その中では MyBaz::Bar となるので同じ名前で異なる機能をロードできるわけです。 [感想] プロダクトが大きくなってくると名前の衝突って結構面倒な問題です。 我々のサービスでも意図せず名前が衝突してしまって不具合の原因になったこともあります。 また、名前が衝突しないように prefix をつけたり、 名前空間 の階層をたくさん作ったりしますが、 シンプルな名前を自由につけられるようになるのは DX 向上の面でもありがたいです。 Tips and Tricks for working in the MRI Codebase Youtube : [EN] Tips and Tricks for working in the MRI Codebase / Jemma Issroff @jemmaissroff - YouTube Slide: Kaigi 2023.pdf - Google ドライブ [内容] MRI (CRuby) のメンテナンスの手順について説明してくれている How to 的な講演です。 実際にあった issue を題材にして、そのバグの調査を行う過程を見ながら解説をしてくれています。 調査のためには実際にコードの挙動を確認しなくてはいけません。 まずローカルの開発環境に ruby をクローンしてビルドしていきます。 この時全ての機能をビルドすると時間がかかるので、 miniruby という軽量版としてビルドすることも可能です。 次にテストコードを作成して実行しバグの再現をしてみます。 バグが再現することがわかったら、問題となっているコードを探していきます。 ruby で実行可能なコードは C のソースの中では rb_define_ という prefix がついている関数によって定義されていることが多いです。 例えば sum という ruby の関数が定義されている場所を探すなら、 rb_define_method.*sum で grep すれば良いわけです。 また、クラス単位でファイルが分かれていることも多いです。 例えば Array クラスの関数は array.c に定義されています。 このように該当のクラスの .c ファイルを探すというアプローチもあります。 問題の C のコードを見つけたらデバックしていきます。 ruby のコードの デバッグ には irb などが用いられますが、C のコードの デバッグ には lldb または gdb を利用します。 irb と同様に ブレークポイント の設定やステップ実行などをしながらバグの原因をさらに特定していきます。 [感想] RubyKaigi では尖った技術を駆使した最近の成果発表が多い一方で、 このような普遍的な How to を紹介する講演は少なかったのでとても印象に残ってます。 聞いてみるとなんだか自分にも CRuby のメンテナンスができそう(な気がしてくるだけ)になってきます。 僕含め RubyKaigi でこういった世界に初めて触れて面白そう!と思った方にはぜひ聞いていただきたい講演です。 Optimizing YJIT’s Performance, from Inception to Production Youtube : [EN][Keynote] Optimizing YJIT’s Performance, from Inception to Production / @maximecb - YouTube Slide: YJIT RubyKaigi 2023 slides - Google スライド [内容] Ruby 3.2 よりいよいよ YJIT が本番アプリケーションでも実用的だというアナウンスがありました。 この講演では YJIT の開発の歴史を振り返るとともに、 実際に Shopify が本番アプリケーションに YJIT を投入してどの程度パフォーマンスが向上したのか?についてお話ししてくれています。 そもそも JIT コンパイラ とはなんなのでしょう?? JIT とは Just In Time の略で、プログラムの実行時に コンパイル を行うのが特徴です。 表面上は インタプリタ の様に振る舞うため、 Ruby のような インタプリタ 言語でも違和感なく開発が行えます。 また、事前 コンパイル と違い実行時の環境に応じて最適化を図れるため、より優れたコードを生成できる可能性もあります。 YJIT は初め Ruby と同じく インタプリタ 言語である MATLAB の JIT コンパイラ が原型となっています。 そして登壇者である Maxime 氏が Shopify に Join し、μ JIT -> YJIT へと開発を進め今に至ります。 実際に YJIT は多くが Ruby で作成された Shopify の Website に本番投入され、既にパフォーマンスの測定がなされています。 2023年1月地点で応答速度が 6% 向上しており、 さらに改善が図られた2023年4月にはなんと 18% もの速度向上が認められたようです。 また以前は Warm up のタイミングで膨大にメモリを消費する点が問題となっていましたが、 開発が重ねられた2023年以降は YJIT を使わない場合とほとんど変わらないレベルにまでなっているそう。 [感想] Ruby でアプリケーションを開発している人間にとって、 採用するだけでパフォーマンスが向上する YJIT を使わない手はないよなと思いました。 我々のプロダクトでもガンガン使っていきたいです! 3日目 The Adventure of RedAmber - A data frame library in Ruby Youtube : [JA] The Adventure of RedAmber - A data frame library in Ruby / Hirokazu SUZUKI @heronshoes - YouTube Slide: The Adventure of RedAmber - A data frame library in Ruby - Speaker Deck [内容] 登壇者の Suzuki さんが開発に携わっている RedAmber というライブラリの紹介になります。 RedAmber はデータフレームを作成、操作するためのライブラリで、 同様の機能を提供しているものは他言語だと Python の Pandas や R の dplyr などが有名です。 データフレームは行方向でデータを操作したり、列方向でデータを操作したりすることができ、 非常に柔軟かつ簡単にデータ処理を行えることが特徴になっています。 RedAmber では各関数の戻り値が Ruby の Array や Hash になっていたり、 Ruby の シンタックス によく似た表現が多く使えるため、 普段 Ruby を使う人にとってはかなり直感的に操作を行うことができそうです。 また、Suzuki さんは普段はエンジニアではない仕事をしているそうで、 こういった OSS 活動はア マチュア として行っているそうです。 [感想] 世界中のあらゆる人が Ruby を一緒に盛り上げているんだなあと感じられる、 Ruby コミュニティの素晴らしさもわかる講演でした! Ruby + ADBC - A single API between Ruby and DBs Youtube : [JA] Ruby + ADBC - A single API between Ruby and DBs / Sutou Kouhei @ktou - YouTube Slide: Ruby + ADBC - A single API between Ruby and DBs - Kouhei Sutou - Rabbit Slide Show [内容] データベースとアプリケーション間でデータを読み書きするときには多くのオーバーヘッドが発生します。 これはデータが少ないうちはまだ良いですが、 大量のデータになってくるとそのオーバーヘッドによる遅延はとても許容できるものではありません。 講演内で紹介されている ADBC はそのような大量データの読み書きに適したライブラリです。 Read 時には結果セットを分割し並行に処理を行うことで高速化を実現し、 Write 時にはバルクインサートを実行し最適化された大量データの書き込みを行うことが可能です。 また、クライアント側で扱うデータには Apache Arrow データフォーマットを採用し、 こちらもデータベースのデータとの変換コストが非常に安くなるよう設計されているようです。 既に Postgres の一般的なクライアントである libpq との比較では、 1カラム1000万レコードの読み取りで2倍の高速化を実現しているとのこと。 高速化以外の面でも、抽象化された API をインターフェースに持っており、 クライアント側ではデータベースの違いを意識することなく利用できる使い勝手の良さも魅力です。 [感想] Ruby は Rails のイメージが強く、Web アプリケーション向けの言語という印象を持つ方も多いのではないでしょうか?? こういったデータ処理の分野での利用が盛り上がって、 AI やデータ分析の現場でも Ruby の存在感が増していってくれば Rubist にとってとても嬉しいことですよね! Code indexing: How language servers understand our code Youtube : [EN] Code indexing: How language servers understand our code / Vinicius Stock @vinistock - YouTube Slide: Code indexing: How language servers understand our code - Speaker Deck [内容] ruby-lsp は Shopify が提供する Ruby のためのモダンな LSP (Language Server Protocol) です。 LSP はエディターと連携して開発者体験を向上させるための様々な機能を提供してくれます。 今回の講演では Go to definition 機能がどのように実装されているのか?というのを詳しく紹介してくれています。 Go to definition は具体的にいうと、呼び出している関数やクラスなどが定義されているファイル、場所にジャンプする機能です。 エディター上でクラス名などの一部をクリックすると、 ファイル名とクリックした場所を含むオブジェクトがどこにあるのか?という index 情報を LSP にリク エス トします。 index 情報にはファイル中の何行目の何文字目にそれがあるのか?という情報が含まれており、 ファイル名と組み合わせることで何がみたいのかを一意に特定できるわけです。 LSP 側では index 情報を受け取って、その場所にあるオブジェクトは何か?を特定し、 また、その定義がある場所をエディター側にレスポンスしなくてはいけません。 このためにはあらかじめ全てのコードの index 情報を持っておく必要があります。 なので LSP では立ち上げ時にこれを行っています。 また、LSP 立ち上げ後のコードの変更に対応できるよう、 変更を検知すると index 情報も併せて更新するよう設計されています。 [感想] これらの機能は Ruby で開発されていて、 この講演では設計/実装内容もかなり詳しく説明されています。 気になるかたは動画の方を見ていただけますとかなりおもしろいと思います! Unleashing the Power of Asynchronous HTTP with Ruby Youtube : [EN] Unleashing the Power of Asynchronous HTTP with Ruby / Samuel Williams @ioquatix - YouTube Slide: presentations/2023/Unleashing the Power of Asynchronous HTTP.pdf at main · ioquatix/presentations · GitHub [内容] HTTP はあらゆるデータをクライアントとサーバー間でやりとりするための 通信プロトコル です。 現在のインターネットの根幹をなす技術と捉えて差し支えないでしょう。 HTTP は現在から 30 年以上前に誕生し、当初はテキストファイルのみのやり取りしかできませんでしたが、 Web の発展と共に画像や音声データ、動画までをサポートし徐々に進化してきました。 中でも HTTP 1.0 は広く我々に受け入れられ、 長らく Web の世界のスタンダードとなっていましたが、今はその転換機にあると言えます。 まず初めに HTTP 2.0。特に重要なアップデートがストリーミングの多重化による並列処理です。 HTTP 1.0 まででは1つのコネクション内で一度に1つのリソースのやりとりしかできませんでした。 しかし、HTTP 2.0 では同時に複数のリソースを処理できるため、 高速化が期待できるのと、大きなコンテンツを取り扱うと他の処理が待たされる(HoL ブロッキング )のを回避できます。 次に HTTP 3.0。ここでは HTTP 2.0 にあった欠点をさらに克服しています。 HTTP 2.0 では並列処理といってもコネクションは同じなので、ここが途絶えれば全ての処理が止まります。 しかし HTTP 3.0 では コネクションレス であり多重化されたストリームは独立で、他の処理に影響を与えることはありません。 また固有のコネクションIDを持ち、これらは IP アドレスなどが変わっても固定です。 すなわち途中で通信が途切れて別のネットワークに変わっても継続して処理を行うことが可能です。 これらの機能は通信が不安定になりがちな、屋外でのインターネットの利用において非常に強力です。 今や多くのユーザーが スマートフォン からインターネットを利用しており、 HTTP 3.0 はこれからのスタンダードになっていくことが予想されます。 ただ、 Ruby にて HTTP 2.0, 3.0 をサポートしているアダプタはほとんどありません。 登壇者の Samuel 氏は falcon という HTTP Server の開発を進めており、 既に HTTP 2.0 はサポート済み、年内には HTTP 3.0 のサポートを開始するようです。 [感想] ゴリゴリにネットワークの話で、RubyKaigi のなかでも異色を放っていた講演でした。 ただ自分のような Web 業界の人間にはとても刺さる内容で強く記憶に残っています。 Parsing RBS Youtube : [EN][Keynote] Parsing RBS / Soutaro Matsumoto @soutaro - YouTube [内容] Ruby の型定義を記述するための RBS のパーサーを改善しました、という講演です。 RBS のパーサーはこれまでエラートレラントではなかったので、 シンタックス エラーが発生するような場合に 構文解析 が中断し、 そのエラー理由を知ることもできないという状態でした。 改善されたパーサーでは3つのアプローチでエラートレラントを実現しています。 まず初めに MissingTree の導入。 正しい構文には始端となる トーク ンがあれば終端となる トーク ンが対となって存在する必要があります。 例えばクラスを定義するための class という トーク ンがあるなら、その後に end がどこかで現れる必要があるわけですね。 もし終端の トーク ンが存在しないときは シンタックス エラーを raise する代わりに MissingTree としてノードを作成し、 構文解析 を先に進めます。 次のアプローチは不要な トーク ンをスキップすることです。 attr_reader foo: -> String のような場合ですが、 本来 attr_reader をパースするとき : の後に -> がくるのは構文エラーになるのでここで解析が終了してしまいます。 特定の トーク ンの後に来ても問題のない トーク ンをあらかじめルールとして用意しておき、 ルール外の トーク ンはスキップすることでこの問題を回避できます。 最後がネストされた定義のパースに関する問題です。 例として以下のような構文を見てみましょう。 class Foo class Bar def initialize end Bar クラスに終端がなく、 initialize 関数にも終端がありません。 このような場合どちらも MissingTree として Bar の関数として initialize 関数があると解析されます。 これを以下のように変更したらどうでしょう? class Foo class Bar ; end def initialize end Bar クラスが編集されて終端記号が補われました。 編集後は initialize 関数は外側のクラスの Foo の関数として解析される必要があるわけです。 これを解決するためには終端記号を最後に補った場所に [EOF] のような トーク ンを追加します。 再度パースを行う際に [EOF] を見つけたらネストを抜け、 外側のメンバーとして解析を行うようにすることでこの問題を解決しています。 [感想] パーサーに関する講演は RubyKaigi 中にも多くありましたが、 みなさんそれぞれ違ったアプローチで問題解決に取り組んでいました。 比較しながら聞いてみるとおもしろいと思います! 来年は、沖縄で会いましょう! ありがとうございました! 株式会社 エニグモ すべての求人一覧 hrmos.co
今回はQAエンジニアのインタビュー記事です。 エニグモ のQA業務の詳細や、QAチームが抱えている課題や今後の展望などについてお話を伺いしました。ぜひご覧ください! インタビュイープロフィール:K.I ソフトウェア検証(第 三者 検証)企業でクライアントの様々なプロダクト・サービスの品質管理業務に携わり、2019年に エニグモ に1人目の専任QAエンジニアとして入社。 どのような業務を行っておりますか? 「 BUYMA 」を中心とした エニグモ の自社サービスの、Webサイトやアプリの品質保証に関する業務を担当しています。 私が所属するチームはサービスエンジニアリング本部というエンジニア組織のQAチャプ ターです。現在QAチャプターの専任エンジニアは1名体制です(2023年6月15日時点)。 チャプターとは専門性を持ったエンジニアのチームで、QA以外にもフロントエンドチャプターなどがあります。 具体的な業務はQAエンジニアとして、担当プロジェクトの上流工程から関わり、仕様の段階から不具合がないかを確認し、テスト項目を開発側と決めて、開発が始まればテストを行います。また自動テストは Autify を利用しており、会員登録・購入・発送などのシナリオを毎日実行して、Slack へテスト結果を通知しています。 直近関わったプロジェクトは、他社との配送サービスの連携や出品者向けの新規画面の開発などを担当していました。 QAエンジニア1名体制で、どのように エニグモ 全体のQA業務に対応してますか? もともと エニグモ は専任のQAエンジニアがいない時期の方が長かったこともあり、開発のフローにテストやQAの視点が組み込まれており、ディレクターや開発者がテストを含めた開発や設計を行っていました。 大中規模のプロジェクト、特に iOS 、決済など、不具合があった際、影響が大きいプロジェクトを中心にQAエンジニアの私が担当しておりますが、QAエンジニアがいる前提でないプロジェクトもあります。 プロジェクト参加の流れを教えてください。開発者やディレクターと関わりながらどのように進めていますか? 参加の流れは、開発者やディレクターから依頼があり、そこで開発スケジュール(リリースの予定や、テスト・開発の期間)を確認します。さらに、ミーティングでより詳細の内容をドキュメントやデザインを見ながら、QAなりの観点で他の機能との不整合がないか、考慮漏れしている画面がないかを確認します。 その後テスト設計や実施を進め、不具合があればチケットを起票して開発者と共有しています。 また、所属するプロジェクトの朝会や定例に参加し、その日の作業や抱えている課題の共有を行います。スプリントの終わりには振り返りを行っています。 開発者やディレクターとは常にコミュニケーションがとれる状況で、不明点があれば、slackやzoomなどですぐに確認ができます。リモートで勤務しているメンバーが多いですが、あまり出社時と変わらない環境で開発を進めています。 現在のQAチームの課題と今後の展望を教えてください。 BUYMA の開発チームは ドメイン ごとに主に4つに分かれています。具体的には、出品者向けの機能を開発するチーム、購入者向けの機能を開発するチーム、モバイルアプリチーム、決済や配送の開発を行うチームです。 それぞれ、開発者やディレクターが所属しておりますが、QAは現在私1名なので、QAエンジニアが上流から入れていないプロジェクトがあることや、案件が重なった時にプロジェクトのリリースを調整する必要があることが課題です。 今後の展望は、QAチームを拡大し、ゆくゆくは各 ドメイン チームに最低1名ずつQAエンジニアが所属する体制にしたいです。QAエンジニアが全ての開発案件に上流の設計フェーズから関わり、プロダクト全体の品質向上を目指したいです。 さらに、そういった体制になることで、プロジェクト アサイ ン時に必要な、 ドメイン ・開発機能のキャッチアップのプロセスが短縮化することで、QAのみでなく開発プロジェクト全体の開発サイクルが速くなることを期待しています。 最後に 現在、QAエンジニアも募集しておりますので、ご興味をお持ちいただけましたらお気軽にエントリーください。ぜひお話ししましょう! QAエンジニア以外も、 エニグモ では技術課題の解決、サービスグロース、ビジネスやユーザーの課題解決のための開発など、より良いシステム作りを一緒に進めていくためのメンバーを募集しています。 hrmos.co
自己紹介 初めまして!2023年4月に新卒で入社した中村友哉です! 入社して早2ヶ月経ちましたが、早く仕事を覚えるためにがむしゃらに働いております。 この記事では、エンジニア就活のことや、 エニグモ に入社してどう感じたかお話しして行こうと思います。 エンジニアを目指したきっかけ 私がエンジニアを目指したきっかけは、小学校からの幼馴染です。 彼とは大学生になっても毎年会うような仲で、よくくだらない話で盛り上がっていました。しかし、大学3年生になると急に意識が高くなっており、別人のようでした。話を聞くとエンジニアを目指すようになってから考え方・行動力を意識して変えていったとのこと。 将来の理想的なキャリアを想像し、そこから逆算して今何をすべきか考え実行する、そんな彼の行動・姿勢に感銘を受け、彼のように自分の考えを持った人間になりたいと思いエンジニアという職業に興味を持ち始めました。 そこから、とりあえず勢いに任せて Python の入門書を買ってプログラミングの勉強を始めました。意外と新しいことをやってみるのは楽しく、 競技プログラミング で アルゴリズム の勉強もするようになりました。 学部時代はプログラミングの授業があり、情報系の知識に触れる機会がありましたが、当時は苦手意識を持っており、まさか自分がエンジニアを志すとは夢にも思いませんでした。 エンジニアになるためにやったこと 大学院に進学するタイミングで就活をスタートしましたが、IT業界・エンジニア種類などの知識が皆無だったことから、どんなエンジニアになりたいか考えるところからスタートしました。 試行錯誤 まずは Python をやっていたこともありデータ周りの技術を学びましたが、途中フロントやバックエンドの技術も学び、現状のスキルのみで自身の適正を判断しないよう心がけました。 この時点では確信は持てなかったものの、データ周りの技術に興味が湧き、データサイエンティストを目指すようになりました。大学院の専攻は電子工学というソフトウェアと真逆のものでしたが、指導員の方と相談して 機械学習 を取り入れた研究ができることになり嬉しかった思い出があります。 それからは、 統計学 ・データ分析をはじめとし、SIGNATEというデータ分析コンペなどで 機械学習 モデルの勉強に取り組みました。 方向性の決定 データサイエンスを学んでいくにつれデータを用意するところが難しいことを知り、その泥臭い作業を担当するデータエンジニアという職種があることを認識しました。 確かに個人レベルで扱うようなデータは綺麗なものが多く、整形せずとも使えることが多かったので気にしていませんでしたが、企業が扱うような ビッグデータ は扱える形にするには多くの苦労を伴うだろうと想像できました。 そこで、ビジネスの世界でどのようなデータが扱われ、どんな苦労が発生するのか知りたいと思い、 修士 一年の12月から東京のとある企業でデータサイエンティストとして インターン を始めました。 インターン 経験 インターン では以下のように幅広い技術を経験させていただきました。 データ分析基盤の開発・運用 BIツールの整備・ビジネスサイドへの展開 自然言語処理 NLP モデルのPoC・プロダクト応用 MLOps プロンプトエンジニアリング その中でも、私が特に興味を持ったのはデータ分析基盤の開発業務でした。というのも、データを活用しようにもデータを活用できる環境がないことには何も始まらないので、非常に重要な領域だと感じたからです。 そのような基盤作りに携わり、社内の関係者・ビジネスに貢献していきたいと思い、データエンジニアになることを軸に就職活動に臨みました。 ポートフォリオ の作成 就職活動に望むにあたって、自分のやりたいこと・技術スタック・課題解決能力などを伝えるために ポートフォリオ の作成も行いました。 私は当時フットサル部に所属しており、我が部のコミュニケーションにおける課題を解決するために部活仲間と協力し、LINEbotを作成しました。ユーザーの要求を満たすことを最優先に考え、できるだけフラットな視点で技術選定を行ったり、私が卒業した後のことも見据え、運用しやすい構成にするなどの工夫を凝らしました。 この開発経験は、データエンジニアとしての技術を伸ばすというよりも、エンジニアとして課題を解決する際の思考・取り組み方を改めて考える良い機会となりました。 就職活動〜入社 修士 一年の1月くらいから本格的に就職活動をスタートしました。 ドメイン にこだわりはありませんでしたが、昔から服が好きだったので、できればアパレル系のデータを扱える企業で働きたいという願望がありました。しかし、アパレル系の事業会社でエンジニアを募集しているところはそもそも母数が少ないので、探すだけでも一苦労でした。 そんな中、自分の好きなインポート系のブランドを扱いつつエンジニアを募集している エニグモ を見つけました。 エニグモ のことを調べていくうちに、「ここしかない!」と思いすぐに応募しました。 一次面接から早速データエンジニア・マネージャーの方とお話しすることができ、自分のやりたいこと、 エニグモ の目指しているところを早い段階で擦り合わせることができました。個人的な野望として、ブランドの情報を集めて最新のファッション動向を知れるようなプラットフォームを作りたいと思っており、そのような考えに共感してくださり、現場のエンジニアの方と意気投合したことを覚えています。 この時点で、ご縁があったら絶対に エニグモ で働きたいと思っていたので、続く二次面接・最終面接では他のチームの業務や、社長の目指しているところを把握することを主眼において面接に臨みました。 面接を担当していただいた方々は全員優しく、素の自分を受け入れてもらったことが印象的でした。内定後も人事の方と何回か面談する機会を設けていただき、不安要素が全くない状態で入社することができました。 入社して2ヶ月経ち感じること 入社後、データ・ 機械学習 ・検索の基盤開発・運用を行うデータテク ノロ ジー グループに配属されました。具体的な業務としては、データエンジニアとしてデータ分析基盤の開発・運用を担当し、他部署からのニーズに応えるため日々データ周りの整備を行います。 入社して一ヶ月くらいは単語レベルでわからないことが多く、いわゆるパニックゾーンに入っていました。特に、現状把握する上でドキュメント等が完全に整っている訳ではないので、現場のエンジニアが前提として備えている技術・知識をキャッチアップするのに苦労しました。 しかし、困っているときは、一人でデータエンジニアを務めてきたメンターの方や、 エニグモ でのエンジニア歴が長く、開発背景を熟知しているマネージャーの方が親身に相談に乗ってくださるので、滞りなく業務することができました。 二ヶ月経った今、まだ周りの方々の手助けなしではタスクを完遂できないので、早くチームの方々、会社に貢献できるような人材になるべく、多くの経験を積んでいきたいと思っています。 今後の抱負 まずは、一人で滞りなく業務を遂行できるようになることを目標にしています。 大きな目標としては、データドリブンな意思決定ができる環境をさらに整え、データの 民主化 を目指していきたいと考えています。 入社式で社長から激励のお言葉をいただき、その中でも「コンフォートゾーンに逃げない」という言葉が胸に刺さりました。その言葉を信条に、日々業務に取り組んでいきたいです。 最後に この記事を見て、エンジニア就活に役立ててくださる方がいらっしゃれば幸いです。 データエンジニアは地味なイメージを持たれる方が多いと思いますが、ファッションデータを扱える弊社は、服好きにとっては楽園のような環境だと思います。 とは言っても、弊社はファッションにそこまで興味があるわけではないエンジニアも多いです。ファッションに興味がないけどデータエンジニアを志す方にとっては、分析基盤の拡大フェーズを経験できるという点でとても魅力的な環境だと思います! ざっくばらんな話になってしまいましたが、最後までお読みいただきありがとうございました!