トレタ開発者ブログ

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

なぜQAエンジニアが仕様を書いたのか

この記事はトレタ Advent Calendar 2019 21日目の記事です。

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

トレタに入社して1年が経ち、既存サービスのエンハンス開発やいくつかのリニューアルプロジェクトに携わってきました。プロダクト品質向上やメンバーへの貢献を模索するなかでQAエンジニアが仕様を書くという試みをテーマの1つとして行ってきましたので、今回はその話をしたいと思います。

なお、本記事では「プロダクトの外部的な振る舞いを定義したもの(UI仕様)」のことを「仕様」と呼びます。テストの仕様ではありませんのでご注意ください!

そもそもなぜ仕様を書くのか

仕様を書く目的を私なりにざっくり表現すると、以下のような感じになります。

  • リリース前:プロダクト設計の不明瞭さや矛盾を早期検知・解決するため
  • リリース後:プロダクトの振る舞いが本来どうなっている(べき)か確認するため

リリース前は開発フェーズ、リリース後は運用フェーズと言い換えると分かりやすいかもしれません。

仕様はメンテが大変、すぐ陳腐化する、などの理由でそもそも書かれないことも多いと思いますが、それはリリース前ではなく後者(リリース後)のドキュメント保守の不毛さが原因だと考えています。

最近ではソースコードやテストケースなどの最終成果物で仕様を分かりやすく表現し、中間的な仕様書の保守をなくしてしまおう、という考えも主流になっています。 リリースを経て最終成果物が揃っている段階であれば、個人的にはこの考え方に大賛成です。

ここで問題なのは、前者と後者が混同され、保守が大変だからという理由で前者(リリース前)の目的での仕様作成も行わない判断が下されがちなことです。

リリース前は、要件検討〜UI設計〜実装〜テストの全てのフェーズで設計を反復的に更新する必要が生じます。 また、後ろのフェーズで不明瞭さや矛盾が見つかれば、都度前のフェーズへ戻って設計を見直すことになります。

そのため開発フェーズにおいては、可能な限り早い段階から設計の問題点を解消しつつ、プロジェクト全体に及ぶ頻繁な設計変更に追随し、メンバー間で常に認識を揃えるための何か(つまり仕様)が必要です。

個人的な考えですが、開発フェーズでは「プロダクト設計の不明瞭さや矛盾を早期検知・解決する」ために仕様を書くのは意義があるし、むしろそれに特化させてしまって良いのではないか、と考えています。

なぜQAエンジニアが書くのか

通常、QAが仕様をレビューすることはあっても、書くことはあまりないかもしれません。 にも関わらず私が仕様を書こうと思ったのは、以下のように着手しやすい状況だった、というのが大きいです。

  • ゼロからではなく既存プロダクトの動作をベースに仕様を書き起こすことができた(リニューアルプロジェクトの場合)
  • プロジェクト参入時点で画面デザインや遷移図などのドキュメントが揃っていた
  • キャッチアップのための時間的余裕があった

実際に仕様を書いてみて、QAが仕様を書くのは自分のためにもチームのためにも利点が多いと実感しました。 具体的にいくつか挙げてみます。

仕様作成へのモチベーションがある

困ったことに、そもそも仕様(あるべき振る舞い)が分からないとQAはテストを設計できません。 そうした状況を日頃から経験しているQAは、テストに足りない情報を集めて仕様を整理する、という行動を元々取ってきており、それをプロジェクト早期から行っておくことへの強いモチベーションがあります。

早期に第三者視点で仕様をチェックすることで品質が上がる

個人的に一番重要と考えているのがこれです。 実はテストで見つかるバグのほとんどは実装の不手際ではなく、重要な要件に誰も気づいていなかったり、仕様に綻びがあったり、仕様の認識がずれていたことが原因だったりします。

ソフトウェアテストには「初期テスト」という原則があり、プロジェクト終盤のテストでバグを見つけるよりも序盤で設計の問題点を早く潰した方が何倍何十倍も少ないコストで対処できます。

