トレタ開発者ブログ

飲食店向け予約/顧客台帳サービス「トレタ」、モバイルオーダー「トレタO/X」などを運営するトレタの開発メンバーによるブログです。

トレタにJOINして勉強になったRailsに関する3つを紹介

この記事はトレタ Advent Calendar 2020の22日目の記事です。

はじめまして。2020年3月からJOINしたサーバーサイドエンジニアをしている @shiroemons です。

Appleから AirPods Max が発表されて、SonyのWH-1000XM4を即購入して、ノイズキャンセリング機能と2台の機器に同時接続できるマルチポイント機能に感動している今日このごろです。 マイク性能に関しては、手持ちのAirPodsの方が良さそうなので、ビデオ会議の時は切り替えが必要なのが残念ポイントでした...

本日は、私がトレタにJOINして、勉強になったRailsに関する3つを紹介します。

config/routes.rb のファイル分割

1つ目は、巨大になりがちなルートファイル(config/routes.rb)を複数のファイルに分割するTipsです。

↓でconfig/routes.rbにdrawメソッドを追加することでルーティングの内容を別ファイルに分割できるようになります。

# config/routes.rb
class ActionDispatch::Routing::Mapper
  def draw(routes_name)
    instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb")))
  end
end

Rails.application.routes.draw do
  draw :admin
end

# config/routes/admin.rb
get :foo, to: 'foo#bar'

DHHのgistもあります。

こちらは、Rails 4.0がリリースする前に一度取り込まれた機能になりますが、revertされました。 ところが、最近リリースされたRails 6.1で、この機能が復活しました🎉

Rails 6.1からは、今回のTipsは不要になります。

# config/routes.rb
Rails.application.routes.draw do
  draw(:admin)
end

# config/routes/admin.rb
get :foo, to: 'foo#bar'

Rails Guidesもすでに用意されています。(日本語はまだかな)

guides.rubyonrails.org

ルートファイルを分割したい場合は、試してみてください!

APIドキュメントツール apipie-rails

ここ最近のRailsの役割は、APIで利用されることが増えてきました。 必要になってくるのは、エンドポイントは何で、どんなパラメーターが必要で、どんなエラーが発生するのかなどのドキュメントが大事になってきます。

そこで重宝されるのは、REST API向けのドキュメント生成ツールのapipie-railsです。

github.com

ここでは、設定方法などは省略します。設定方法などは、GemのREADMEをご参照ください。

コントローラーのメソッドの上部に、HTTPメソッドやエンドポイント、パラメーター、エラーなどの説明を記載することで簡単にAPIドキュメントが残すことができます。 学習コストが少なくすぐに利用することができます。

サンプルコード

class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  api :GET, '/users', 'ユーザ一覧を返します'
  def index
    @users = User.all
  end

  api :GET, '/users/:id', '指定したIDのユーザー情報を返します'
  param :id, :number, require: true, desc: 'ユーザーID'
  error code: 404, desc: 'Not Found'
  example <<-EOS
  {
    "id": 1,
    "name": "hoge",
    "created_at": "2020-12-17T05:23:18.135Z",
    "updated_at": "2020-12-17T05:23:18.135Z",
  }
  EOS
  def show
    #...
  end
  
  #...

画面キャプチャ

f:id:shiroemons:20201221164343p:plain
サンプル画面

このような、ドキュメントが簡単に生成することができます。

apipie-railsは、Swagger形式(OpenAPI 2.0)のJSON出力が可能です。

rake apipie:static_swagger_json

GemのREADME にも記載があるので参照してみてください。

APIドキュメントの管理は、苦労したことがあるので簡単に生成できるのでとても感動したので、紹介してみました。

コールバッククラス

Railsを運用していくとどうしてもコールバック処理が煩雑かつ、複雑になりがちです。 そんな時は、Railsガイドにも載っているコールバッククラスを利用します。

railsguides.jp

サフィックスにDispatcherを付けてコールバッククラスを利用しています。 主な用途としては、複数のモデルから共通の非同期処理を行う Worker の呼び出しに使用しています。

以下は、店舗の席情報に変更が行われた際、非同期処理で席在庫を再計算するサンプルコードです。

