1枚のラベルの向こうには、1人のユーザがいる【連載:クックパッドマート開発の裏側 vol.3】

こんにちは。クックパッドマート連載3日目を担当します、買物事業部エンジニアの今井(@imashin_)です。

去年の10月ごろから、生鮮食品ECサービスクックパッドマートの販売者向けサービスの開発を行っています。クックパッドマートを利用するのは、商品を買うユーザだけではありません。商品を販売する方々にも簡単に利用できるよう開発を進めています。

今回は、どのようにして商品を販売者からユーザまで届けられるように開発しているかを紹介します。

クックパッドマートではどうやって商品をユーザに届けているのか

まず、今どのように商品を届けているのか、商品の注文から受け取りまでの流れを紹介します。

発注

f:id:ima_shin:20190410170609p:plain

販売者は、四六時中クックパッドマートだけを利用しているわけではありません。これまで通りの生産、販売業務が忙しい中で、クックパッドマートも利用していただいています。

そのため、販売者に合わせた方法を開発し、負担にならないようにしています。

みなさんの近くにある精肉店、青果店を思い出してもらうとイメージが湧くかもしれないのですが、販売者はFAXや電話で注文を受けていることが多いです。必ずしもIT、インターネットに慣れているわけではありません。そのためクックパッドマートは、毎日FAXでの発注書の自動送信を行っています(FAX送信にはTwilioを利用しています)。一方でスマホから見たいという要望の販売者向けに、LINE WORKS経由でも発注書をPDFにて送付しています。利用者の多いLINEと同じUIを提供しているLINE WORKSを利用することで、利用障壁を大きく下げることができています。

仕分

販売者は発注で受けた商品の発送準備を行います。この準備段階で、ユーザが受け取り時に目印とするラベルの貼り付けを行います。

  • 07:00 商品に貼るラベルを遠隔で自動印刷する
  • 07:00-14:00 販売者が注文を受けた商品にラベルを貼る。 商品をコンテナごとに仕分けする

f:id:ima_shin:20190410170612p:plain

商品ラベルについても、完全に操作不要で発行できる構成で設置し、必要な時に必要なラベルを発行しています。また、商品へのラベル貼り間違いが発生せず仕分けが素早くできるよう、コンテナ別、商品別でラベルが発行されるようにソートしています。こうすることで、負担をかけないように工夫しています。

配送

  • 11:00-14:00 配送員がコンテナを受け取りにくる
  • 14:00-17:00 受け取り場所にコンテナを配送する

f:id:ima_shin:20190410170536p:plain

配送員は指定のコンテナを受け取り、冷蔵状態を保ちながら商品を集荷し、受け取り場所まで配送します。

受け取り

  • 17:00- ユーザが受け取り場所にて、自分の注文した商品をピックアップする

ユーザには配送が完了すると通知が送られます。受け取り場所に行き自分が注文した商品のIDを確認し、コンテナからピックアップしていきます。

どのようにして今の配送を作ったのか

クックパッドマートはまだまだ完成していないサービスです。今の配送フローがベストだとは考えていません。これからも日々、改良を続けていきます。

ですが、リリース当初の状態からはかなり改良されています。今回はどのように改良、開発を行っているかを商品の受け取りに必要なラベルの発行にフォーカスして紹介していきたいと思います。

クックパッドマートでは、基本的に

  • 初めから実装せず、頑張る運用からやってみる
  • 頑張る運用の知見を元に、プロトタイプを試験運用する
  • 利用者に当てたプロトタイプの知見を元に、スケール可能なプロダクトを作る