QAは通常テストを行うことで品質をチェックしますが、むしろより重要なのは、どれだけ早期に仕様の不明瞭さや矛盾を沢山発見し解決に導いていけるかだと考えています。

その意味で、QAがテストに使える粒度でちゃんと仕様を書き、同時に仕様作成のベースとなるドキュメントを第三者視点でチェックする役割を持つ意義は大きいです。

実装フェーズやテストフェーズで本来のタスクに集中できる

仕様が書かれない場合、実装フェーズでデベロッパが細かい仕様を検討しながらコードを書いていくことになります。 その結果、デベロッパはシステムの内部構造やアルゴリズムの検討など本来のタスクに集中できなくなり、外部の振る舞いまで扱う必要が生まれ、複雑度は格段に上がってしまいます。

仕様策定作業を後工程に丸投げせず先に行っておくことで、各フェーズで本来のタスクに集中できるようになり、開発スピードも向上します。

デザインから開発への橋渡しができる

QAは元々テスト設計などで「ユーザに対して果たすべき機能」を「システムが行うべき振る舞い」として表現する能力を身につけています。

これはまさに仕様を書く際にも発揮される能力で、システム構造や内部処理を念頭に置きながらデザインで意図された機能や振る舞いを書いていくことで、必要な情報を開発に必要な粒度で漏れなく記載することができます。

デベロッパにちゃんとテストを書いてもらえる

期待する動作が仕様で整理されているとデベロッパがユニットテストや結合テストを書きやすくなり、実際に書いてもらえる、という利点もあります。

仕様を書いてみて学んだこと

最後に、QAエンジニアが仕様を書いてみて学んだことと反省点を整理します。

  • 良かった点
    • 仕様を細部まで書こうとすることで初めてデザインや仕様の不明瞭さや矛盾点を発見できることが多かった
    • 仕様を書いている途中で色々とテスト観点が浮かび、それをテストフェーズに活かせた
    • プロジェクトのスムーズな進行や品質向上に多少なり貢献でき、PMやデベロッパから感謝された
  • 反省点
    • 仕様を詳細に書いたつもりでも、仕様漏れによる手戻りがいくつか発生してしまった
    • QAのプロジェクト参画時期が、仕様を書くには少し遅いタイミングだった
    • プロジェクトメンバーに仕様を読んでもらう努力が足りず、仕様が認識されていないことがあった
    • プロジェクト完了まで仕様書をちゃんと更新し続ける意識と覚悟が必要

次にやりたいこと

テーマは変わりますが、E2EテストレベルでBDD(ATDD)を実践していきたいです!

一緒に働く仲間を探しています

トレタのQAエンジニアはお互いの自主性を尊重しつつ、プロダクト品質向上・生産性向上のために積極的に幅広い役割を担っていくことが可能です。

SET(Software Engineer in Test)も求めていますので、ご興味ある方はぜひ下記からご応募ください!

トレタCCにAmazonConnectを導入した話

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

こんにちは。リングフィットアドベンチャーを2週に1回のペースでプレイしているフロントエンドエンジニアの北川です。

私はトレタのコンタクトセンター事業(以下、トレタCC)のシステム開発全般を担っており、今年は基幹のCTI(電話機)をAmazonConnectへ入れ替えたので、その辺の話を本記事で紹介します。

トレタCCとは

トレタCCは、契約している飲食店の電話業務を代行するサービスです。

トレタって予約台帳なのになんでコンタクトセンターやってるの?と聞かれることがありますが、トレタは飲食店を支援することをミッションとしています。店舗にとっての電話業務とは、予約の電話に限らず当日の道案内や落し物の問い合わせなど、予約以外の多種多様な電話も時間問わずにかかってくるとても業務負荷の高いものです。トレタCCではそれらすべての電話を代行し、飲食店に電話がかかってこない環境をつくることで本来の業務にフォーカスしていただくためのサービスです。