# app/callbacks/restaurant_table_stock_manager_dispatcher.rb
class RestaurantTableStockManagerDispatcher
  def after_commit(record)
    rebuild_stocks_async(record)
  end

  private

  def rebuild_stocks_async(record)
    # 非同期処理(Worker)呼び出し
  end
end

# app/models/restaurant_table.rb
class RestaurantTable < ActiveRecord::Base
  after_commit RestaurantTableStockManagerDispatcher.new, on: [:update]
end

このようにafter_commit等のコールバックに対応するメソッドをパブリックメソッドとして定義します。 引数にはコールバックを呼び出すキッカケになったオブジェクトが渡されます。

コールバッククラスは、コールバック処理すべてをコールバッククラスにするのではなく、 複数のモデルで同一の処理を行う場合や、複雑な処理を行っているものなどの機能単位に切り出せるものに留めておくのが良いかと思います。

コールバッククラス自体は、目新しいものではありませんがバリバリ活用していたので、紹介したかったところです。

おわりに

1つ1つは、目新しいものではないですが、実際に利用し活用していますよという発信の意味を込めて紹介してみました。

トレタに少しでも興味を持っていただいた方がいれば、ぜひ遊びに来てください! 仲間を募集しています!

corp.toreta.in

超短期プロジェクトでのQAエンジニアの立ち回りを振り返ってみた

※この記事はトレタ Advent Calendar 2020 20日目の記事です。

こんにちは。QAエンジニアの坂田です。

今年はコロナ禍で大変な年でしたね。私はそんな中ボルダリングに嵌ってしまい、感染対策を心掛けつつジムに通う毎日です。

さて、弊社代表がオーナーを務める「豚組しゃぶ庵」というしゃぶしゃぶのお店が先日「豚組しゃぶ庵β」として期間限定リニューアルオープンしました。
※ リニューアルの経緯やコンセプトは以下のひとしさんのエントリーをご覧ください

note.com

こちらの予約サイト、お店の間取りを見て好きな個室を予約できるという飲食店では珍しい仕組みを採用しています(「席指定予約」と呼んでいます)。従来のトレタには無かった仕組みのため、裏側のシステムを含めほぼゼロからの新規開発でした。

reserve.ox-shabuan.jp

席指定予約の構想と実装方針が固まってきたのが10月半ば頃、その時点で引いたリリース日が12月7日という1ヵ月の超短期プロジェクトで、私がQAとしてメンバーに入ったのは11月10日でした。今まで経験した中でかなりの突貫プロジェクトでしたが、スケジュール通りに無事リリースを迎え、大きな問題なく稼働しておりホッとしています。

本記事では、このプロジェクトでやったことを振り返りつつQAとしての立ち回りのポイントをいくつか挙げてみたいと思います。スピード優先の開発での品質担保に興味のある方、少しでも参考になれば幸いです。

※ ちなみに、席指定予約のバックエンドの話をいっしーが 12/14のアドベントカレンダー で詳しく語ってくれています。是非あわせてご覧ください!

プロジェクト全体の振り返り

f:id:asktn:20201220151853p:plain
プロジェクト期間中の実装・QAの動き

上の「テスト」の欄に私の大まかな動きをまとめました。
(休日にも便宜上ラインが引かれていますが、当然ながらお休みです)

機能テストやマルチブラウザテストの実施期間よりも、仕様書作成やスモークテスト(デプロイ単位で行う簡易的な動作チェック)に掛けた期間の方が長くなっています。

これは後でも述べますが、デザイン案をベースとした実装方法にリスクが高く、視覚的には想定しづらい抜け漏れが大量に発生し得ると考えたためです。

抜け漏れをテストフェーズまで持ち越すとスケジュール破綻やリリース時品質の低下を招くため、とにかく早期に機能全体をチェックして未確定事項を挙げられるだけ挙げ、デザイナ・エンジニア間で都度あるべき挙動をすり合わせながら進むことを最優先しました。

ちなみに仕様書の手直しはリリース直前まで頻繁に行っており、仕様書に関わった時間の割合は上で引いたラインよりもかなり多いです。結局テストフェーズまで持ち越してしまったバグも幾つかありましたが、結果的にはかなりの数を予防・早期発見の方向に寄せることができました。
(マルチブラウザテストの開始タイミングが遅く、大きめのブラウザ依存のバグを終盤まで見逃してしまったことは反省点です)