の段階を踏んでサービス開発を行っています。(ex サービスリリース初期の話

今回は商品ラベルの発行にフォーカスして、実際に行った開発をお伝えしたいと思います。

頑張る運用をやる

初期の段階ではコストをかけてでも(後々自動化可能な)配送を行えるのか検証を行いました。商品ラベルの発行は人の頑張りで次のような運用をしていました。

  • 注文の締めとともに社内に設置されている複合機でラベルを印刷する
  • 配送業者にラベルを配送してもらう

f:id:ima_shin:20190410170808p:plain
複合機でのラベル印刷

検証結果としては、商品へのラベル貼りを販売者が問題なく行えることを確認できました。加えて、ラベルに印字したIDを元にユーザが自分の受け取るべき商品をピックアップできることも確認できました。

プロトタイプを販売者にあてる

ラベルを毎日郵送するにはコストが莫大にかさみますし、スケールさせることも困難になります。そこで次の段階として、販売者にラベルを印刷してもらう方向でプロトタイプを作成しました。

安価に、素早く開発できることを基準に技術選定を行い、iPadとiOS用のSDKを提供しているラベルプリンターを採用しました。

  • ラベル発行用iPadアプリを開発し、ラベルプリンターにて印刷できるようにする
  • プロトタイプを店舗に設置し、試験運用してみる
    • ただし、問題発生時にはバイク便にてすぐラベルを届けられるようにバックアップを用意

f:id:ima_shin:20190410171717j:plain

f:id:ima_shin:20190410171833p:plain
ラベルの印刷フロー

このラベル発行アプリとiPadとラベルプリンターを販売者に提供することで、ラベルの配達をなくすことに成功しました。しかしながら、多くの問題点も浮き彫りとなりました。

  • ラベルプリンターの紙詰まりによる故障
    • 耐久性に特化したラベル発行機でないと長期の運用は保守が大変だった
  • 操作可能な画面は不要
    • 導入当初は、発注内容の確認や商品の情報入力をiPadからできるのではと思われたが、実際には設置場所の狭さや操作する余裕がないことがわかった
  • 通信環境の不良
    • iPadが安定してIPアドレスを払い出せない
    • iPadとプリンターの接続状態を安定させることが難しかった
  • OS、アプリの管理
    • 販売者の操作なしにOS、アプリを常に最新状態に保たせる仕組み、運用を作ることが難しかった
  • 販売者ごとのITリテラシーの差異
    • 必ずしも全ての販売者がiPadやプリンターの操作に慣れているわけではなかった

このように実際にプロトタイプで試験運用した結果、多くの問題点を洗い出すことができました。ラベルは商品を販売者からユーザに届けるために必要不可欠なものです。毎日必ず発行できる安定性を実現させる必要があります。

スケールできるプロダクトを作る

安定してサービスを運営するためには、ラベル発行に高い安定性が必要だということを認識することができました。また広くスケールをさせるためには、誰でも簡単に設置、管理できる必要があります。そこで、以下のような要件を元にスケール可能なプロダクトの開発を行いました。

  • 安定してラベルを発行できる構成と設計
    • 完全に遠隔でラベル発行をコントロールできる
    • ラベル発行が可能か把握するための死活を監視する
    • ラベル切れ等によるラベル発行不可能状態になる事前に検知する
  • 簡単に導入、運用できる設計
    • 電源を刺すだけ利用できる
    • 複雑な操作なしに運用できる

以上を満たすように開発を行い、つい2週間ほど前に新たな構成でプロダクトをリリースしました。

今の状態

では、今の構成がどのようなものかを紹介しようと思います。

ハードウェア

安定した稼働を実現するために、以下の機器でラベル発行機を組みました。

  • LTE ルーター UD-LT1/EX + SORACOM Air
    • 定期リブート
    • ネットワーク断絶時のリブート
    • Syslog
    • 外部ネットワークからの設定変更
    • SNMPによる状態監視
  • TSP743IIE3-24J1 JP
    • 通電すると常にONの状態に固定可能
    • 紙詰まりしにくい
    • ネットワーク経由でコントロール可能
    • カバーが開いている、紙が詰まっている、ラベルが切れかかっている等の状態を取得可能
    • SNMPによる状態監視
  • Raspberry Pi Model B+
    • デバッグ、キッティング、監視用

f:id:ima_shin:20190410171946p:plain
ラベル発行の構成

各機器の安定性、死活監視を利用することで、ラベル発行を安定して行うことができるようになりました。Raspberry PiでLTE通信を行うこともアイデアとしては挙がっていましたが、リリース速度を重視し、一旦既存のルータ製品を採用することにしました。

ソフトウェア

以上のハードウェアを稼働させるために、主に3つの開発を行いました。

star_ethernet

スター精密製プリンターを制御するiOSやAndroidのSDKは提供されていたのですが、サーバから直接利用するケースが少ないのか、Rubyはサポートされていませんでした。しかし、ソケット通信によるプリンターのプロトコルについて、細かな仕様が提供されていたため、Rubyからスター精密製プリンターを制御するgemを作成しました。

基本的にはTCPソケットで制御コマンドを送信し、プリンターを制御します。公開されている全てのコマンドをラップし、ラベル発行に必要なハンドリングを可能にしています。

例えば、文字を大きくしたりレイアウトを変えたりする、QRコードを印字する、線を引くといった印字内容の操作もこれを用いて行います。ラベル台紙のカットやラベル送り、ビープ音を出すこともできます。プリンターの細かな状態を取得することもできます。

f:id:ima_shin:20190410172040p:plain
https://www.starmicronics.com/Support/Mannualfolder/UsersManual_IFBD_HE0708BE07_EN.pdf

mart_server

プリンターへのラベル発行命令はECSから送信します。

mart_server(クックパッドマート全体を支えるRailsアプリケーション)に発行すべきラベルの情報を集約し、日次バッチにて発行するラベル情報をstar_ethernetを利用してプリンターに送信します。

バッチにはkuroko2を利用し、barbequeで各プリンターへのラベル発行ジョブの管理しています。何かしらのトラブルでラベル発行に失敗した時は、原因を調査しジョブを再実行することで全てのプリンターで確実にラベルが発行されるようにしています。またラベル残量が少なくなっていたり、紙詰まりの発生を検知しています。

mart_shepherd

配布端末、ネットワークの管理を新たなのアプリケーションとしてmart_shepherdに切り出しました。

mart_shepherdはSORACOMプラットフォームとの間に立ち、mart_serverからgRPC経由のリクエストに応じて端末の管理を行います。また、ルーター、プリンター、ラズパイ各端末との通信時にはプロクシを行い、通信路を確立します。

アセンブル

実際にこれらの構成を設置するためには、機器を一つの什器にまとめてコンパクトにする必要があります。また、電源を刺すだけで簡単に運用を開始できるようにすることを目指しました。

そこで、一つのボックスに全ての機器を収納し、プラグを刺すだけで全ての機器の電源がONになり、即座に運用状態になるようにしました。

弊社には、ハードウェアを加工できる「工房」と呼ばれるスペースが存在し、そこで全ての加工、組立を行いました。

f:id:ima_shin:20190410172304j:plain
加工中の様子

f:id:ima_shin:20190410172230j:plain
加工済みのボックス

f:id:ima_shin:20190410172532p:plain
アセンブル済のボックスとプリンター

改善点

以上のように、安定して稼働するプロダクトを完成させることができました。2週間運用している限りでは、何かトラブルが発生しても遠隔で復旧することに成功していて、ラベルの発行ができない問題にぶつかったことはありません。

しかし、まだ改善点は残っています。

  • 低コスト化、小型化
  • 輸送可能な構成、耐衝撃性
  • 熱制御

これらを実現するために、引き続き開発を行っています。

まとめ

たかが商品のラベル1枚と思いがちですが、ラベルが発行されないとユーザーに正確に商品を届けることができません。1枚のラベル発行に失敗すると、1人のユーザが料理を作れない状態に陥ってしまいます。

そのようなことが決して起きないよう、たかがラベルの発行であっても真剣に開発に取り組んでいます。

これからもクックパッドマートは、素早いサイクルでの開発の元、安定したサービスの提供と、スケールを実現していきます。

この記事を通して、クックパッドマートのサービス開発にご興味を持っていただけた方がいらっしゃいましたら、ぜひ一緒にサービスを作りましょう!

www.wantedly.com

お知らせ

クックパッドマートでは、4/24(木)に、買物事業部のエンジニアによる発表とエンジニアとのミートアップを開催予定です。

cookpad.connpass.com

今回の記事のような生鮮ECそのものの仕組みや、流通の仕組みの開発に興味がある方、クックパッドマートのエンジニアと直接話してみたい方はぜひご応募ください!お待ちしています!

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/