トレタCCのシステムアーキテクチャ

トレタCCで使用するアプリケーションは、代行した店舗の電話をうけるCA(コールエージェント)が利用するシステムです。 トレタの予約台帳への予約の書き込みや、顧客台帳から電話口の顧客情報を表示したりします。また、前述の通り予約以外の電話もあるため、専用のDBヘ電話の対応履歴を登録したりします。

利用技術

  • AmazonConnect
  • Angular
  • Lambda(Node.js)
  • DynamoDB

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

クライアントはAngularで作られたWebアプリケーションになっており、AmazonConnectが提供しているCCP(ContactControlPanel)を埋め込み電話操作を行います。 サーバーサイドはAmazonConnectとの連携を考慮し、Lambdaを主軸にしたサーバーレス構成にしています。AngularへのAPIを提供や、AmazonConnectの問い合わせフロー(IVR)との連携を行います。DBはLambdaとの相性を考慮してDynamoDBを利用しています(先日LambdaにRDS Proxy機能が発表されたので、今ならRDBを使う選択肢もあると思います)。 また、serverlessフレームワークを利用しており、Lambdaのデプロイや、APIGatewayとDynamoDBをオフライン化した状態(serverless-offline, serverless-dynamodb-local)の開発環境を構築しています。

データ分析

AmazonConnectでは、履歴メトリクスとして通話履歴やエージェントの稼働状況の統計画面が標準で提供されています。データ出力も提供されており、トレタCCでの通話履歴とトレタ台帳の予約データを突合させるため、BigQueryでデータの一限管理を行なっています。

  • AmazonKinesis
  • GoogleBigQuery

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

AmazonConnectはAPIを提供していますが、通話履歴のAPIは提供されていないため、AmazonKinesisを設定することで、リアルタイムの通話履歴が取得できます。AmazonKinesisに設定されたLambdaが随時トリガーされ、渡される通話履歴データをBigQueryへ集約しています。また、音声ファイルは自動でS3へアップロードされ、通話履歴内にも音声ファイルのURLが含まれています。

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

AngularとAmazonConnectの連携

電話のインターフェースはAmazonConnectが提供しているCCPを使用しています。また、jsライブラリであるamazon-conect-streamsを使うことで、CCP上での受電などのイベント取得や発信を行うことが出来るようになります。

f:id:mkitagawa-312:20191216133054p:plain (AmazonConnectのCCP)

amazon-conect-streamsでは、エージェントのステータス変更イベントや通話のステータス変更イベントにコールバックが設定できるので、RxJsと組み合わてストリームにしてAngularで扱っています。

export class AmazonConnectAdapter {
  callStatus$ = new BehaviorSubject<CallStatus>(CallStatus.disconnect);
  agentStatus$ = new BehaviorSubject<AgentStatus>(AgentStatus.unavailable);
  availableQueues$ = new BehaviorSubject<Queue[]>([]);
  agentEndpoints$ = new BehaviorSubject<AgentEndpoint[]>([]);
  onInboundCallIncoming: EventEmitter<CallNumbers> = new EventEmitter();
  onInboundCallAccepted: EventEmitter<CallNumbers> = new EventEmitter();
  onOutboundCallConnected: EventEmitter<CallNumbers> = new EventEmitter();