さて、以降は時系列を追って詳しく振り返っていきたいと思います。

キャッチアップとテスト計画作成

f:id:asktn:20201220142422p:plain:w486
ごあいさつ。

参加初日にまず取り掛かったのは状況確認です。「超短期で皆多忙」「途中参加」「原則リモート勤務」という制約もあって、必要な情報は全部自分から取りに行きました。esaのドキュメントやSlackの過去ログを漁れるだけ漁り、体制・役割分担・スケジュール感・各種ドキュメントの有無・タスクの進捗状況などを確認していきます。 (弊社では社内の情報共有にesaを使っています)

同時にテスト計画に取り掛かり、アウトプットベースでQA活動に関する認識合わせの土台を作ります。書き始めからメンバーへ共有し、初日のうちにフィードバックを貰いつつ大体のアウトラインを固めました。

f:id:asktn:20201220150840p:plain:w485
テスト計画を書きながら状況確認を進める

テスト計画はシンプルに、事前にステークホルダーに最低限伝えておくべきことと、メンバーに事前承認を取っておくべき項目に絞って書きます。

f:id:asktn:20201220163630p:plain:w251
テスト計画の項目抜粋。大体2〜3時間程度で書き終えられる分量。

状況確認や情報収集にあたりポイントだったのは、当たり前っぽい質問でも知らなければ躊躇なく聞いてしまうことです。
もちろん漁れる資料は漁っておくなどメンバーになるべく負荷を掛けないよう配慮が大事ですが、

  • 各メンバーとのコミュニケーションの下地を作れる
  • 既存メンバーの情報量に追いつき早くから一人前の働きができる
  • 当たり前のようでいて実は認識が揃っていなかったこと、決まっていなかったこと、抜けていたことを発見できる

といった大きなメリットがあります。実際、本プロジェクトでもチーム体制・役割など当たり前の情報がまとまっておらず、キャッチアップのついでにプロジェクト概要を整理してドキュメント化したりもしました。

f:id:asktn:20201221061006p:plain:w485
知らないことが沢山。とにかく教えを乞う。

情報収集を通して分かったプロジェクトの全体像はおおよそ以下の通りです。

  • 開発の主力はデザイナ1名とエンジニア4名(フロント2、バックエンド2)の少数精鋭
    • PM/POはプロジェクト複数掛け持ちで多忙、エンジニアは一時的なヘルプの増員あり
  • ざっくりした要件や開発方針はチーム内で共通理解が取れているが、要求仕様に関するまとまった文書は無い
  • デザイナが要件をデザイン案(画面設計と画面遷移図)に落とし、それをベースに実装が始まっている
  • 実装が全て完了してからのテスト期間はせいぜい5日間程度しか取れそうにない

ここでQAとしてリスクを感じたことは、テスト期間もそうですが何より一番はデザイン案と実装との間に情報のギャップが大きすぎることです。

デザイン案はユースケースの基本的な流れや画面の構成・機能を視覚的に分かりやすく把握できますが、実装に必要な情報を全て与えてくれるものではありません。
特に以下のような動的な挙動や非視覚的な処理の表現には不向きです。

  • 各画面要素の詳細な挙動や状態変化
  • 画面要素同士の相互作用
  • 入力値の保持やクリア
  • 時間経過が関係する挙動
  • バリデーション処理
  • エラー発生時の挙動
  • バックエンドの仕様や制約に依存する処理

このまま進むと「エンジニアが気づいた都度よしなに対応する」という対処になりますが、抜け漏れや手戻りが多く非常にリスキーです。
そこで、品質担保のため、まずデザイン案と実装との間の情報ギャップを埋め、実装が進行する前に未確定事項を極力つぶすことを最優先に進めることにしました。

機能仕様書の作成

f:id:asktn:20201221072627p:plain:w500
markdownで仕様を書いていく

3日目、技術的なキャッチアップと並行して、デザイン案をベースに機能仕様書を書き始めました。

QAエンジニアが仕様を書くのに違和感を覚える方がいらっしゃるかも知れませんが、QA自身にとってもメリットが大きく、取り掛かる意義があります。この辺りの意義は昨年のアドベントカレンダーに書きましたので宜しければご覧ください。

tech.toreta.in

仕様を書くことで、細部の未確定事項や矛盾に気づけます。デザイナやエンジニアが既にプロジェクトの細部にどっぷり浸かっているのに対し、フレッシュな第三者視点で全体を眺めて疑問点を洗い出せたことも良い点でした。

取り急ぎデザイン案から想定される挙動と、他に検討すべき項目を2日間で整理し、チームメンバーに共有しました。未確定箇所や要確認事項はとりあえず [TBD] とラベルを付けておきます。

また、仕様整理後すぐにデザイナ・エンジニアを交えて仕様書の読み合わせを行いました。視覚的に分かりやすいデザイン案と異なり、言語とロジックで記述された仕様はとっつきにくく誤解を招きやすいためです。読み合わせの場で [TBD] を解消できる可能性があるというメリットもあります。
(エンジニアは既にデザインベースでよしなに実装を進めていたため、単に共有しただけでは読んでもらえないだろう、という事情もありました)

読み合わせの結果、デザイナやエンジニアから沢山のフィードバックが得られました。デザイン・実装面での制約や解決のアイデア、データの実体や内部ロジックの詳細などです。
これらはテスト設計のインプットとしても有用であり共有価値もあるため、機能仕様そのものではありませんが仕様書に [補足] として追記していきました。

f:id:asktn:20201221081216p:plain:w500
補足情報や要件変更に絡む情報も一緒にまとめていく

[TBD] はメンバーが持ち帰り検討したり、大きなものはPOに確認を取ったりして確定していきます。また、実装が進む過程で要件漏れや不具合・それに伴う要件変更が次々と発生するため、その都度仕様に反映してチームに共有し、必要があれば再度読み合わせを行いました。 (結果的に、リリースまでに30回以上のドキュメント更新を行いました)

スモークテストと機能テスト

f:id:asktn:20201221083214p:plain:w943
テスト計画に記述した各テストタイプの位置づけ

仕様反映作業がひと段落したタイミングで、エンジニア側も一連の画面遷移や主要なコンポーネントの実装が完了し、UIでの確認が可能な状態になりました。そこで、後続の機能テストのためにテストパターンを作成しつつ、出来上がった実装部分のスモークテストを先行して実施することにしました。

スモークテストに必要なテスト用データは予め投入されていることを確認済みで、実行環境はstg,prod環境が未構築のタイミングだったためローカルマシンでビルドし実行する、という手段を取りました。

実装作業と並行してローカルでスモークテストを実施する際の制約として、どこが未実装でどこが実装済なのか区別がつきづらい、という点があります。実装の進捗状況はデイリーで大まかに把握はしていますが、部分部分にいつどこまで手が入るかは細かく把握できず、都度エンジニアに聞くのもコストになります。

そのため、スモークテストは

  • 主要なユースケースに沿った正常系の機能確認(トップ画面から順に遷移してちゃんと予約完了まで到達するか)
  • 実装上の要注意箇所に絞った動作確認(入力した条件に合致した空席情報が表示されるか)

といった観点に絞って実施しました。

また、事前にバックエンド担当のエンジニアと連携を取り、各データストア(Hasura, Closure, Firestore)上で空席データや予約完了データを直接参照できるように権限を貰っておきました。これによりデータの流れを含め確実にE2Eで追える状態になり、スモークテストだけでなく機能テストで詳細なロジック検証を行う際にも役立ちました。

次にテストパターンの作成および機能テストについてです。

f:id:asktn:20201221092941p:plain:w203
テストパターンの大項目

概ね「ユースケース」「ロジック」「エラー処理」「各画面要素の挙動」を軸にテスト観点を洗い出しました。

「ユースケース」は画面の行き来や繰り返し実行、異なる操作の組み合わせや時間経過に注目してユーザの一連の操作を展開しています。
「ロジック」はある程度複雑な内部処理や判定条件が絡むもの、「エラー処理」は異常な入力のパターン、「各画面要素の挙動」は個別のコンポーネントごとの依存関係や状態変化のパターンに注目してケースを洗い出していきます。

この辺りの観点整理はテスト対象のシステムによって最適解が異なると思いますが、私は(特にWebアプリの場合は)上記のフレームワークをよく使っています。