  initialize(ctiElement: ElementRef) {
    this.initCCP(ctiElement);
    this.openLogin();

    connect.agent(agent => {
      this.onAgentCallback(agent);

      agent.onRoutable(() => {
        this.agentStatus$.next(AgentStatus.available);
      });

      agent.onOffline(() => {
        this.agentStatus$.next(AgentStatus.unavailable);
      });

      agent.onAfterCallWork(() => {
        this.agentStatus$.next(AgentStatus.unavailable);
      });
    });
// ...

CCP内で行える機能はほぼamazon-conect-streamsから可能になっているため、今後は自前のUIに切り替える予定です。 逆にCCPではできないこともあり、発信時には発信元が設定できなかったりします。

f:id:mkitagawa-312:20191216133113p:plain (発信用インターフェースの例)

LambdaとAmazonConnectの連携

AmazonConnectはIVR機能である問い合わせフローを管理画面から設定することができ、フロー内でLambda関数を呼び出し、取得した値に応じたフローを書くことが可能です。

f:id:mkitagawa-312:20191216133135p:plain たとえば、コールセンターの業務時間の設定は標準機能である「オペレーション時間」の管理画面から設定することができますが、飲食店では祝日や祝前日は特別なオペレーションを行うことがあるため、自前のオペレーション時間設定をLambda関数から呼び出すことで柔軟に対応を行なっています。

今後の取り組み

AmazonConnectへの切り替えが終わり、現在はAmazonConnectを利用した自動音声応答(VoiceChatBot)に力を入れています。 AmazonConnectの問い合わせフローでの標準機能として、

  • VideoStreamによるリアルタイムな音声データの抽出
  • Lambda呼び出しによる動的なフロー作成
  • AmazonPollyによる動的な文章読み上げ

が可能です。 これらを組み合わせることで、店舗にあった動的な自動応答が可能になります。 また、先日Amazon Transcribeの日本語対応が発表され、文字起こしがさらに容易に行えるようになりました。 テンプレ的な予約の受け答えはBot化し、人にしか対応できない部分はオペレーターが対応するハイブリッドな形を目指していこうと思っています。

おわりに

トレタCCでは、今までにない新しいコンタクトセンターを作っていく仲間を募集しています。 フロントエンドでAngularをゴリゴリやりたい方、Node.jsでサーバーレスをやりたい方、通話内容を分析して機械学習をやりたい方、などなど幅広く募集しております!

www.wantedly.com

お次はインフラエンジニアのなぎらさんです。お楽しみに!

トレタに入社して新鮮に感じていること

この記事はトレタ Advent Calendar 2019 18日目の記事です。

こんにちは。2019年10月に入社したフロントエンドエンジニアの@punipunityanです。好きな動物は犬です!犬の話になると急にテンションがあがります!!特に柴犬が大好きです!!!

入社して3ヶ月弱なので、今日はトレタに入社して新鮮に感じていることを書いてみようと思います。

トレタに入社して新鮮に感じていること

Respect ALL

トレタの VALUE の1つに「Respect ALL」があります。

私は転職する上で、「お互い尊重することを大切にしている環境」で働きたいという理想を持っていました。そしてトレタに入社してみると、この Respect ALL という VALUE が実際に浸透していると感じました。

例えば、自分の担当の仕事をしたとき、自分では当たり前な些細なことをしたという感覚のことでも、すごく「承認してくれている」と感じるような言葉をかけていただくことがありました。そして、それを当たり前のようにチームの皆さんがされていました。

正直、最初はびっくりして少し照れ臭さがあったし、戸惑いました。しかし、やはり一緒に働いている方に喜んでもらえることは嬉しいですし、入社したばかりで緊張している状況の中でも、安心感を得られました。

素直に、惜しみなく感謝の言葉を伝えたり賞賛したりする文化は、本当に素晴らしいと思います。自分も積極的に言葉にしていきたいと思っています。

丁寧なレビューコメント

開発をしていて驚いたことの一つに、PRに対してかなり丁寧なレビューコメントを書いてくださることがありました。その手厚いレビューから日々学ばせていただいています。

自分の発想になかったような指摘をいただいた時は素直に面白いな、なるほどこういう捉え方があるのかと感じますし、疑問に思ったことに関しては腑に落ちるまで丁寧にコミュニケーションをとってくださいます。

その過程で自分自身の引き出しを少しずつ増やしたり、発想を少しずつ柔軟にしていったりする感覚があり、ワクワクしながらレビューコメントを読んでいます。すごく丁寧なコメントを書いてくださっている労力を考えると、感謝しかありません。

色々と指摘を受けるという意味では自分が未熟な点があるということでもあり、反省も多いですが、そこをネガティブに捉えるのではなく、感謝の気持ちと、指摘いただいたことを自分の血肉にして恩返ししていこうというポジティブな気持ちで取り組んでいます。

また、会社によって文化が違ったりするので、「トレタではこういうやり方だよ」という文化を学んでいるフェーズでもあります。

こうした過程で得たものはしっかり吸収して、早く同じ目線に立ち、自分もどんどん価値を生み出していけるようになりたいと思っています。

DDDを実践している

私はこれまでDDDを実践してきたことがなく、数年前に増田さんの「現場で役立つシステム設計の原則-変更を楽で安全にするオブジェクト指向の実践技法」を読んだことがあるくらいの状態です。その時はそれまでやってきた開発スタイルとのギャップから、実際に実践していくイメージがあまりわいていませんでした。

トレタに入社して感じたことは、あらゆる役割の方々がDDDを重要だと捉え、DDDを実践することに積極的だということです。社内ではDDDをテーマとして勉強会が開催されたりしていますし、ユビキタス言語を明確にしようと、気づいたものから一つ一つ議論して決めていくという過程をしっかり踏んでおり、『あ、DDDって実践できるんだ。こうやって実践していくんだな』と思いました。

自分自身、DDDの実践に取り組んでいるところですが、まだキャッチアップ中の状態です。必要性はしっかり理解していると思っていますので、プロダクトに関わるあらゆる方々の幸せのためにしっかり身につけて実践していきたいです。

フロントエンド定例とテックトーク

トレタではフロントエンド定例というのが週一回行われており、そこではそれぞれの進捗報告、課題があればその相談をしています。また、進行中の業務と直接関係あるなしに関わらず、最近気になることがあればその話のシェアなどを行なっています。

最近は毎週成長ポイントの報告をするようになったりして、日々学んだことを改めて言葉にして振り返るようになりました。そこから新たに他の方からコメントをいただけたりもするので学びの多い、私の好きな時間です。

それとは別に、最近社内で始まったテックトークという時間があり、エンジニアが集まって毎回異なった話を持ち回りで行っています。普段自分がやっていることとまた違った学びがあり、楽しみな時間です。また、仕事で直接関わっていないエンジニアの方々が日頃何を考えているのか、どういうことをされているのかというのを知ることができる点でも面白いです。

部活が盛ん

トレタは部活が盛んなようです。 日本酒部、映画部、フットサル部、釣り部など様々な部活があります。

私は元々運動不足で運動したいなと思っていたこともあり、筋肉部に入部しました。筋肉部は毎週火曜日の9:30〜10:00に筋トレの時間があって、詳しい方に教えていただきながら筋トレしています。(と言ってもまだ2回しか参加できていないのですが。。)

体を動かすのは楽しいですし、部活を通して仕事では普段関わらない方ともコミュニケーションが取れるので、そういった機会があるのはありがたいなと思っています。

電車に乗って美味しいものを食べにいく

これまでは同僚の方と飲みに行く時、会社から徒歩圏内のお店にいくことが当たり前でした。

トレタに入社してからは、会社終わりに電車やタクシーで移動して美味しいものを食べにいく機会が増えました。トレタはやはり食へのこだわりが強い方が多いということを感じています。

おわりに

以上、入社3ヶ月目のフロントエンドエンジニアがトレタに入って新鮮に感じていることでした。

私は今、人や環境に恵まれて、ここにいられるのは本当に幸運だなと思っています。これからしっかり貢献していくべく、毎日自分をアップデートする意識で目の前の役割に取り組んでいます。

トレタに興味を持った方は、ぜひ遊びに来てください!

© Toreta, Inc.

Powered by Hatena Blog