テストパターンおよびテスト結果の記述には、やはりesaを用いました。スプレッドシートやSaaSのツールなど様々なテストケース管理手段がありますが、本プロジェクトではテストパターンの作成・管理コストを最小限に抑えたいことと長期的な運用の想定は不要な点から、使い慣れていてかつ他メンバーも参照しやすい点を重視しました。

機能テストは、テストパターンに対応したテストデータの投入とstg環境のデプロイが完了したタイミングで着手しました。

この時点では既にほとんどの実装が完了しており、スモークテストで検知した不具合も修正済みだったため、大きな不具合や手戻りに悩まされることなくテストを実施できました。エンジニアも余勢を駆ってユニットテスト追加を精力的に進めてくれ、システム全体をより強固にすることができました。

最終確認とリリース

f:id:asktn:20201221095815p:plain:w300
macSafariでは input type="date" でカレンダーが表示されない...だと...!?

プロジェクトも終盤に入り(といっても4週目ですが)、大方の機能確認が完了したタイミングでマルチブラウザテストに入りました。
席指定予約システムはスマートフォンからの予約を主対象にしていますが、デスクトップブラウザ経由のアクセスも想定し、主要なサポート対象ブラウザでの動作確認を行いました。

ここで想定外に不具合が出たのが誤算で、上図のようにブラウザ標準のカレンダーが表示されないブラウザがあったり、個室の間取りの上に予約の◯×マークが正確に表示されずずれてしまうブラウザがある、等のバグを発見してしまいました。
これは機能テスト開始のタイミングでデスクトップブラウザも簡易的にチェックしておくべきだった...と、良い反省材料になりました。 (ブラウザ標準カレンダーの不具合は、近々のリリースで独自のカレンダーUIを実装することで解消見込みです!)

ともあれ、デザイナとエンジニアがすぐに代替案や修正方針を提示してくれたお陰でリリースには響かず、事なきを得ました。席指定予約のリリースは当初お店のオープンに合わせた12/7を予定していましたが、開発がスケジュール上の余裕を少し残して完了したため、12/3に内部リリースして本番動作確認を行い、12/4に外部向けの本番リリースを迎えることが出来ました!

まとめ

QAとして立ち回ったポイントを改めてまとめると、

  • 早期に機能全体をチェックして未確定事項を特定し、デザイナ・エンジニア間で都度あるべき挙動をすり合わせながら進むこと
  • プロジェクト参加直後は情報を漁れるだけ漁った上で、当たり前っぽい質問でも躊躇なく聞いてしまうこと
  • 仕様をドキュメントに整理し、早期にデザイナ・エンジニアを交えて仕様書の読み合わせを行うこと
  • なるべく実装と並行してスモークテストを実施し、主要なユースケースや実装上の要注意箇所に絞った確認を行っておくこと
  • 環境依存のバグを侮るなかれ

という感じかなと思います。

それにしてもスケジュール通りリリースできて良かったです。
プロジェクトへの献身と惜しみないサポートをくれた優秀なチームメンバーに感謝しつつ、本記事を結びたいと思います。

おっと、、トレタでは新しい仲間も募集中です。興味を持った方は是非。

エンジニア採用 | 株式会社トレタ

AmazonConnectのリアルタイムダッシュボードを作る話

本記事はトレタアドベントカレンダー2020、21日目の記事です。

こんにちは、フロントエンドエンジニアの北川です。 去年の記事を振り返ってみるとリングフィットアドベンチャーを始めた時期でしたが、 一年越しでクリアして今はFitBoxing2へ移行しました。

はじめに

自分は主にトレタCCという飲食店の電話代行を行うコールセンターのシステムを普段作っているのですが、 今年はコロナの影響もうけていろいろありました。。 途中で方針変更をしたり、リリースを早めたりなど一年を振り返るとあるのですが、 今回はちょっと新しめの技術を取り入れてコールセンターのリアルタイムダッシュボードを作ったことを紹介します。 ただし、諸事情によりまだ完成してないのでほぼ作った話です。。

コールセンターにおけるダッシュボード

トレタCCではオペレーターが電話対応するフロアがあり、フロアには当日のコール数の推移などを表示するモニターがあります。 全スタッフが見れる状態にあることで、現状のコールの状況やスタッフの過不足などを共有するのに役立てています。 そこでダッシュボードをよりリアルタイム性をあげるために、新しいダッシュボードを作ることになりました。

ダッシュボードに表示させたい内容は、

  • リアルタイムな待呼数(電話をかけて待っている人数)
  • リアルタイムな応答可能オペレーターの人数
  • 当日のコール数

などです。

ポイントとしてはリアルタイムであることで、待呼数に関しては電話がかかってきてから最低でも1~2秒以内のタイムラグで画面に反映させる必要があります。

システム構成

コールセンターの電話にはAmazonConnectを利用しています。 なので、AmazonConnectの各種イベント、データを使い、それらをダッシュボードへ連携するように設計を行いました。

設計のポイントとしては、

  • システム間を疎結合に保つために、Amazon EventBridgeを利用しイベントを介してシステム間の連携を行っています
  • クライアント側にリアルタイムでデータを表示させるために、AWS AppSyncを利用しGraphQLのSubscrptionによるクライアントへのデータのプッシュを行っています。

f:id:mkitagawa-312:20201216224311p:plain

1. データの取得

AmazonConnectからデータを取得するには主に3つの方法があります。

  • AmazonConnectの問い合わせフローにLambda呼び出しを設定することで、着信時に電話番号、通話のIDなどが取得できます
  • AmazonConnectAPIGetCurrentMetricDataから、現在の通話(キュー)の状態、オペレーターの状態が取得できます。今回はこれを1秒おきにポーリングして取得し、一度データベースにおいて差分の確認を行っています。
  • AmazonConnectにAmazonKinesisを設定することで、通話終了時に通話内容の詳細がKinesisの方に送られ、Transform用のLambdaを設定しておけばデータを抜き出すことができます

2. イベントの送信

取得したデータとイベントのタイプを設定してAmazon EventBridgeへイベントを送ります。 他システムはEventBridgeのイベントを購読する構成にすることで、システム間での密な連携を避けられます。また、連携するシステムが増えても購読側が増えるだけなので、配信側への変更は不要となります。

3. ダッシュボードシステムでの表示

  1. EventBridgeでイベントが送られると、購読しているLambdaが発火します。
  2. Lambdaの方で集計を行い、集計数をDynamoDBに更新します。
  3. DynamoDBにAppSyncが設定してあるため、DynamoDBの変更を検知し、GraphQLのエンドポイントに変更が送られます。
  4. クライアント側のReactアプリケーションがGraphQLのAPIを購読しているので、変更があれえばReactのコンポーネント側に変更がかかり画面に反映されます。

この部分の仕組みは一見実装が大変そうに見えますが、AWS Ampliftyを使うことで非常に簡単に構築することが可能です。

AWS Amplifyとは

AWS Amplify は、モバイルとウェブのフロントエンドデベロッパーが、安全でスケーラブルなフルスタックアプリケーションを構築しデプロイできるようにする、AWS による製品およびツールのセットです。Amplify を使用すれば、アプリケーションのバックエンドを数分で設定し、わずか数行のコードでそれをアプリケーションに接続できます。そして、3 ステップで静的なウェブアプリケーションをデプロイできます。AWS Amplify で迅速な市場投入を実現しましょう。

こちらがAWS Amplifyの説明ですが、一言でゆうとFirebaseのAWS版です。

  • CLIのコマンドからサーバーアプリケーションの雛形がつくれる
  • テーブルのスキーマを設定すればDBも作ってくれる
  • GraphQLサーバーも作れるし、クエリも作ってくれる
  • クライアントアプリケーション(Reactなど)の雛形も作ってくれる
  • デプロイコマンドを実行すればAWS環境に一発でデプロイまでやってくれる

ということで今回はAWS Amplifyを使うことでダッシュボード側のシステムはさっと作ることが出来ました。 プロトタイプを作る、小さいアプリケーションをシュッと作りたい時にはAWS Amplifyは良い手段だと感じたので今後も使っていこうと思います。

おわりに

トレタではエンジニアを積極的に募集しています!

自分が担当しているトレタCC事業でもエンジニアを募集していますので、気になった方はまずは気軽に遊びにきてください!

© Toreta, Inc.

Powered by Hatena Blog