株式会社ZOZOのブログ - TECH PLAY

TECH PLAY

株式会社ZOZO

株式会社ZOZO の技術ブログ

1025

みなさんこんにちは、今村( @kyuns )です。今回は弊社の新規サービス開発にて、Hashicorp製品を中心にインフラ周りを整えたお話をしていきたいと思います。今回はTerraformとAtlasの話が中心になります。 今回実現したこと TerraformでAWS上のリソースをコードで管理 GithubでPullRequestを作ってインフラに対する変更をコードベースでレビュー Github上でPullRequestに対して変更がテストされ、テスト結果が貼られる Pull Requestをマージすると自動的にAtlas経由でterraformが実行されてインフラの変更が適用される いわゆるインフラのコード化&自動化です。 導入によるメリット インフラがコードで管理されることにより属人性を排除することができる インフラの変更に対して事前にレビューすることにより事故を減らせる 変更の適用はPullRequestのmergeボタンを1つ押すだけなので格段にデプロイの手間が省けて安全(etc) 今回は0からのインフラ構築ということもあり、HashicorpのTerraformとAtlasを利用して上記を実現してみることにしました。それでは順を追って説明していきたいと思います。 Terraformって何? Terraform とはHashicorpが提供している Infrastructure as Code を体現するためのツールです。今回は新規サービス開発ということもあり、ちょうど0から作るいいタイミングであった為、採用に踏み切りました。TerraformはAWSのほとんどの設定に対応しているため、コードベースでインスタンスをたてたり、ELBやS3の設定などのAWSのリソースを管理することができます。 Terraformの導入 インストール ダウンロード ダウンロードページ から各プラットフォーム別のバイナリを取得します。プログラム自体はGoのバイナリなので好きなフォルダに展開後、PATHを通しておきましょう。 また、ruby gem経由でもインストールすることができます。 $gem install terraform $terraform -version Terraform v0.6.7 現時点(2015.11.27)での最新版は0.6.7です。 AWSクレデンシャルの設定 AWSのログインクレデンシャルを環境変数に設定しましょう。 bash/zshならexport、fish shellならsetenvを利用します。 export AWS_ACCESS_KEY_ID=XXXXX export AWS_SECRET_ACCESS_KEY=XXXXX export AWS_DEFAULT_REGION=ap-northeast-1 setenv AWS_ACCESS_KEY_ID XXXXX setenv AWS_SECRET_ACCESS_KEY XXXXX setenv AWS_DEFAULT_REGION ap-northeast-1 以後、terraformコマンドを実行する際にこのアカウントの設定が利用されます。 Terraformを理解する Terraformの構成 Terraformを理解するのに重要なファイルが2つ存在します。 .tf ファイルと .tfstate ファイルです。 .tfファイル terraformの設定を記述するファイルです。 .tfstateファイル terraformコマンドを実行した後、現在のインフラの状態が記述されたファイルです。tfstateファイルはterraformを実行すると自動的に生成されるので通常は触る必要はありません。 また、terraformコマンドを実行すると自動的に tfstate.backup というバックアップファイルも生成されます。 Terraformを使ってEC2インスタンスを立ててみる tfファイルの記述 Terraformを使って試しにインスタンスを立ててみます。 新しくインスタンスを立てる場合、例えばec2の設定は以下の様な感じになります。 ec2.tf resource "aws_instance" "web01" { ami = "ami-6927d769" availability_zone = "ap-northeast-1c" ebs_optimized = false instance_type = "t2.medium" monitoring = false key_name = "iqon" subnet_id = "subnet-4ee15817" vpc_security_group_ids = [ "sg-2f765a4a" , "sg-55755930" ] associate_public_ip_address = true private_ip = "10.0.0.10" source_dest_check = true root_block_device { volume_type = "gp2" volume_size = 20 delete_on_termination = true } tags { "Name" = "web01" } } 非常に直感的ですね。設定を記述できたらterraformコマンドを実行してみますが、その前に設定を確認してみましょう。 設定を確認してみる(Dry run) terraformにはDry runの機能があります。 予め適用する内容を出力してくれるので、事前にどのような変更が起きるかを確認することができます。 $terraform plan 気をつけなければならないのですが、完璧なDry runというわけではなくplanでうまくいっても実際に実行すると失敗するということもありますので、あくまでも目安程度に捉えておいたほうが良いと思います。 コマンドを実行する 先ほど作ったec2.tfファイルを実行するためにはapplyコマンドを使います。 $terraform apply これでしばらくするとEC2インスタンスが作成されます。 ec2のほかにもS3やRDS、ELBなどあらゆるAWSサービスに対応しています。 既存のAWSインフラの設定からtfファイルを生成する 完全に0からインフラを構築する機会はそんなにないと思うので、殆どの人は既存のインフラに少しずつ適用して試してみたいと思うのではないでしょうか。また既存のAWSの設定からtfファイルを生成したい場合はどうしたらいいでしょうか?公式ではそのようなexportの機能はサポートされていないので3rdParty製のツールを使います。 terraforming という便利なgemがあるので今回はこちらを使ってみます。 gem install terraforming 試しに現在のs3の設定を取得してみます。 $terraforming s3 resource "aws_s3_bucket" "iqonsample" { bucket = "iqonsample" acl = "private" } resource "aws_s3_bucket" "iqonsample.test" { bucket = "iqonsample.test" acl = "private" } このように $terraforming サービス名 を指定すると、自動的に現在のAWSのリソース設定からtfファイルの形式で出力してくれます。また、同時に tfstate ファイル形式を出力する機能も備えています。tfstate形式の場合はオプションで --tfstte を指定します。 $ terraforming s3 --tfstate { "version" : 1, "serial" : 1, "modules" : [ { "path" : [ "root" ] , "outputs" : { } , "resources" : { "aws_s3_bucket.iqonapi" : { "type" : "aws_s3_bucket" , "primary" : { "id" : "iqonapi" , "attributes" : { "acl" : "private" , "bucket" : "iqonapi" , "force_destroy" : "false" , "id" : "iqonapi" , "policy" : "" } } } , "aws_s3_bucket.iqonapi-test" : { "type" : "aws_s3_bucket" , "primary" : { "id" : "iqonapi-test" , "attributes" : { "acl" : "private" , "bucket" : "iqonapi-test" , "force_destroy" : "false" , "id" : "iqonapi-test" , "policy" : "" } } } } } ] } 現在のAWSの状態からtfstateファイルの出力までしてくれます。 こちらを既存のtfstateファイルにmergeする場合は --merge オプションを利用してマージすることもできます。 AtlasとGithubでインフラを自動化する Terraformで設定を記述した後は、次にAtlasとGithubを利用してインフラを自動化します。 ATLAS はHashicorpが提供するクラウド型の統合プラットフォームであり、Vagrant,Packer,TerraformやConsulなどの機能を内包しています。ここではATLASとGithubを連携させてGithub上でPRを出した時点でterraform planの実行結果がPRに貼られ、mergeすると自動的にATLAS経由でterraform applyが実行されるのを目指します。 Atlasの設定 まずはAtlasにアカウントを作成します。 次にCreate and manage infrastructure with Terraformをクリックして設定に進みます。 画面に表示されるATLAS_TOKENを環境変数に設定します。 export ATLAS_TOKEN=XXXXXXXXXXX TerraformプロジェクトをAtlasにpushする 次にローカルにあるterraformプロジェクトをAtlasにプッシュします。(チュートリアルに従うとexampleになる) $ terraform remote config -backend-config "name=vasily/example" Remote configuration updated Remote state configured and pulled. Configuration "vasily/example" uploaded! (v1) 設定がAtlasにプッシュされました。 この仕組みは、terraformのtfstateファイルを リモートで管理する機能/terraform remote を利用したものです。 注意点ですが、この段階でローカルにあるtfstateファイルは削除されます。(Atlas上で管理されます) 自動デプロイ用の設定をする Auto applyの設定 Github上でデフォルトブランチにmergeされた際に、terraform applyを自動実行するかどうかの設定ができます。ここでは自動化をするのでAuto applyにチェックをいれます。 Githubとの連携設定 次にgithubのイベントをフックできるようにAtlasの管理画面のサイドバーにあるIntegrationにてGithubの設定をします。 レポジトリのルートディレクトリにterraformの管理ファイルがない場合は、 サブディレクトリを指定することもできます。 環境変数の設定 次に管理画面のVariablesにて環境変数を設定します。 access_key,とsecret_keyを指定します。 GithubでPRを作成してみる Atlas側で設定ができたらGithubに実際にPRを作成してみます。 PRのTESTの部分にTerraformでのterraform planの実行結果が表示されます。 PRをマージしてterraform applyが実行されるのを確認する。 Github側でPRをマージするとあとは自動的にAtlas側で terraform applyが実行されます。もちろん管理画面からでも実行結果が確認できます。 これでGithubでPR作成->mergeからAWSへの反映まで自動化することができました。 terraform applyに失敗した場合 このようにterraform planが成功してもapplyが失敗する可能性があるので、手動でapplyする場合はコンソールからQueue Planを押すと再度実行されます。 まとめ おすすめの導入方法 いきなりインフラ全てをTerraformで管理する、というのは0からインフラを作る時以外はあまりオススメできません。 TerraformはほとんどのAWSリソースに対応しています、よって全てをコードで管理できるわけではありますが、全てをTerraformで管理しようとすると個人的には結構事故の原因になりかねないなぁと運用してみて感じています。 例えばVPCのサブネットやインターネットゲートウェイの設定などの変更頻度のかなり低いものは場合に応じてTerraformで管理しない、という使い方もありだとは思います。 すでにAWSのインフラがある人は、まずは前述のterraformingを用いて既存のEC2インスタンスの設定などからtfファイルをつくり、同じ設定でインスタンスをたてたりするところから始めて見ると導入しやすいかなと思います。 また、Terraformへの完全移行の際には、tfstateファイルとの差分を排除するために、AWSのコンソールからポチポチというのは無くすというルールをつくって徹底する必要があります。(IAMとかをきちんと管理する) 導入後の感想 Terraformによって運用に必要な部分のインフラがコード化され、インフラの変更に対してもPRで確認してから適用できるようなったことで格段に属人性を排除することができました。また、変更の適用はGithubでPull Requestをmergeするだけで自動的にTerraformが実行されるようになり、安全性も確保することができました。 今日は紹介しませんでしたが、今後は弊社でのPackerやConsulまわりの取り組みもご紹介できればと思います。 VASILYではHashicorp製品をつかってインフラで新しいチャレンジをしてみたいエンジニアを募集しています。 ぜひとも一緒に取り組みましょう。興味があるインフラエンジニアはぜひ こちら から応募してみてください。
先日、弊社がAndroid版iQONの開発に使用しているサービスとツールについて紹介させていただきました。 Android版iQONの開発で利用しているサービス&ツールを紹介します その中でアプリの動作確認にSORACOM Airを導入したとご紹介しましたが、今回 SORACOM ユーザーグループ発足記念リレーブログ の11/19日分として、もう少し掘り下げてご紹介しようと思います。 SORACOM Airを導入しようと思ったきっかけ VASILYでは、 Qiita:Team を導入しており、職種を問わず全員がDaily Reportと呼んでいる弊社テンプレートでの日報(以下、DR)を書いています。詳細は こちら をご覧ください。DRには、テンプレで困ったことや思ったことを書く欄があるのですが、ある社員がDRで以下のように書いていました。 http://ramenandicon.hatenablog.com/entry/2015/10/30/091352 本文とは趣旨が違うのですが、冒頭に「月末と言えば7GB超過の通信速度制限。」とあり、IT系企業従事者以外にとっては、自宅にWifiがなかったり、通信速度制限が当たり前の文化としてあるのかもしれない=通信速度をもっとシビアに意識したほうがいい、と感じました 10月はあえて自宅のWifiを切って生活してみたのですが、そうなると月の半分ほどで通信速度制限がかかり、その状態だと使うアプリもシビアに選ぶようになったので、実感をもてた気がします(意識的には乗換えや検索系は必須で使うので再優先で残したいと思い、それ以外の通信が発生するアプリは立ち上げなくなりました) ちなみに速度制限がかかった状態の4GでiQONを立ち上げると、ホーム画面が開くまで平均して3秒〜5秒ほど待ちます、自宅にいるときに使うとそこまで感じないですが、電車などで暇つぶしに立ち上げようとしたときには「かなり遅いな」という印象です このDRを読み実際に調べてみたところ、iQONユーザーの中にも通信速度の低下で快適にサービスを利用できないユーザーが一定数以上存在することがわかり、帯域制限によって通信速度が低下した場合のアプリの挙動について調査・改善することになりました。 しかし、帯域制限を受けた状態は再現しにくく、Facebookの ATC のようなOSSはありますが、より簡単に帯域制限を受けた状態を再現できる方法を探しました。 その結果注目したのがSORACOM Airです。SORACOM Airの開発者が通信速度を自由に変更できるという特徴を利用して帯域制限を受けた状態を再現し、アプリの動作確認を行う事にしました。 導入してみた結果 SORACOM Airを導入することで通信速度を制限した動作確認が簡単に行えるようになりました。ユーザーコンソールからSIM単位で簡単に速度の変更を行え、非常に便利です。実際に速度クラスをs1.slowに設定してアプリを起動してみた結果は以下の通りです。s1.fastを指定した時と比較して、ホーム画面を表示するまでの時間が平均で3倍程度かかることがわかりました。また、画像の表示完了にはより長い時間がかかることがわかります。 以下はs1.fastに設定してアプリを起動した結果です。 SORACOM Airの速度クラスをHubotから変更 また、弊社ではSORACOM Airの速度クラスをSlackからHubot経由で変更できるようにもしています。 Hubotのコードは以下の通りです。SORACOM API Clientには soracom を使用しました。AWS Lambdaを経由して通信量が一定を超えたらSlackに通知する等もやりたいと考えています。 まとめ SORACOM Airは開発者がSIMの操作を簡単に行えるので非常に便利です。また、APIドキュメントもわかりやすく、簡単に利用することができました。今後、SORACOM Airを使用し、通信速度が遅い場合でも快適に利用できるようサービスを改善していきたいと思います。 最後に VASILYでは、新しい技術が大好きなエンジニア&学生インターンを募集しています。少しでもご興味のある方は是非 こちら からご応募よろしくお願いいたします。
こんにちはVASILYエンジニアの松本です。先日 MERY を運営する株式会社ペロリと合同で Fashion Tech meetup #1 と題した勉強会を開催しました。   当日は約100名のエンジニアの方々に集まっていただき、Fashion × Technologyを題材として各社のエンジニアが、お互いのサービスを支える技術について発表しました。今回はFashion Tech meetup #1でのVASILYの発表資料をご紹介します。 iQONを支えるクローラーの裏側 iQONでは提携先ECサイトからアイテム情報をクロールしています。VASILYでは2015年の夏にクローラーの仕組みを大幅に変更することによって、1ヶ月間で400サイト分のクローラーを製作することができるようになりました。エンジニア以外のメンバーでもクローラーを作ることができるようにしたそのシステムの裏側を紹介。 iQONを支えるクローラーの裏側 from Takehiro Shiozaki トピックモデルを用いた 潜在ファッション嗜好の推定 自然言語処理の潜在意味解析の分野で主に文章解析に用いられるトピックモデルを用いて、ユーザーの行動データからファッションの嗜好を分析。分析結果からユーザーのペルソナを定義するところに至るまでを紹介。 トピックモデルを用いた 潜在ファッション嗜好の推定 from Takashi Kaneda OpenCVを使ったiQONの画像処理の全容 iQONでクロールしたアイテム画像がコーディネートに使われるまでの画像処理の全容を公開。モデル画像、トルソー画像の機械学習を用いた自動判定。さらにECサイトの画像から物体領域を抽出し背景の透過処理を行うまでの手法を公開。 OpenCVを使ったiQONの画像処理の全容 from Kazuki Matsumoto iQON ADのfluentd, mackerelを使ったモニタリング iQONの広告配信システムiQON ADのモニタリングシステムを公開。fluentdを用いたログ集計方法とmackerelでの監視方法を紹介しています。 Google Cloud Test Labによるテスト自動化 今年のGoogle I/Oで発表されたクラウドベースのAndroidアプリのテスト基盤である、 Google Cloud Test Lab について発表。具体的にどのようなテストを実行可能なのかや、Web UIやAndroid Studio、CLIからのテストのスケジューリング、テスト結果の解析、そして弊社で実際に使用しての感想について紹介しました。 最後に 今回の勉強会では懇親会とLTセッションまで多くの方々に参加していただき、大盛況に第一回を終えることができました。これからもFashion Tech meetupは定期的に開催し、ファッション領域に取り組むベンチャー企業を盛り上げていこうと思っています。今回の勉強会に参加できなかった方々も是非次回ご参加ください。 また、今回の勉強会の会場と懇親会での軽食・飲み物はすべて株式会社ディー・エヌ・エーにご提供いただきました。本当にありがとうございました。 VASILYではFashion × Technologyに興味のあるエンジニアの方々をお待ちしています。一緒に最新のテクノロジーでファッション業界を盛り上げていきましょう。 VASILYのエンジニア募集要項はこちら
こんにちはVASILYエンジニアの松本です。VASILYではクローラーの仕組みを大幅に見直した際にDynamoDBの導入を行いました。今回はその導入方法とDynamic DynamoDBを用いた運用方法について話したいと思います。   DynamoDBを導入した理由 iQONではクローラーで取得したデータをDynamoDBに保存しています。DynamoDBを導入した理由は以下の通りです。 ・ ECサイトごと、さらには商品ごとにクロールするデータの形式が異なるためスキーマレスである必要があったこと。 ・ DynamoDBはデータベース容量が増大した際も自動でスケールしてくれるのでメンテナンスコストがかからないこと。 ・ 平均レイテンシーは1桁台のミリ秒単位であること。   iQONでは1日約80万点のアイテムをクロールしているので、メンテナンスコストがかからず、ある程度のパフォーマンスが担保できることがDynamoDBの魅力でした。DynamoDBに関する説明は このスライド に詳しく書いてあります。 DynamoDBの導入手順 AWS SDK for Rubyを使ったDynamoDBの利用 まずaws-sdkのgemをインストールします。 DynamoDBはサーバーにインストールして使うものではなくあくまでサービスなので、開発環境やCircleCIなどのテスト環境ではDynamoDBと互換性のある DynamoDB Local を使います。 テーブルを作成 クロールしたアイテムを保存するテーブルを以下のように作成します。AWSコンソール上からでもテーブルの作成は可能ですが、DynamoDB Localにも同様のテーブルを作成したかったのでその方法を記載しました。 DynamoDBは基本的にはスキーマレスなのですが、HASH keyとRANGE keyについてはスキーマを定義する必要があります。DynamoDBを使いこなすためのkeyやインデックス定義の仕方は この記事 が参考になります。 データの書き込みはこんな感じ HASH keyとRANGE keyの二つをスキーマに定義した場合は二つのkeyで値を取り出す。 CircleCIへのDynamoDB Local導入 VASILYではテストツールとしてCircleCIを利用しています。 CircleCIはDynamoDBを標準ではサポートしていないため、テスト時にDynamoDB Localを動かすように設定する必要がありました。 まずはDynamoDBをubuntuに入れるシェルスクリプトを記述。 circleci.sh dynamodbディレクトリがなければAmazonから最新版を取得して展開するようにしています。 circle.ymlでDynamoDB Localを起動するように指定します。 circle.yml dependenciesのcache_directoriesに展開済のdynamodbディレクトリをキャッシュするように指定し、次回以降のCircleCIのセットアップの時間を短縮することができます。 あとは、preでDynamoDB Localを起動させればOKです。 参考にした記事 Dynamic DynamoDBを用いたプロビジョニング量自動調整 DynamoDBの利用料金はストレージ容量、転送容量、書き込み・読み込みスループットのプロビジョニング量で決まります。詳しい説明は 公式ドキュメント にあります。その中でも、プロビジョニング量の課金が一番料金を左右する部分です。AWSコンソール上でプロビジョニング量は手動で調整できるので、最初はとりあえず余裕を持って設定しました。(赤線がプロビジョニング量、青が実際に発生したスループット)  しかし、書き込み・読み込みスループットは時間帯や時期によって大きく変動するので、プロビジョニング量を大きめに設定すると無駄な料金が発生してしまい、小さくしてしまうと性能劣化を招く危険性があります。そこで、プロビジョニング量をスループットの増減に応じて自動で調整する Dynamic DynamoDB を導入しました。導入の際に参考にしたのは この記事 です。 Dynamic DynamoDBで指定できるオプションについて DynamoDBはconfigファイルの設定に様々なオプションを設定することができます。オプションが多すぎるので、最低限のものだけ設定しました。 dynamic-dynamodb.conf その他のオプションについては こちらのドキュメント を参照してください。 これらのオプションを設定し、Dynamic-DynamoDBを動作させたところ以下のようにプロビジョニング量がスループットに応じて自動調整されるようになりました。  まとめ DynamoDBはフルマネージドサービスなのでメンテナンスコストもかからず、パフォーマンスも高いことが一番の利点です。しかし、部分的にスキーマ定義を行う必要があることから、スキーマレスであるmongodbと全く同じ使い方ができるわけではありません。詳しくは MongoDBとDynamoDBの性能比較記事 を参照してください。 ただし、あくまでKVS的な使いかたで利用するのであればメンテナンスコストの低いDBとして幅広く使うことができます。今回紹介したDynamic DynamoDBも併用すれば利用金額も大幅に節約することができます。 VASILYでは今回の事例のように新しい技術を使って課題を解決していけるエンジニアを募集しています。一緒に新しい技術をどんどん取り入れて、ファッション業界にイノベーションを起こしていきましょう。 VASILYのエンジニア募集要項はこちら
Androidアプリを効率良く開発する上で各社様々な外部サービスやツールを利用していると思います。今年の6月にGoogleからトップデベロッパーに選ばれた弊社VASILYでも効率化のため多くのサービスやツールを利用しています。 今回は 2015年11月 の今、実際にAndroid版の iQON で利用しているサービスやツールを紹介したいと思います。 ログ解析 ・ Puree ・ BigQuery 数ヶ月前までアプリのログ解析に関しては Localytics を利用していましたが、アプリの規模が大きくなってきたことと優秀なデータサイエンティストが会社にジョインしてくれたことで、自社で生のログを管理した方が都合良くなってきました。そのためLocalyticsをやめて新しい仕組みに置き換えました。 新しい仕組みはクックパッド社で開発されたログコレクタのライブラリであるPureeを利用して自社のサーバーにログを送り、そこからBigQueryに流す形になっています。 そこからBigQueryでSQLを実行して数値を取ったり、別のサービスを使ってBigQueryからデータを取得したりして分析を行っています。 クラッシュ解析 ・ Crashlytics クラッシュ解析に関してはCrashlyticsを利用しています。 これまでCrashlytics -> BugSence (現 Splunk MINT ) -> Google Analyticsといろいろ他社のサービスも利用していましたが、レポートの即時性や見やすさなどを考慮して、結局Crashlyticsに戻ってきました。 今ではアプリのリアルタイムの動向が見られるAnswersという機能と、後述するBetaという機能が入ったため非常に役立っています。 Twitter社が提供するFabricに統合されたため導入するとアプリ全体のメソッド数がかなり増えてしまいますが、 Multi-dex Support していたり、メソッド数に余裕があったりするプロジェクトでは利用する価値があるサービスだと思います。 リリース作業・テストの自動化 ・ Beta 社内テストや検証などでAPKの配布を行う場合にはCrashlyticsの機能であるBetaを利用しています。 過去には他社サービスを利用していた時期もありましたが、iOSで先にBetaを利用していたこともあり社内でサービスの統一ができた点と、Crashlyticsに内包されているためにすぐに利用開始できた点からBetaを採用しました。   ・ CircleCI ・ AWS Device Farm テストやリリース作業の自動化にはCircleCIを利用しています。 Betaと組み合わせてAPKの自動配布を行ったり、Developer ConsoleへのAPKの自動アップロードを行ったりして、作業効率の向上を図っています。 詳しくは こちら を参照してください。 また AWS Device FarmによってE2Eテストを自動化 しています。しかし、先日から Cloud Test Lab の検証を始めましたが、非常に素晴らしいサービスなため今後はこちらに乗り換える可能性が高くなってきました。 *Cloud Test Labは現在、クローズドαテスト中のためGoogleからの完全招待制になっているそうです IDE ・ Android Studio IDEはチーム全員がAndroid Studioを利用しています。 Preview版の頃からAndroid Studioで開発を行っているため、使い始めて早2年になりますがアップデートを重ねて非常に使いやすいものになってきました。 昨年の12月に正式版である1.0が出て現在は1.4まで開発が進んでいますが、これまで様々な機能が追加されてきました。 追加された機能の中でも特に利用しているものが Memory Monitor になります。アプリのメモリ使用量をリアルタイムに表示してくれるため開発を行う上で非常に役立っています。ただし異常を発見した際、詳細を追うには従来通りに Memory Analyzer を利用する方が効率良いので今後のAndroid Studioのアップデートに期待しています。 CLIツール ・ dumpsysコマンド VASILYでは通年で学生インターンを募集していますが、Androidチームにジョインした学生に最初に教えることは、このdumpsysコマンドの使い方です。 dumpsysコマンドをしっかり押さえていると、自分たちのサービスのコードリーディングが捗るのはもちろんのこと、他社のアプリを覗き見したいときに非常に便利です。 adbには様々なツールがありますがdumpsysは使用頻度が特に多いと感じています。 dumpsysコマンドに関しては マネーフォワード社のブログ によくまとまっているのでぜひ参照してみてください。 UI実装 ・Grid 現在少しずつマテリアルデザインに移行中ですが、マテリアルデザインのUI実装を行う際にデザインから1dpのズレも出さないために Grid というUI確認ツールを作りました。 機能は至ってシンプルで、マテリアルデザインでよく使われる間隔である 8dp の網をオーバーレイ表示で画面全体に敷き詰めるだけになります。 非常にシンプルなツールですが、エンジニアとデザイナー間でUIの確認をする際に非常に役立っています。 簡単に作れて開発効率とUIのクオリティが上げられるのでぜひとも作ってみてください。   ・開発者向けオプション > レイアウト境界を表示 開発者向けオプションの中でもお馴染みの機能ですが、Gridと同様にUI実装を行う際によく使用する機能になります。 MarginとPaddingがどのようにかかっているのかを目視で確認できるためUI実装に役立つだけではなく、コードレビュー時に最適なレイアウトファイルが組めているか確認するときにも役立っています。 その他 ・SORACOM Air 帯域制限をかけた低速な通信速度で動作確認を行えるように、 株式会社ソラコム が提供しているIoT向けデータ通信SIMであるSORACOM Airを最近導入し始めました。 ユーザーコンソールからSIMの管理・操作が行え、帯域の制限を開発者が簡単に行うことができるため使い勝手が良いです。 SDK も用意されており、コードからも容易にあらゆる操作が可能になっています。 ドキュメント も充実しているので大変便利です。 @Horie1024 がSORACOM SDKを使用して帯域を変更する方法を ブログ にまとめているのでぜひ参照してみてください。 まとめ 細かいものを入れると書ききれないほど様々なサービスやツールを利用していますが、今回は利用頻度が高いものや、チーム内でホットなものを中心に紹介しました。 2年前にも 同じ内容の記事 を書かせていただきましたが、この2年で変わったもの、変わらないものがそれぞれあり感慨深かったです。 今後もチームの開発環境や効率が向上するなら新しいサービスやツールをどんどん導入していきたいと考えているため、オススメがありましたらぜひとも教えてください。 最後に VASILYでは、新しい技術が大好きなエンジニア&学生インターンを募集しています。少しでもご興味のある方は是非 こちら からご応募よろしくお願いいたします。
こんにちは。VASILYでインターンとして働いている永井です。大学では統計の研究をしていて、VASILYでは主にデータ分析に取り組んでいます。今回は先月の10月13日にβ版で提供開始となったGoogle Cloud Datalabを試してみたので、その紹介をしたいと思います。 1.Cloud Datalabとは? 2.準備 3.Notebookの作成 4.BigQueryからのデータ取得 5.取得したデータの可視化 という流れで紹介していきます。   Cloud Datalabとは?   Cloud Datalabの特徴 Cloud Datalabは、Google Cloud Platform上で起動するJupyter Notebook(旧IPython Notebook)のことです。 特徴としては、 ・BigQuery、Compute Engine、Cloud Storage上にあるデータの分析をPython、SQL、JavaScriptでおこなえる ・Jupyter Notebookを使用しているのでデータを対話的に分析でき、分析結果の描画、保存、共有などが簡単に行える ・料金はCompute EngineやBigQueryなどを使った分だけ課金される  ということがあげられます。VasilyではデータをBigQueryを使って管理していて、自分が普段Jupyter Notebookを使ってデータ分析をおこなっているので、今回試してみることにしました。 Jupyter Notebookとは? Jupyter Notebookは元々はIPython Notebookという名前で使われていて、 Pythonをウェブブラウザ上で対話的に実行し、保存、共有ができるサービスです。Jupyter NotebookはそれをRやJulia、Scala等他の言語(現在は40以上の言語)でも使えるようにしているもので、現在ではIPython NotebookもJupyterプロジェクトの一部、という扱いになっています。 Cloud Datalabの使い方は基本的にはJupyter Notebookと同じなので、普段Jupyter NotebookやIPython Notebookを使用している人は普段と同じように使えると思います。 では、実際にCloud Datalabを試していきます。   準備 Cloud Datalabを使い始めるためには、Googleアカウントを取得し、Google Cloud Platformに登録する必要があります。ここで課金のための情報登録が必要になりますが、60日間、300ドル分までの無料期間が用意されています。逆にGoogle Cloud Platformに登録する以外には難しい設定は必要なく、Google Cloud Platformで新しいprojectをつくれば、後はCloud DatalabのページでDeploy Datalabボタンをクリックして少し待つだけでCloud Datalabを使えるようになります。   Notebookの作成 分析を始めるには、まずNotebookを作成します。Notebookはボタンを押せば新しいNotebookがすぐに作成できます。   これでNotebookが作成できました。後はPythonのコードを矢印で示した"Cell"に入力しながら対話的にデータを分析していくことになります。 今回はCloud Datalabの特徴である、BigQueryからのデータの読み込みを行い、結果を可視化するところまでを行いたいと思います。 使用するデータはBigQueryにサンプルデータとして用意されているgithub_timelineというテーブルで、これは過去ある期間にgithubに作成されたレポジトリの作成日時、使用言語等が入っているものです。   BigQueryからのデータ取得 Cloud DatalabからBigQueryのデータを取得するには、gcp.bigqueryをimportし、 bq.Query(query内容)として実行するだけです。 結果だけを表示したい場合には bq.Query(Query内容).results() とすれば表示できます。  また、Queryを変数として保存し、それを使ってQueryを実行するということもできます。結果をpandasのDataFrameとして出力したい場合は、 df= b q.Query(Query内容).to_dataframe() とすれば変数dfに結果を出力することもできます。 今回はテーブルから、レポジトリ作成日、閲覧数、言語を作成日が新しいものから上位百万件を取得しています。 取得したデータの可視化 DataFrameに出力したデータは、通常のPythonと同様に、matplotlibを使って可視化することもできます。 例えば日ごとのレポジトリ閲覧数合計の推移を図示したり、 言語ごとに閲覧数を合計して図示したりもできます。  また、分析した結果は通常のJupyter Notebookと同様にhtmlとして出力したり、nbviewerで見れるようにして一般に公開することもできます。 今回試した内容を下記で公開しているので、興味のある方は確認してみてください。 http://nbviewer.jupyter.org/gist/ngyk/60799e83e8e4d4a09780?flush_cache=true   使ってみた感想 今回はBigQueryからデータを取得してそれを描画する、ということを試してみましたが、やはり同じCloud Platform上で動いているということで、簡単にデータを取得することができました。今回は紹介できませんでしたがCloud Storageへのデータの読み書きも簡単にできるので、今後もデータ分析に使っていくつもりです。また、インタラクティブな図表を作成できる機能など、使いきれていない部分もまだまだ多いので、これからさらに勉強していきたいと思います。   最後に VASILYのデータサイエンスチームでは、分析を効率良く、集中しておこなえるように新しいツールも積極的に取り入れてデータ分析に取り組んでいます。 もちろん分析内容としても色々なことに挑戦していて、 多腕バンディット問題をプッシュ通知配信の最適化に応用したり 、トピックモデリングという文章分類に使われる手法をユーザーのLikeデータに適用し、ユーザーの隠れた嗜好を推定してレコメンドに応用するといったことや、ユーザーの口コミをネットワーク分析によって明らかにすることで、ファッションに関する情報伝播がどのような構造になっているのか解明するといったことを行っています。 また、ここには書ききれないこともたくさんありますので、こういった取り組みに少しでも興味があるという方がいれば、是非 こちら からご応募をよろしくお願いいたします。 
エンジニアの権守です。今回は、VASILYのWebフロントチームがWeb版iQONのエラーレートを0.1%から0.003%以下まで減少させた際に、行った取り組みについて紹介します。 概要 今回行った取り組みを、ひとことで言うと、テストとデバッグの強化です。 具体的には、次の3つの取り組みを行いました。 APIモックを用いたテストの廃止 テストの高速化 New Relicの活用 各項目について、詳しく説明していきます。 APIモックを用いたテストの廃止 iQONは、iOSアプリ、Androidアプリ、Webの3つのプラットフォームを展開しており、 そのために、主なロジックをAPIサーバーで処理しています。 そして、それぞれのフロントのテストにはAPIモックを利用していました。 しかし、次の2つの理由からWebフロントで利用していたAPIモックの保守は行われていませんでした。 APIの更新をフロントチームが常に把握するのが困難 APIの更新の度にその変更をリポジトリに取り込みたくない 他のチームがAPIを更新したことを察知して、該当するエントリポイントに対してモックを作成するのは手間がかかります。 また、その都度、フロント側のリポジトリを更新し、コミットするのは気乗りしませんでした (当時、モックはAPIのレスポンスをjsonで保存し、Webフロントのgitリポジトリで管理していました)。 そのような背景からAPIモックが保守されなくなり、 テストを通っていてもエラーが発生するという事態が起こっていました。 そこで、Webフロントチームでは、モックではなく開発サーバー上で動作しているAPIを利用するようにしました (ここで参照するAPIは、APIリポジトリの最新のmasterブランチを自動的に取り込んだものです)。 これにより、フロントチームが保守せずとも自動的に最新のAPIを参照するため、テストの正当性が保たれるようになりました。 しかし、JSONで保存されたモックを利用するのとは異なり、実際にリクエストを投げるため、 テストにかかる時間は伸び、15分ほどになってしまいました。 テスト時間が伸びたことによりチームの士気は下がり、また、新たなテストの追加を躊躇するようになりました。 そこで、次に、チームはテストの高速化に取り組みました。 テストの高速化 まず、最初に行ったのはAPIの開発サーバーをwebrickで立ち上げていたのをunicornに変えました。 これは非常に手軽な変更でしたが、テスト時間を2, 3分減らすことができました。 しかし、それでもまだ10分以上かかっていたので、さらに高速化を続けました。 次に、取り組んだのは テストの並列化 です。 実は、ローカルではテストに1分ほどしかかかっていませんでした。 10分以上かかっていたのはCircle CI上からリクエストを投げることによって、 通信に時間がかかっていたのです。 そこで、テストの並列化を行うことにしました。 iQONではテストにRSpecを利用しています。 RSpecを並列処理するライブラリの候補として、次の3つを検討しました。 parallel_tests rrrspec test-queue 結果的にtest-queueを導入することにしました。 test-queueを選んだ理由としては、次に2つが挙げられます。 割り振るプロセスを動的に決めてくれる 導入が簡単 parallel_testsはテストを単純にワーカー数で割るため、重たいテストが一つのプロセスに集中すると、全体の実行時間が長くなってしまいます。 rrrspecは、その点は問題ありませんが、導入にはMySQLやRedisの準備が必要でしたので、今回は見送りました。 test-queueの導入は非常に簡単で、Gemfileへのtest-queueの追加と、Circle Ciの設定ファイルの変更のみで済みました。 # Gemfile group :test do gem ' test-queue ' , ' 0.2.13 ' end # circle.yml test: pre: override: - TEST_QUEUE_WORKERS=10 bundle exec rspec-queue spec/controllers test-queueを導入した結果、Circle CI上のテストの実行時間も5分程度に収まるようになりました。 New Relicの活用 テストがよく運用されるようになりましたが、それでも全てのバグを予め潰せるわけではありません。 そこで、起こってしまったエラーをより早く検知し、対処できるように、New Relicを活用することにしました。 Webフロントチームでは、元々、エラーの検知やパフォーマンスの測定のために、New Relicを導入していましたが、 様々な要因で、まともに機能していませんでした。 そこで、次のような改善を行いました。 404エラーの除去 カスタムパラメーターの追加 アラートの設定 404エラーの除去 以前は、正常に処理した結果としてNotFound (404) になるような場合にも、 New Relicにエラーとして送信していました。 そのため、本当に問題のあるエラーが埋もれがちでした。 そこで、404エラーの場合は、New Relicに送信するのをやめました。 また、その際に、正常に処理できていない場合に404にするといった、 適切でない例外処理を除き、エラーをNew Relicに適切に送信するようにしました。 これによって、サイト上で起こっているエラーが一目でわかるようになりました。 カスタムパラメーターの追加 New Relicにはカスタムパラメーターという機能があり、エラーの送信時に、 情報を付加することができます。 そこで、デバッグの効率を改善するために以下のパラメーターを追加しました。 パラメーター名 値 備考 full_url クエリパラメーターを含まないURL全体 デフォルトで送られるURLはurlでなくpathなので、item.iqon.jpとwww.iqon.jpを判別するために追加しました params リクエストに渡されたパラメーター 同じURLでもパラメーターによって処理が異なることもよくあるので、デバッグに必須です。パスワードやメールアドレスなどの個人情報については送信しないようにしています referrer リファラー エラーが発生した際にそのページが不要な場合があります。それを判断するためによく用います user_agent ユーザーエージェント 同じURLでもPCとスマホで処理が異なるので、その判別や、botによるアクセスかを判断するために用いています iQONのWebフロントはRailsで実装されています。 今回のカスタムパラメーターの追加は、application_controllerに以下の様なコードを追加することで実装しました。 before_action :add_custom_requests_to_newrelic def add_custom_requests_to_newrelic copied_params = params.clone copied_params[ :email ] = copied_params[ :email ].gsub( /./ , " * " ) if copied_params[ :email ].present? copied_params[ :password ] = copied_params[ :password ].gsub( /./ , " * " ) if copied_params[ :password ].present? :: NewRelic :: Agent .add_custom_parameters( params : copied_params.inspect, full_url : request.original_url, referrer : request.referrer, user_agent : request.user_agent, ) end 上の画像はNew Relicに送られてきたエラーの詳細画面です。 追加したカスタムパラメーターは画面の右下部分に表示されています。 これらのカスタムパラメーターを追加することで、デバッグの効率が大きく改善しました。 アラートの設定 エラーレートが一定以上に高まった場合に、Slackに通知がくるように設定しました。 これによって、万が一、デプロイした内容にバグが含まれていた場合などにもいち早く事態に気づくことができるようになりました。 まとめ 適切なテストを行うための仕組みを作ることと、デバッグの効率化を図ることで、 エラーレートを下げることに成功しました。 特に、404エラーの送信を止めたことがエラーレートを下げた一因ではありますが、 それによって他のエラーが表面化することで、保守されていなかったページのエラーや、 特定の条件でのみ発生するエラーに気づけたことが大きかったです。 また、今回行ったカスタムパラメーターの追加やテストの並列化はそれぞれ一日で導入できたのに対し、 非常に効果的でしたので、同様のパフォーマンス監視ツールやテスト方法を用いている方におすすめです。 最後に VASILYでは、プロダクトの品質に責任をもてるエンジニアを募集しています。 ご興味のある方は是非 こちら からご応募よろしくお願いいたします。 参考URL parallel_tests ではなく test-queue に乗り換えようと思う
こんにちは。iOSエンジニアの庄司( @WorldDownTown )です。 最近のIQONのアップデートで、コーデのタグ表示のUIを変更しました。 この変更では、ユーザーがテキストで無制限に埋め込んだタグを選択できるようになりました。 例えば「#スニーカー」をタップすると、「スニーカー」タグが付いたコーデが表示されます。 他のアプリでも見かけるUIなので、簡単にUITextViewで実装できるかと思ってたのですが… 思いの外ハマったので、今回の実装を共有します。 UITextViewのサブクラスを作成して、上記の動きを実現します。 実装  1. UIGestureRecognizerを継承したクラスを作る IQONのコーデのタグのような使い方だと、リンク文字列が多いためタッチイベントを奪ってしまい、スクロールに失敗することが多くなります。 NSAttributedString の NSLinkAttributeName の機能では上記の問題が発生するため、独自のジェスチャー ( TouchUpDownGestureRecognizer ) を実装しました。 このGestureRecognizerを次に説明するUITextViewのサブクラスに適用します。 このジェスチャーがUITextViewに対するタッチイベントの、開始、終了、移動、キャンセルの状態を決めます。 1-1. 親Viewのジェスチャーとの衝突を回避する スクロールを阻害しないようにするには UIGestureRecognizerSubclass の下記のメソッドをオーバーライドします。 // TouchUpDownGestureRecognizer.swift override func canPreventGestureRecognizer (preventedGestureRecognizer : UIGestureRecognizer ) -> Bool { if let _ = preventedGestureRecognizer.view as ? UIScrollView { return false } // UIScrollViewのスクロールイベントを優先させる return true } 2. UITextViewを継承したクラスを作る 自作したジェスチャを受け取るUITextViewのサブクラスのLinkTextViewを作ります。 2-1. ジェスチャーの設定 LinkTextViewのイニシャライザでジェスチャーを設定します。 // LinkTextView.swift required init ?(coder aDecoder : NSCoder ) { super . init (coder : aDecoder ) ... let recognizer = TouchUpDownGestureRecognizer(target : self , action : "handleTouchUpDownGesture:" ) addGestureRecognizer(recognizer) } func handleTouchUpDownGesture (recognizer : TouchUpDownGestureRecognizer ) { switch recognizer.state { case .Began : touchDownWithRecognizer (recognizer) // テキストをハイライトさせる case .Changed : break case .Ended : touchUpWithRecognizer (recognizer) // ハイライトしたテキストを戻し、テキストをクロージャに渡す default : touchCancelWithRecognizer (recognizer) // ハイライトしたテキストを戻す } } 2-2. タップした文字列を判別する 下記のメソッドでタッチした位置のリンク文字列の位置と長さをNSRangeで受け取ります。 // LinkTextView.swift /** 選択したリンク文字列のrangeを返す - parameter recognizer: ジェスチャーレコグナイザ - returns: タップしたリンク文字列のNSRange。リンク出ない場合はnil **/ private func selectedLinkRangeWithRecognizer (recognizer : TouchUpDownGestureRecognizer ) -> NSRange ? { // タッチ位置がtextContainerInsetの分だけズレるので調整 var location = recognizer.locationInView( self ) location.y -= textContainerInset.top // タッチした位置の文字列の位置 location.x -= textContainerInset.left let characterIndex = layoutManager.characterIndexForPoint(location, inTextContainer : textContainer , fractionOfDistanceBetweenInsertionPoints : nil ) // 指定した位置と属性に該当するリンク文字列のNSRangeをrangeに格納する var range = NSMakeRange( 0 , 0 ) let object = attributedText.attribute( LinkTextView.LinkKey, atIndex : characterIndex , effectiveRange : & range) return (object == nil ) ? nil : range } ハマったこと 3D Touch 対応端末で動かなかった iPhone 6s を購入して動作を検証すると、リンクが動きませんでした。 Xcode7のiPhoen 6sシミュレータでは再現しないため、かなり焦りましたが、原因は3D Touchの仕組みによるものでした。 UIGestureRecogniezrを継承したクラスでは、タッチの強さ ( touch.force ) の変化でも touchesMoved:withEvent: が呼ばれてしまうようです。 // TouchUpDownGestureRecognizer.swift // 修正前 override func touchesMoved (touches : Set <UITouch> , withEvent event : UIEvent ) { super .touchesMoved(touches, withEvent : event ) state = .Cancelled } // 修正後 override func touchesMoved (touches : Set <UITouch> , withEvent event : UIEvent ) { super .touchesMoved(touches, withEvent : event ) if let touch = touches.first { let beforePoint = touch.previousLocationInView(view) let afterPoint = touch.locationInView(view) // タッチした指が動いたらキャンセル // 3D Touch 対応端末では touch.force の変化でも touchesMoved:withEvent: が呼ばれてしまうので、厳密にtouchの位置の変化を監視する if ! CGPointEqualToPoint(beforePoint, afterPoint) { state = .Cancelled } } できあがったもの こんな感じで動きます。 使い方 LinkTextView の attributedString にリンク文字列を NSAttributedString で渡します。 この時、リンクを識別するために LinkTextView.LinkKey という属性を含めます。 タップ時の処理はクロージャプロパティに設定し、リンク文字列をタップした時のみ、このクロージャが呼び出されます。 let attributes = [ NSForegroundColorAttributeName : UIColor.blueColor (), // リンク文字列の色 LinkTextView.LinkKey : "linked" , // リンクと認識するためのカスタムキー mutableAttributedString.addAttributes(attributes, range : NSRange ( 0 , 5 )) ] // helloをリンクにする let textView = LinkTextView() textView.attributedText = mutableAttributedString textView.linkClickedBlock = { (string : String ) in // リンクがタップされた時の処理 } まとめ 文章の中の任意の文字列をリンクにする方法を紹介しました。 今回の方法を使えば、UIScrollViewにaddSubviewしてもスクロールイベントを妨害せずにリンクを作る事ができます。 3D Touch端末の問題をクリアしていますが、逆に3D TouchのPeekを実装するためには、別の実装にするのが良いと思います。 細かいところはいくつか省きましたが、そのまま動くサンプルプロジェクトを用意したので、動かしながら確認してみてください。 https://github.com/WorldDownTown/LinkTextView
こんにちは、VASILYエンジニアの塩崎です。 今回はiQONを支えているクローラーの並列処理について紹介したいと思います。 並列処理の効率化をする過程でresqueを見限りsidekiqに移行した理由、移行時に書き換えた部分などについてもお話ししたいと思います。 iQONのクローラーの並列処理の仕組み iQONでは毎日数100万点のアイテムのクローリングを行っています。 一度クローリングしたアイテムも毎日1回再クロールし在庫や値下げをiQON内のDBと同期しているため、大量のアイテムを効率良くクロールする仕組みが必要です。 クロールの処理は「ダウンロード」と「スクレイピング」の2つに大きく分けることができます。 このうち、ダウンロードの部分については、提携ECサイトに対して負荷がかからないようにスケジューリングをする必要があります。 この部分の効率化についてVASILYでは分散MutexとResqueを使った並列処理システムを使っています。 ResqueでECサイトごと個別のキューを作り、キューからジョブを取り出し処理をするワーカーもサイトごと個別に作ります。 そして、ダウンロードワーカーは処理を行う前にmutexのロックを行うことによって、1つのECサイトに対して同時に大量のGETリクエストを投げることを防ぎます。 これによって、ECサイト毎に一定の速度でリクエストを発行することができます。 この機能を実現するためにRubyのメタプログラミングを活用し、クラスの動的な生成を行っています。 ダウンロードワーカーは複数のサーバーに分散しているため、このmutexはredisで管理しています。 この辺りの詳細については、以下の記事に詳しく書いてもあります。 Redis::DistMutex - 時限付き分散ロックで効率良くサイトクロールをしよう Resqueで複数サイトにまたがるクローリングを最適化しよう ECサイトが増えた時に問題発生 しかし、クロールするべきECサイトが増えた時に問題が発生しました。 前回の記事でもお伝えしましたが、クローリングするECサイトの数を80から400に増やすという施作を実施しました。 その結果クロールする速度が全然スケールせず、サーバーを増やしても増やしてもクロールしきれないという問題が発生しました。 ECサイト数の増加に合わせてサーバー数も5倍に増加させてみましたが、全くクロールしきれませんでした。 ボトルネックの計測 問題解決のためには、ボトルネックの計測をしなければいけません。 以下のような実行時間測定用のメソッドを作成し、怪しそうな部分全部にこのメソッドを適用しました。 ログ集計用のバックエンドにはfluentdとBigQueryを利用しているためデータのスループットについては特に考えずに、少しでも怪しそうな部分全部で実行時間の測定を行いました。 その結果、ダウンロードや解析の処理そのものは決して遅くないことが分かりました。 Resqueue内部でRedisからジョブをデキューしている部分でほとんどの時間を費やしていることが分かりました。 Resque内のボトルネック Resqueのソースコードを見てみると、デキューをする時にキューを1つ1つ順番にチェックしていました。 Redisには複数のリストから1つのデータを取得するためのAPIである BLPOP があるため、このループの実装はムダです。 iQONのクローラーではクロールするECサイトの数だけ動的にキューが作られるので、キューの数も80から400に増えました。 そのため、この部分が大きなボトルネックになっている可能性があります。 sidekiq移行 というわけで、Resqueはキューの数が増えた時にスケールしないのでsidekiqに移行することにしました。 移行にあたっては以下のことを重要視しました。 ・キューの数が多い時にスケールするかどうか ・Resqueからの移行が簡単か ・キューやワーカーを動的に生成することができるかどうか 開発環境でこれらのことを検証した結果、sidekiqが最有力候補に残りました。 ソースコードを読んでみても、BRPOPを使ってdequeueをしていることが確認できましたので、キューが増えた時でも効率的にdequeueしていることがわかります。 また、sidekiq移行をすることで以下のようなメリットも期待できました。 ・並列化がプロセスレベルではなくスレッドレベルであるため、forkのオーバーヘッドがない ・キューからジョブを取得するときに、キューのpriorityを毎回shuffleできる ・web UIがキレイ 最終的には 公式 に書かれていた What if 1 Sidekiq process could do the job of 20 Resque or DelayedJob processes? という挑発的な文言に煽られて導入を決意しました。   移行にあたって Resqueからsidekiqへの移行は https://github.com/mperham/sidekiq/wiki/Resque-Compatibility を参考にしました。 ワーカー、キューの動的な生成 基本的にはほとんどいじる部分がなさそうですが、iQONのクローラーはECサイトごとにキューやワーカーを動的に生成しているため、その部分を考慮する必要があります。 以下のようにしてECサイトごとのワーカー、キューを動的に生成しています。 エンキューするときには次のようにします。 このときにhoge-1, hoge-2, hoge-3という3つのキューが動的に生成され、それぞれに対して1つずつジョブがエンキューされます。 また、エンキューされたジョブを処理するためのワーカーは以下のようにして起動します。 キュー名の後ろに",1"をつけることによってdequeueをするときのpriorityをランダムにすることができます。 これを指定しないとdomain_idの小さいキューが毎回最優先でdequeueされ、分散mutexの競合が頻繁に発生してしまいます。 移行した結果 resqueを捨て、sidekiqに移行したことによってRedisからのdequeueのオーバーヘッドが大幅に減り、クロール速度が大幅に向上しました。 また、forkのオーバーヘッドが減ることによって1サーバーあたりのワーカー数を4から18に増やすこともできました。 そのほかにも諸々の改修を行うことによって、クローラー用サーバー管理費を1/10にすることも達成しました。 後日談 Resqueの最新版ではBLPOPを使用しているみたいでした。 ですが、最新版でもforkのオーバーヘッドは依然として存在しているので、やはりsidekiq移行はしてよかったと思います。 まとめ クロールするECサイトが増えるに従って、Resqueではスケールしないような状況になってしまいました。 そのため、dequeueが効率化されているsidekiqを導入しました。 結果としてクロール速度の大幅な向上とサーバー管理費を1/10にすることを達成しました。 最後に VASILYでは一緒に働くことができる優秀なエンジニアを募集しています。 世界一のファッションデータベースを作ることに興味のある方は是非 ご応募 ください。
VASILYの自動化大好きAndroidエンジニア堀江( @Horie1024 )です。今回、GitHubとCircleCIを利用したAWS Device Farmでのテストの自動化ついてご紹介しようと思います。 概要 Calabash で書いたAndroidアプリのE2Eテストを AWS Device Farm で実行する GitHub + CircleCIでAWS Device Farmでのテストの実行を自動化した Hubotを利用して実行中テストの監視とSlackへの通知をやってみた Cloud Test Lab を早く試したい Bazelのロードマップに Cloud Test Labのサポート が記載されている サンプルプロジェクト サンプルプロジェクトは こちら です。サンプルアプリはLogin画面とHello world!と表示されるだけの画面を持つアプリで、このアプリをテストするCalabashのfeatureはとても簡単に書けます。 以下はローカルでテストを実行した様子です。これをAWS Device Farmで実行します。 GitHub + CircleCIでAWS Device Farmでのテストを自動化 以下の図のような流れで自動化してみました。 1. GitHubへPush 2. GitHubへのPushを検知してCircleCIがビルド開始 3. AWS Device Farmでのテストを実行 4. Slackへテストの開始とHubotへの監視依頼を通知 5. Hubotがテストの監視依頼に反応 6. AWS Device Farmで実行中のテストを監視 7. テストが完了後その結果をSlackに通知 8. 結果を確認 以下各項目の詳細です。 1. GitHubへPush 開発したコードと追加したCalabashのfeaturesをcommitしGitHubへPushします。 2. GitHubへのPushを検知してCircleCIがビルド開始 CircleCI上でAndroidプロジェクトをビルドしAPKを作成します。Androidのプロジェクトをビルドするには以下のようなcircle.ymlを用意し、プロジェクトのリポジトリに追加します。今回はmasterブランチに差分がPushされた場合にビルドされるよう設定します。 これでmasterブランチへ差分がPushされると ./gradlew assembleDebugが実行されAPKが作成されます。 CircleCIの設定などより詳しい内容は こちらの記事 をご覧ください。 3. AWS Device Farmでのテストを実行 AWS Device Farmでのテストを実行するまでの流れは以下のようになります。 1. DevicePool情報の取得 2. APKのアップロード 3. テストパッケージのアップロード 4. テストのスケジューリング これらをAWS SDKを使用して実現します。 APIドキュメントと使用するSDK ドキュメントは以下を参照しました。 http://docs.aws.amazon.com/sdkforruby/api/Aws/DeviceFarm.html また、SDKには AWS SDK for Ruby - Version 2 を使用します。 AWS SDK Clientの作成 clientを作成します。IAMでAWSDeviceFarmFullAccessのポリシーをアタッチしたユーザーを用意し、そのAccessKeyIdとSecretAccessKeyを使用します。また、regionには us-west-2 を指定します。AccessKeyIdとSecretAccessKeyは環境変数に登録したものを使用するのが良いでしょう。 Device Farmのプロジェクト情報を取得 まず試しにDevice Farmで作成したプロジェクトの情報を取得してみます。プロジェクトを作成していない場合Device Farmのコンソールから「Create a new project」をクリックしプロジェクトを作成しておきます。 プロジェクト情報は以下のコードで取得できます。 devicefarm.get_project({ arn: "プロジェクトのARN" }) ここで、プロジェクトのARNは以下のようになります。 arn:aws:devicefarm:us-west-2:{ユーザーID}:project:{プロジェクトID} プロジェクトIDは、Device Farmでプロジェクトを開いた際のURLの以下の部分を使用します。 /devicefarm/home?region=us-west-2#/projects/この部分/runs DevicePoolの取得 デバイスプールについて テストを実行するデバイスの種類は、デバイスプールと呼ばれる単位で管理されています。デバイスプールをAWS SDKから作成することは出来ますが、コンソールから作成した方がわかりやすいです。 コンソールのプロジェクトページに表示される「Create a new run」をクリックすると新しいRun(テスト対象APKやデバイスの種類、テストケースを紐付けたもの)を作成することができます。そのStep3で任意のDevicePoolを作成できます。 デフォルトで「Top Devices」というDevicePoolが用意されていますので、今回はこちらを使用します。 AWS SDKでのDevicePoolの取得 以下のコードでDevicePoolを取得できます。レスポンスにはDevicePoolのARNなどの情報が含まれます。ドキュメントは こちら です。 APKのアップロード アップロードは2段階の手順を踏みます。 ・ devicefarm.create_upload()でのinitialize ・ Pre-Signed URLを使用したAPKアップロード initialize 以下のコードで初期化し、Pre-Signed URLを取得します。また、アップロードするオブジェクトのARNを取得しておきます(実行をスケジューリングする際に使用します)。ドキュメントは こちら です。 create_uploadのレスポンスのシンタックスは以下のようになっています。ドキュメントは こちら です。   { "Upload": { "Arn": "string", "ContentType": "string", "Created": number, "Message": "string", "Metadata": "string", "Name": "string", "Status": "string", "Type": "string", "Url": "string" } }   statusは、 FAILED 、 INITIALIZED 、 PROCESSING 、 SUCCEEDED の4種類があり、create_uploadを実行した直後は、 INITIALIZED になり、この状態ではDevice Farmから利用できません。Pre-Signed URLを利用してAPKをアップロードすることで PROCESSING となり、その後 SUCCEEDED へ変化します。ここで PROCESSING の際に不具合が起きると FAILED になります。 upload こちら を参考に、以下のようなコードでAPKをアップロードできます。 テストパッケージのアップロード 今回テストにはCalabashを使用するので、Calabashのfeaturesを圧縮後テストパッケージとしてアップロードします。アップロードの手順はAPKのアップロードと同じです。create_uploadのtypeに CALABASH_TEST_PACKAGE を指定します。 テストの実行 実行のスケジューリング 実行のスケジュールは以下のように行います。ドキュメントは こちら です。 app_arnには、APKをアップロードした際に取得したARNを、device_pool_arnはDevicePoolの取得で取得したARNを、test_package_arnにはCalabashのfeatures.zipをアップロードした際に取得したARNを使用します。 APK、テストパッケージのアップロード完了を待つ APK、テストパッケージをアップロードした直後では、それぞれのstatusが SUCCEEDED になっているとは限りません。このため、定期的にstatusが SUCCEEDED かどうか確認する手順が必要になります。 get_uploadを使用すると、uploadしたオブジェクトの状態を取得できるので、それを定期的に取得しstatusを確認します。そして、APK、テストパッケージ両方のstatusが SUCCEEDED ならschedule_runを実行します。 これでスケジューリングされ、Device Farm上でCalabashのテストが実行されます。 CircleCIからのスクリプトの実行 CircleCIからここまでで作成したRubyのスクリプトを実行し連携させます。circle.ymlは以下のように設定します。 ここでdeploy.shは以下のようになります。 これにより、GitHub上のmasterブランチへ差分がPushされると deploy.sh が実行され、以下の流れでDevice Farmでのテストが自動実行されます。 1. ./gradlew assembleDebug (APKの作成) 2. zip -r features.zip features (テストパッケージの圧縮) 3. ruby scripts/devicefarm.rb (APK、テストパッケージのアップロードとテストのスケジューリング) 4. SlackにHubotへの監視依頼を通知 スケジューリングしたテストは、テスト終了まで時間がある程度かかるためHubotにテストの進捗を監視させます。Hubotへの指令はSlackを経由して行い、Slackへの通知にはSlackの Incoming WebHooks を利用します。 Incoming WebHooksの設定はSlackの「Configure Integrations」からIncoming Webhooksを選択します。 選択すると以下のような画面に遷移しますので、Incoming WebHooksをpostするchannelを選択し、「Add Incoming WebHooks Integration」をクリックします。 これでWebhook URLを取得できるので、そのURLに対してPOSTでHTTPリクエストを送ります。また、Device Farm上でのテストの実行状況を監視するには、実行中のテストのARNが必要になります。 このため、RubyスクリプトからスケジューリングしたRunのARNをSlackに通知します。 今回、Hubotに監視依頼するフォーマットを @vasilybot ARN と決定します。 以下SlackにIncoming WebHooksで通知を送るコードです。 このSlackへの通知のコードを含めた最終的なスクリプトのサンプルは こちら になります。 5. Hubotがテストの監視依頼に反応 CircleCIからSlackのIncoming WebHooksで送られてくるメッセージにHubotを反応させます。 Incoming WebHooksで送られてくるフォーマットは @vasilybot ARN になるので、Hubot側のコードは以下のようになります。これでSlackに @vasilybot ARN と投稿された際にHubotが反応するようになります。 6. AWS Device Farmで実行中のテストを監視 AWS SDKを使用し、定期的にテストの状態を確認します。SDKには AWS SDK for JavaScript を使用します。 Access Key IdとSecret Access Keyについては、 Configuring the SDK in Node.js を参考に環境変数にそれぞれ  AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY として登録します。これでSDKが自動的にAccess Key IdとSecret Access Keyを参照してくれます。 getRun を使用することで実行中のテストの状態を取得できますので、Hubot側のコードは以下のようになります。 7. テストが完了後その結果をSlackに通知 getRunでテストの実行状態を監視し、statusが COMPLETED ならSlackへ結果を送信します。また定期実行を clearInterval でキャンセルします。 最終的なHubot側のコードは以下の通りです。Slackに送信する内容については、 Advanced Message Formatting を利用してテスト実行結果詳細へのリンクを付けるなど、より見やすくする工夫をしても良いと思います。 8. 結果を確認 Slackにテストの結果が表示されるので確認します。 Device Farmのコンソールを確認するとテストがパスされています。 まとめ GitHub + CircleCIを利用してAWS Device Farmでのテストの実行を自動化しました。AWS Device Farmは便利なサービスですが、プロジェクトやRunの削除が出来ない、実行中のRunのキャンセルが出来ないなどまだまだ発展途上と感じる部分も多いです。 AWS Device Farmのようなモバイルアプリのテストをクラウド上で実行する環境を提供するサービスは複数あり、AWS Device Farmも、今年7月にAmazonが AppThwack を買収して始めたサービスです。同様なサービスには、 Scirocco Cloud や Testmunk があります。さらに今年のGoogle I/Oでは、Googleが恐らく同様なサービスと思われる Cloud Test Lab の発表を行っています(事前登録に申し込みましたが今のところ連絡無し)。 Cloud Test LabはAndroidStudioとの連携も考えられているようですし、Bazelのロードマップに Cloud Test Labのサポート が入っており、非常に楽しみです。利用可能になり次第試してみようと思います。 最後に VASILYでは、自動化や新しい技術が大好きなエンジニアを募集しています。少しでもご興味のある方は是非 こちら からご応募よろしくお願いいたします。
こんにちは。VASILYに入社して、オシャレぶるようになったと周りにイジられているデータサイエンティストの金田です。 VASILYでは、プッシュ通知の開封数を上げるために様々な施策を行っていますが、その一つとして、多腕バンディット問題を応用し、複数の異なるタイトル文の配信比率を動的に最適化することで、開封数を高めるといった取り組みを行っています。今回は、なぜプッシュ通知配信の最適化に多腕バンディット問題を応用したのか、アルゴリズム選定にあたりどのようなポイントを考慮したか、また実用にあたってどのような問題に直面し、それをどう克服したのか、といった点について紹介したいと思います。 プッシュ配信最適化の背景 iQONでは、新着の雑誌記事やコンテストのお知らせをユーザーへ通知するため、1日に数回プッシュ通知を配信しています。プッシュ通知は、どのようなタイトル文を配信するかによって、開封率が大きく異なってきますが、プッシュ配信最適化の取り組みを行う以前は、担当者がそれまでの定性的な知見を基にしてタイトル文を作成し、全てのユーザーに同じタイトル文を一斉に配信するといった運用をしていました。 そのため、日々のプッシュ通知の開封率にバラツキがあったり、また仮に、開封率が高かったとしても比較対象がなく、他の条件を一定にするという"実験"の条件を満たしていないために、なぜそのプッシュ文言の開封率が高かったのか、といった定性的な知見が蓄積されにくいという課題がありました。 従来、インターネットにおける広告やコンテンツの改善にあたっては、一般的にA/Bテストと呼ばれる手法を用いて、無作為に文言やクリエイティブの出し分けを行い、仮説検定を実施して実績の高かった方だけを採用するといった施策を行うことが多いかと思います。 しかしながら、広告やコンテンツと違い、プッシュ通知のタイトル文は賞味期限が短く、その日限りしか使えないことが多いため、仮にどのタイトル文の開封率が高いかという結果がわかっても、その結果を次に活かすことができません。そのため、プッシュ配信の最適化を行うにあたって、多腕バンディット問題に目をつけました。 多腕バンディット問題とは? 多腕バンディット問題に関しては、既に日本語でも広告やコンテンツの最適化への応用が様々なブログ等で紹介されていますが、機械学習の強化学習の手法の一つです。 問題の設定としては、当たり確率の異なる複数のスロットマシンがあり、手持ちのお金が限られているという状況を想定します。また、マシンは1回ごとに変更ができ、アームを引くごとに当たりかハズレかの結果が確率的に得られるとします。このような条件下で、スロットマシンから得られる報酬を最大化するために当たりやすいマシンの見極め(探索)と、当たりやすいマシンにお金を掛けること(活用)をどううまくバランスをとったらよいかという問題になります。 直感的には、各々のスロットマシンに少しずつお金を掛けていき、その過程で他よりも当たりやすそうなスロットマシンがあれば、そこへ他のマシンよりも多めにお金を掛けていくことで、報酬を最大化させるといったイメージになります。 今回は、多腕バンディット問題をプッシュ通知の配信に応用して、開封数をリアルタイムに取得し、開封率が高そうなタイトル文へ配信比率を寄せることで、開封数を増やすことができるのではと考えました。   システム構成 従って、多腕バンディットを適用するためには、配信途中にタイトル文の比率を変えていく必要があるため、リアルタイムに開封数を取得する必要があります。そのため、VASILYではプッシュ通知の配信に下図のシステム構成をとっています。具体的には、プッシュ通知を配信後、デバイスからの開封情報をAPIサーバーで受け、Fluentdを介してGoogle BigQueryへストリームインサートで送っています。そして、配信サーバーからBigQueryへクエリを発行することで、リアルタイムに開封数を取得しています。この仕組みによって、タイトル文の配信比率を動的に変更しています。 また、Tableauサーバーを活用することで、配信結果が自動的にレポートに反映されるような仕組みも作っているため、エンジニアの手を介すことなく、担当者が結果を参照できるようになっています。   プッシュ通知配信に係る問題とその解決策 しかしながら、実際にプッシュ通知の配信に多腕バンディットを適用しようとした場合、通常の多腕バンディット問題とはいくつか問題設定が異なる点があり、実装にあたりいくつか試行錯誤がありました。ここでは、その問題と解決策について説明します。 ステップごとの一括送信 相違点の一つは、アームを一回ずつ引くのではなく、結果をまとめて計算する必要があることでした。なぜなら、VASILYのプッシュ通知配信の仕組みでは、プッシュ通知を何ステップかに分け、まとめて送信をする仕組みになっているためです。例えば、仮に10万通の通知を送信するのであれば、20時半から1万通づつ、5分間隔で10ステップに分けて送信するといった仕組みになっています。 アルゴリズムの実装にあたっては、O'Reillyから出ている下記の書籍を参考にしましたが、通常の多腕バンディット問題では一回づつスロットマシンのアームを引くことを前提としていてるため、実装にあたっては、新たにbulk_update() という一括で更新ができるメソッドを独自に追加することで対応しました。また実装はRubyで行い、Gemパッケージとして導入できるようにしました。 なお、今回ご紹介したRubyでの実装は、GemとしてGitHub上で公開をしていますので、要望、質問、プルリクなどありましたら歓迎いたします。 https://github.com/vasilyjp/multi_armed_bandit 報酬の遅延 また、多腕バンディット問題とのもう一つの違いは、報酬が遅れて支払われるということでした。プッシュ通知の配信の場合、実際にプッシュ通知を送信してから、それが開封されるまでにタイムラグがあります。 そのため、あまり最初の方から配信比率が大きく変化するようにパラメーターを設定してしまうと、配信数が安定せず、1ステップごとに配信比率が大きく振れてしまうということがわかりました。下の図は、ステップごとの配信数の割合を色分けして表したものですが、左側のグラフでは1ステップごとに配信比率が大きく変化してしまっていることが分かるかと思います。 従って、最初の方のステップでは、探索の方に比重を多くし、ステップが増えるに従って、探索の割合を減らし、活用の方に比重が移るようにステップごとのパラメータを調整しました。それにより、配信比率が安定し、適切に開封率の多いタイトル文の配信比率が高くなるようになりました。   アルゴリズムの選定 多腕バンディット問題のアルゴリズムにはいくつか種類がありますが、上記に記載した通り、実装にあたっては、 一括で報酬のアップデートができる 配信ステップごとにパラメータを変更できる 設定すべきパラメーター数が少ない といった条件を満たすことが重要であったため、ε-Greedy と Softmax というの二つのアルゴリズムを候補としました。 この二つのアルゴリズムの大きな違いは、どのアームが成果が高いか"探索"を行う際に、アームを等確率で引くか(ε-Greedy)、過去の期待値に応じて、期待値の高いアームを引く確率を高くするか(Softmax)に大きな違いがあります。 両方のアルゴリズムを実際に運用してみた結果、Softmaxの方が、他よりも特に開封率の低いタイトル文があった場合に、早い段階でそのタイトル文に振り分けられる配信数を減らせることから、最終的にはSoftmax アルゴリズムを採用することにしました。 結果と今後の課題 上記の取り組みの結果、プッシュ配信のタイトル文の配信比率を動的に最適化することで、全ての文言を等配分で配信した場合に比べて、平均して約5%開封数をリフトさせることに成功しました。 また同一の条件で、複数のタイトル文を配信し、その結果を簡単に検証できるようになったため、タイトル文にモデル名を入れた方がよいのか?「〜なコーデ5選」といった具合に数字を入れた方がよいのか?といったタイトル文を作成する上での定性的な仮説の検証が容易になり、PDCAを早く回せるようになりました。 今後は、年齢やユーザーの嗜好といったコンテキスト情報を入れることで、更なる最適化を実現したいと考えています。また、広告やコンテンツの出し分けといったプッシュ通知の配信以外にも多腕バンディットアルゴリズムを適用し、さらにユーザーに感動体験を与えられるようなサービスにしてきたいと思っています。 最後に VASILYのデータサイエンスチームでは、これ以外にも、トピックモデルという文章分類に使われる手法をユーザーのLikeデータに適用し、ユーザーの隠れた嗜好を推定してレコメンドに応用するといったことや、ユーザーの口コミをネットワーク分析によって明らかにすることで、ファッションに関する情報伝播がどのような構造になっているのか解明するといったことを行っています。 また、ここには書ききれないこともたくさんありますので、こういった取り組みに少しでも興味があるという方がいれば、ぜひ一度VASILYに遊びに来てください。VASILYでは一緒に働ける優秀なデータサイエンティストを募集しています。一緒に「ファッション×ビックデータ」という未開の分野を一緒に開拓していきましょう! データサイエンティスト募集要項  
こんにちはVASILYエンジニアの塩崎です。 iQONでは提携先ECサイトからアイテム情報をクロールしています。 クローラーの仕組みを大幅に変更することによって、1ヶ月間で400サイト分のクローラーを製作することができるようになりました。 今までの仕組みですと、2年間で80サイト分ですので、製作速度は100倍になりました。 今回はその仕組みをざっと紹介したいと思います。 ユーザーさんの欲しいアイテムがない! そもそも、なんでこんなにアリエナイスピードでクローラーを作る必要があったんでしょうか? iQONにはブランドLIKEという機能があり、ユーザーさんが特定のブランドをお気に入り登録することができます。 しかし、ユーザーさんに人気のブランドにも関わらずアイテム点数がほとんどないブランドが多かったです。 中にはTOP10に入っていながらもアイテム点数が数十点しかないブランドもありました。 ユーザーさんにもっとiQONを使って貰うためには、この部分の改善が必要です。 そのため、ブランドLIKEの上位1000ブランドの全アイテムをクロールするためのタスクフォースが組まれました。 1000ブランドのアイテムを集めるために 課題1 400サイトのクローラーを作成し、運用しないといけない このタスクフォースにはエンジニアが5人いたため、各人が1日あたり2サイト分のクローラーを製作すれば期限に間に合います。 しかし、そんなことをしてしまうと、膨大な数になったクローラーのメンテナンスが非常に大変になってしまいます。 仮に1つのサイトが1年間に1回リニューアルをするとしても、1日に1.5サイト分のクローラーの修正をする必要が出てしまいます。 (ちなみにこれは楽観的な想定)今まで通りの仕組みでクローラーを作成した場合は、クローラーの修正コストが馬鹿になりません。 課題2 アイテム情報の自動判定 また、当時はクロールしたアイテム情報の画像判定(iQONのコーディネート作成用の画面に出す or 出さない)やアイテムのカテゴリ判定(トップス or ボトムス or etc.)を人力で行っていました。 なので、たとえ大量のアイテムをクロールしたとしても、その判定の速度がボトルネックとなることが目に見えていました。 人力判定では1日あたり最大で3000点しか判定できませんでしたが、この程度のスピードですとアイテム情報判定の部分で新規アイテムの追加の滞留が起きてしまいます。 解決策 そのため、プロジェクト期間の前半1ヶ月で判定の自動化と、クローラーを簡単に製作するためのツール作りをすることになりました。 その後の1ヶ月を使いチーム全体でクローラーの作成をすることとなりました。 チームの約半数はビジネス職でしたので、エンジニアじゃなくてもクローラーを製作できるようなツールが必要です。 このクローラー作成ツールと自動判定機を使うことによって、今までとは比べものにならないスピードでクローリングを行い、常に最新のアイテムの情報をユーザーさんに届けられるようになりました。 クローラーの構成 まずは、今のiQONのクローラー全体の構成の紹介をしたいと思います。 クローラー作成ツールによるサイト固有パーサーの生成 ECサイト上のアイテム情報は、まず各「サイト固有パーサー」によって解析が行われます。 商品名や価格などの商品名が書かれているHTML要素はECサイト毎に異なります。 このサイト毎の差異を吸収しクローラー共通基盤に対して統一させたフォーマットでデータを提供するのがこのサイト固有パーサーの役割です。 従来の方法では、この「サイト固有パーサー」はエンジニアがrubyコードで書いていました。 しかし、その方法ではエンジニア以外の人がサイト固有パーサーを書くことができません。 さらに、ECサイトにわずかな変更があった場合でもrubyのソースコードを変更する必要があり、エンジニアが行う必要のあるメンテナンスのコストが非常に大きいです。 そのため、エンジニア以外の人でもサイト固有パーサーを簡単に製作できるようなツール、クローラー作成ツールを作りました。 このクローラー作成ツールの画面でXPATHや正規表現を入力するとその結果がDSLとしてDBに書き込まれます。 サイト固有パーサーはこのDSLをDBから読み込み、それに応じてECサイトの情報をパースします。 また、どうしてもツールでは対応できないような複雑なパースを行う必要のある商品情報(非同期なAPI通信を含むもの、詳細ページによって要素のxpathが変わるものなど)を含むECサイトもあります。 そのような要素については「エンジニアに依頼」にチェックを入れると、エンジニアがその要素のパーサーだけrubyコードで実装することができます。その後、エンジニアが書いたrubyコードとツールが生成したDSLをマージしサイト固有パーサーを生成することができます。 このツールを使用することで、エンジニア以外の人でもクローラーを高速に作ることができるようになりました。また、ECサイトのHTMLの構造が変わった場合でもエンジニア以外の人が対応することができるようにもなり、メンテナンスのコストを大幅に減らすことができました。 中には1つのサイトのクローラーをわずか3分で作ってしまうような猛者も登場してしまいました。 いままではエンジニアが1日に1つくらいのペースで作っていたため、エンジニアの間で戦慄が走るようなことにも繋がってしまいました。 アイテム情報判定の完全自動化 未判定アイテム情報テーブルに格納された情報は、自動判定機によって判定済みアイテム情報テーブルに移動されます。 自動判定機は、アイテムの画像判定とアイテムカテゴリーの判定を行います。 アイテムの画像判定 iQONというサービスの大きな特徴の一つに、ユーザーがファッションアイテムを組み合わせてコーディネートを作り、それを投稿できるという機能があります。 そのため、クロールした画像の中から、ユーザーさんがコーディネートを作りやすい画像を判定する必要があります。アイテムが単体で写っている画像がコーディネートに使われやすい傾向にあるため、その判定を行っています。 この処理は従来ではクラウドワーカーさんによる人力判定が行われていましたが、現在ではプログラムによる自動判定が行われています。 この処理の詳細は以下の記事に詳しくまとまっています。 iQONでクロールしたアイテム画像がコーディネートに使われるまで アイテム情報のカテゴリー判定 商品名や商品の説明文などのテキスト情報からアイテムのカテゴリー(Tシャツ、ブラウス、ワンピース、etc)を判定します。 この判定機内部はMeCabで形態素解析を行った結果から、アイテムのカテゴリーを最も判定している可能性の高い単語をピックアップします。 その単語をアイテムカテゴリー辞書と照会することによってアイテムのカテゴリーを自動判定します。 この処理も、クラウドワーカーさんによる人力判定から、プログラムによる自動判定に移行しました。 これら2つの自動判定によってアイテムの判定速度が大きく向上しました。 いままでは最大でも1日に3000アイテムの判定しか行えなかったのが、1日に10万点ものアイテムを判定できるようになりました。 まとめ 今回の記事では、まだクローラーの全体構成を説明するだけに留まってしまいました。 ソースコードなどもなく、図も概念的なものに限っているため、まだまだこれで本当に100倍になるのかという部分に疑問をお持ちの方も多いでしょう。 次回以降の記事で製作速度を100倍にするためにしたことをより具体的に説明していきたいと思いますので、 好ご期待ください。 クローラーそのものは直接ユーザーさんの目に触れることはありませんが、iQONというサービスの根幹を支えていると自負しています。 今回、数多くのクローラーを製作したことにより、「自分の好きなブランドのアイテムが増えた!」という意見をユーザーさんから頂くこともできました。 VASILYでは一緒に働くことができる優秀なエンジニアを募集しています。 100倍なんて生温いという方がいらっしゃいましたら 是非ご応募 ください。
こんにちはVASILYエンジニアの松本です。VASILYが運営しているiQONというサービスの大きな特徴の一つに、ユーザーがファッションアイテムを組み合わせてコーディネートを作り、それを投稿できるという機能があります。それを実現するために、iQON内では様々な画像処理が行われているのですが、それらを最近大幅に改善したのでその内容を紹介したいと思います。 iQONのコーディネートに使われるアイテム画像について コーディネートを作る過程 下の図のようにユーザーは検索画面からアイテムを検索し、それをキャンバスに配置してコーディネート画像を作っていきます。 iQON内の画像のほとんどは提携先ECサイトからクロールして取得している画像なので、コーディネートに使う画像については以下の2点を実現する必要があります。 ・アイテムを検索するときはコーディネートに使いやすいアイテムに限定して表示する必要がある ・アイテム画像の背景は綺麗に透過されている必要がある コーディネートに使いやすいアイテムの選定 iQON内では アイテム単体のみ で写っている画像がコーディネートによく使われていることが統計的に分かったので、そのようなアイテム画像のみが表示されるようにしています。   この選定ですが、iQONには現在月間60万点以上のアイテムが追加されているので、人力では処理しきれずアイテムの追加に滞留が起こっていました。 そこで、今回はその選定を自動化することに踏み切りました。 アイテム画像判定の自動化 ECサイトからクロールされるアイテム画像には膨大なパターンがあります。その中からアイテム単体の画像のみを取り出すために独自の検出器を実装することで判定を自動化しました。 画像処理ライブラリとしてOpenCVをどうしても使いたかったので、iQONのバックエンドはRubyで実装されていることから ruby-opencv を使用しました。 以下に代表的な画像のパターンについての検出器を紹介します。(赤枠が検出に用いた領域です) 複数アイテム画像の検出器  物体領域のみを抽出した mask画像 を生成し、その領域の個数をカウントして判定しています。アクセサリなどカテゴリによっては複数アイテムが入っても良い場合もあるので、その場合を考慮してフィルタリングしています。 モデル画像の検出器 ECサイトの場合、モデル画像のほとんどが顔が写っているものであることと、人物認識よりも顔認識のほうがあるかに精度が高いことから顔認識による判定を採用しました。参考にしたのはruby-opencvの サンプルコード です。 しかし、この方法をそのまま採用しても精度は低く、多くの誤検出を生み出してしまいます。そこで、ECサイトのモデル着用画像の顔の位置、大きさ、色についてさらに分析を行い誤検出を最大限減少させました。 マネキン画像の検出器 これについてはECサイトによっていろいろな種類のマネキンが存在したので一番苦労しました。上図のように首元の領域を検出してマネキンだと判定したものもありましたが、それでも全部のマネキンを検出することはできません。 そこで、過去に人力でアイテム判定を行ってきたログを解析してみることにしました。iQON内には過去すべてのアイテムについての判定結果のログがBigqueryに保存されていたので、そのデータから各ブランドごとにアイテム画像判定の結果を分析しました。その結果から特定カテゴリにおいてマネキン画像しか入らないブランドをリストアップして判定を行いました。  そのほかにも様々なフィルターを実装し、現在ではこの判定精度は95%以上の精度を保っています。 アイテム画像の背景の透過方法 上記の方法でアイテム単体が写っている画像の判定が出来たので、次はコーディネートを作るためにアイテム画像の背景透過をする必要があります。 一般的な背景透過手法 以前はこの処理を画素の明度値に対してしきい値を設けて行っていたのですが、この場合2つの問題点があります。 1. 上の図の真ん中のように背景とアイテムがほぼ同色の場合にしきい値選定が困難。 2. 上の図の右のようにしきい値を調整しても画像の端をスムーズにできない そこで、今回は1を解決するためには背景色に応じて適切なしきい値で透過処理をかけるようにしました。また、2を解決するために画像にフィルタリングとクラスタリングの処理をかけて、画像のノイズを低減しました。以下ではその概要を説明します。 手法の概要 動的に背景画像を生成 画像によって背景色は変わるため、まず最初に背景画像を入力画像から生成する必要があります。背景画像は入力画像の端をスキャンして線形補完することで生成しています。   γ補正で背景色とアイテム画像のコントラストを上げる γ補正 をかけると画像のコントラストを調整することができます。以下がサンプルコードです ここでのgammaの値は直前に行った背景画像の生成処理から得られた背景画像の明度値によって動的に変化させています。この処理によって画像のコントラストを上げることができ、しきい値選定が容易になります。   画像のノイズをフィルタリング処理で除去 ノイズ除去の最も一般的な手法に BILATERAL FILTER があります。これはエッジの情報を失うことなくフィルタングができる方法として有名です。しかし、これだけでは画像の端の境界部分にブラーが残ってしまいます。そこで mean shift クラスタリングという手法で画像のエッジ部分を滑らかにしました。 こちらの 論文 から引用した以下の画像が最も分かりやすい例でしょう。 meanshiftクラスタリングによって各領域の境界がはっきりすることがわかるとおもいます。(特に背景の海と空の境界部分) 変換処理の結果 これらの処理をさきほどの画像に適用してみます。 ruby-opencvには以下のように関数が用意されています。   背景透過処理 最後に背景画像との差分をとってmask画像を生成し、入力画像とのANDをとることで背景透過画像を生成します。   まとめ 今回はiQON内の特に画像処理にフォーカスして全容を書きました。今回の改善では人力で画像を判定していたのを自動化したことによって、多くの工数を削減できたことと、一日のアイテム追加数の大幅な増加ができたことが大きな成果だったと考えています。アイテム追加数は人力では1日最大3000点程度だったのですが、今回の自動化で1日最大10万点のアイテム追加を実現することができました。 また、画像の透過処理の改善では特に白のアイテムの背景透過処理が改善されたのが大きかったです。やはりファッションの基本となる色がコーディネートに使えないのは残念という声が多数のユーザーから上がっていたのでそこを改善できたことが大きかったです。 かなり専門性の高い処理が多く、ここでは書ききれなかったことがたくさんあるので、詳しいことに興味があればぜひ一度VASILYに遊びに来てください。VASILYでは一緒に働ける優秀なエンジニアをお待ちしています。一緒にファッション業界を変えていきましょう! VASILYのRECURUIT情報はこちら
4月ということで、先日VASILYでも 入社式 をとり行い、エンジニア/デザイナーの計6名が新たに加わりました。 やる気に満ち溢れたフレッシャーズを見ると、我々も気の引き締まる思いです! 今回は、自分が新卒デザイナーだった頃に読んだ本、読んでおけば良かったなと思う本をご紹介したいと思います。 1.  欧文書体―その背景と使い方 [ 欧文書体―その背景と使い方 ] 欧文タイポグラフィの 入門書 として最適な一冊。 著者である小林章さんは、日本人でありながらドイツのフォントベンダー「ライノタイプ」にて、タイプディレクターを務める人物。日本人で最も欧文に精通していると言っても過言ではない著者による解説は、 入門書でありながら非常に実用的です 。 従来の解説書にありがちだった、あまり実践では使わない知識(書体の細かな分類や、各パーツの名称など)を必要最小限にとどめ、 「こういう雰囲気を出したいときにはどの書体を選べば良いか」「記号類の本当に正しい使い方はなにか」 など、すぐに現場で役立つ知識が数多く掲載されています。 新卒デザイナーに限らず、フォントにちょっと詳しくなりたいディレクターさんやエンジニアさんも気軽に読める内容だと思います。続刊の『 欧文書体 2 』をあわせて読むのもおすすめです。   2.  タイポグラフィ・ハンドブック [ タイポグラフィ・ハンドブック ] 欧文書体・タイポグラフィの 専門書 として最適な一冊。 小林さんの『欧文書体』が入門書なら、こちらは豊富な情報量で手元にあると重宝する 辞書のような存在 です。 そういえばあの用語の意味はなんだっけ? この書体の歴史は? など、毎日使うわけではないけれど知らないと困る基礎〜応用の情報が数多く掲載されています。 欧文に関する情報はウェブでも簡単に入手可能ですが、情報が多すぎるがゆえに誤った情報も氾濫しています。デザイナーとして最高の「提案」を行うためにも、 常に正しい情報をインプットすること は重要です。 手元に置き、困ったときに見られるようにしておくのがおすすめです。   3.  Balance in Design [ Balance in Design ] プロポーションや構図など、美しいデザインのなかに必ず存在する 「数学的美しさ」 について解説された一冊。 ル・コルビジェの建築やバウハウスのポスターを例に挙げ、そのなかに黄金比や幾何学的な解析など 「数学的補助線」 が存在することを、半透明のシートを重ねて解説しています。 一般的にデザインは「感覚的なもの」と思われがちですが、歴史上でも著名なデザインを題材にすることで、 デザインが決して感覚的なものではない ということを分かりやすく示してくれています。 「目を引くようなデザイン」「斬新なデザイン」「オリジナリティのあるデザイン」を求める前に、まずは 美しさの基礎 を知ることはとても重要です。   4.  エモーショナル・デザイン [ エモーショナル・デザイン ] ノーマンと言えばアフォーダンスについて書かれた『誰のためのデザイン?』が有名ですが、内容がやや古いため2004年に書かれた近著であるこちらの方をおすすめします。 なぜ良いデザインは気持ちが良いのか? 数値だけでは決められない感情的なものとはなにか? など、プロダクトにおける 「認知」と「感情」 を中心に語られています。 製品やロボットを「インターフェース」と捉えると、ノーマンの語る「エモーショナル・デザイン」がUIにも通ずる思考法 であるということが良く分かります。 まずはとりあえず読んでみて、3年後、5年後にもまた読み返してみると新たな発見がある、味わいのある一冊です。   5.  デザイン思考が世界を変える [ デザイン思考が世界を変える ] デザインをするための技術ではなく、 「デザイン的思考」 について書かれた一冊。 著者は世界的に有名なデザイン・コンサルタント会社「IDEO」の代表ティム・ブラウン。 デザイン=ビジュアル的な成果物を連想しがちだが、会社における組織問題や社会問題など、「デザイン的思考」を用いることで多くのイノベーションを起こすことは可能です。 従来の「デザイン」から一歩外に出た思考法、「なにを?」ではなく「なぜ?」に応える姿勢の重要性 を教えてくれます。 ただ、考え方や思想を中心に語られており、具体例がやや不足気味。同じくIDEOについて書かれた『 The Art of Innovation 発想する会社 』をあわせて読むと、よりイメージが掴みやすくなると思います。   最後に 以上、新卒デザイナーにおすすめしたいデザイン本5選でした。 VASILYでは、豊富なデザイン知識とイノベーティブなデザイン思考で、UIにとどまらない問題解決をしてくれるデザイナーを絶賛募集中です! ご応募お待ちしております! https://www.wantedly.com/projects/5485 連絡先:info[at]vasily.jp
最近ではFASHION×ITという話題がネットや雑誌などで注目され、ファッションアプリも増えてきている印象を受けます。 ファッションアプリも、日々のコーディネートをサポートするアプリや、使わなくなったアイテムを売ることのできるものなど様々です。 今日はVASILYデザイナーチームが3月に インスピレーションを受けた ファッション アプリ をいくつかご紹介したいと思います。 1. MODA OPERANDI   モーダ・オペランディは、世界のトップ デザイナーズ ブランドのアクセサリー、ジュエリーなど 最新コレクションを、 ランウェイ後にプレオーダーできるファッションサービス のアプリです。 購入したい最新のコレクションの商品がある場合、 商品価格の50%を先払いし、残りを商品の生産が完了された次点で支払いをするという決済システム になっています。 コレクションの商品は高額なものが多いため、一度に支払うには負担が大きく、手が伸ばせない人も多いと思います。そんな人でも商品価格の半分を先に支払い、生産が完了するまでの間に残りの半分の金額を準備できる期間もあるので、コレクションアイテムを購入できる機会が増えると思います。 2. ASAP54 ASAP54はカメラはアイテムを写真に撮ることで その服がどこのブランドなのかなど詳細な情報を知ることができる アプリです。撮影したアイテムに該当するものがない場合は、そのアイテムと類似した複数のアイテムを表示してくれます。 例えば、テレビや雑誌でいいなと思っったら撮影するだけで、そのアイテムと同じもの、または類似したアイテムを提示し購入画面に繋げてくれるため、店舗に行く手間や、複数のECサイトで探す手間がなくなります。    3. 29CM 29CMは韓国にあるセレクトショップのアプリです。 このアプリは、ドイツレッドドットデザイン賞とIFデザインアワードウィナーを受賞しています。 アイテム写真のセンスが良く、コンテンツも豊富で、見ていて飽きないアプリです。   4. Canopy CanopyはAmazon.comから デザインの 優れた 商品を人の手で厳選し、カタログにしているプロダクト です。 キュレーションされている商品はどれも魅力的で、値段も手頃なものが多く、このデザインでこの値段なら欲しい!と思う商品ばかりです。また、アプリ内全体に「間」があり、商品画像をあまり大きく表示させないところなど、商品とアプリのリッチ感を引き出していると感じました。 5. Spring 以前、 こちらの記事 でもご紹介させていただきましたが、 Apple Design Award 2014にも選ばれている、NY発の新コマースファッションアプリ。Harper's BAZAARの ダウンロードするべきファッションアプリの記事 でも取り上げられていて、ファッション業界からも注目されています。 ユーザー発信のコンテンツをブランドが上手に活用するという手法を取り入れています。買い物本来の楽しさを味わってもらえるよう、あえてシンプルな構成になって、全体的に繊細で上品な印象のファッションアプリです。 ホームから直接注文でき、送り先住所などの入力画面も画面遷移やスクロールしなくとも、そのまま入力できるためシンプルに注文できるのがいいと感じました。  6. Threadflip Theredflipは 使わなくなったファッションアイテムを自由に売り買いができるフリマアプリ です。 自身で写真を撮って売ることもでき、申し込むと無料で梱包セットが届き、30点までまとめて出品することが可能で、 アイテムの撮影は運営側が撮影してくれる とのこと。また、自身で梱包し、必要な手続きの書類をまとめさえすれば、30点以上のアイテムを出品することも可能。 自身で出品する場合、アイテムの撮影の仕方によって見え方が異なり、商品価値を下げる場合もあることや、手間だったりするので代行して撮影して出品してくれるサービスはとてもいいと思いました。 7. ClosetSpace   ClosetSpaceはそれぞれのユーザーに合わせたコーディネートを、毎日提案してくれるファッションアプリです。主な特徴は 天気、気温、シチュエーションを細かく絞り込む ことができます。天気だけでのコーディネート提案はありますが、天気とシチュエーションで絞り込みができるのは、ユーザーごとでライフスタイルに違いがあるところで、それぞれのニーズにうまく応えられているコンテンツだと感じました。  最後に VASILYデザイナーチームがインスピレーションを受けたファッションアプリまとめはいかがでしたでしょうか。 iQONのUI、UXデザインをもっとよくしたい!デザインによってユーザーの課題を解決したい!というデザイナーの方々、絶賛募集中です! ご応募お待ちしております。 https://www.wantedly.com/projects/5485 連絡先:info@vasily.jp  
Functional Ruby
Modern Ruby: Functional Ghost in an imperative shell Ruby is a language designed in the following steps: take a simple lisp language (like one prior to CL). remove macros, s-expression. add simple object system (much simpler than CLOS). add blocks, inspired by higher order functions. add methods found in Smalltalk. add functionality found in Perl (in OO way). So, Ruby was a Lisp originally, in theory. Let's call it MatzLisp from now on. ;-) matz. Buzzwords such as "Functional Programming", "Curried Functions", "Partial Function Application" and "Lazy Evaluation" have been filling forms and tech blogs, mostly in a pure academic sense and also spoken about in the realm of purely functional languages. It turns out that ruby has its roots in functional programming, even blocks one of the most prominent features of ruby comes from higher order functions. Functional programming ideas can be implemented and exercised in non purely functional languages to produce better testable, readable and less error prone code. Purely functional languages don't own these ideas but merely borrow from them, they belong partially to lambda calculus a branch of mathematics. What ideas can be taken away from the realm of purely functional languages? As far as I know a consensus as to what functional programming is has not been reached in as strong a way that object oriented programming is defined by: Inheritance Encapsulation Objects may carry both state and behaviour Functional Programming has the qualities of: Immutability & Stateless Functions - No Surprises! Once a variable is defined its state may never change. (i.e only named constants, not variables) A function provided the same input will always produce the same output, no internal function state is allowed to change between calls. (The desired effect of having immutable variables.) Higher Order Functions - Functions in Functions out. Functions take functions as input and return new functions return as output. Partially applied functions: Lazy Evaluation - ...should really be called Programatic Procrastination. Values are calculated only when needed and not before. State v.s No State [ruby] #A function that carries external state def numberUniq!(inptLst) return inptLst.uniq!.length end #Stateless Implementation def numberUniq(inptLst) input = inptLst.dup.freeze return input.uniq.length end inputList = [1,1,1,2,3,3,3,3] puts "Input Before Execution:#{inputList}\n Function Output:#{numberUniq!(inputList)} Input After Execution:#{inputList}" puts "Input Before Execution:#{inputList}\n Function Output:#{numberUniq(inputList)} Input After Execution:#{inputList}" [/ruby] Analysis After running the above program the output was as follows: Input Before Execution:[1, 1, 1, 2, 3, 3, 3, 3] Function Output:3 Input After Execution:[1, 2, 3] Input Before Execution:[1, 1, 1, 2, 3, 3, 3, 3] Function Output:3 Input After Execution:[1, 1, 1, 2, 3, 3, 3, 3] In imperative programming there is no contract between the compiler and the author of the software about ensuring the output of a function will always be the same for the same input. If we call "numberUniq!" in conjunction with other functions that operate on the same array as input it can easily be seen that interference may easily occur. In the second implementation we can guarantee immutability by first making a duplicate and then calling freeze to virtually turn the value into a constant. By calling these two functions we can promise to the caller of the function that A: The function will not modify the input(.dup) and B: The same data passed to this function for N times will return the same result N times (.freeze). Higher Order Functions - Ground Zero Basics The concept of higher order functions is just as easy to grasp as functions that work on scalars and lists. First before getting into the discussion of what currying and partial function application are and how we can put them to work in everyday production code, lets get a mathematical definition of exactly what a function is. "A function f takes an input x, and returns a single output f(x). One metaphor describes the function as a "machine" or "black box" that for each input returns a corresponding output." - Wikipedia So the two requirements are that in order to be a function it must not only accept input but always return some form of output otherwise it is only a procedure or a block instructions. Currying and partial function application The process of currying is very simple, instead of one function that takes N arguments, a curried function is a series of N functions that take one argument each. For example if we define a function 'f' to be a function that takes four arguments and returns their sum: f (x, y, z, w) = x + y + z + w we define a function f{x,y,z,w} = f{ x + g {y + h{z + i {w}} } } In reality the curried function runs considerably slower compared to its simpler one dimensional implementation that takes N arguments since we now have four times the overhead of calling the function, but there is great merit to this approach. By writing our functions this way it allows us to reuse the curried definition of the function to easily define new variations (partial application) and for testing purposes we now have four smaller functions we can write tests for rather than one large function we need to cover. Partial Application of functions Say we wanted to write a function that multiplies a variable x by a constant K. We could write: [ruby] def mul8(x) x * 8 end [/ruby] But what if we wanted to define a bunch of functions like this? Do we need to formally write the definition each time? Partial function application to the rescue! [ruby] mulXY = lambda{|x,y| x * y} curriedMulXY = mulXY.curry 25 == curriedMulXY.(5).(5) #true mul3Y = curriedMulXY.(3) #returns Partially Applied Function equivalent to lambda{|y| 3 * y} mul9Y = curriedMulXY.(9) #returns Partially Applied Function equivalent to lambda{|y| 9 * y} [/ruby] This use of Proc#curry is more or less syntactic sugar that can be used in place of writing mulXY manually as: [ruby] def mulXY(x) Proc.new do |y| x * y end end [/ruby] The key idea to take away here is that inside the body of the proc we define x's value is fixed to whatever we initially passed in, so from a functional perspective calling mulXY(3) is nearly identical to writing the following proc directly: [ruby] Proc.new do |y| 3 * y end [/ruby] As you can see partial function application is really just another way to view writing closures and the process of currying simply transforms a single function that takes N parameters to N functions that take one parameter each successively calling the next. If a curried function is called with all of the parameters that it takes passed in, then the normal output of the function is returned. If that same curried function taking N values as input is called with less than N values the returned result is a function with its behaviour partially defined. Why hasn't Currying been adapted yet despite having been introduced since the release of Ruby 1.9? There are a few opinions as to why curried functions in ruby are still not in widespread use but after using Proc#curry it may appear a little obvious. Currying is an after thought, an addition to Proc that got merged into trunk later. You have to take a Proc and then run .curry on it which means a curried state is not a natural state for functions to exist in. Currying most likely isn't in production code for the main reason still being a non-standard way of doing things that many rubyists haven't touched yet for fear of being called out for splitting from the herd. I personally think this is a bad reason not to apply currying where it makes sense and hope this little article is enough to spark your interest. If you want to write ruby with our team please contact: info@vasily.jp WE ARE HIRING!! Till Next time, Nick
前回の記事 で、アプリデザイン効率化のためのツールのご紹介をさせていただきましたが、VASILYではその他にも Keynoteによるプロトタイピング を取り入れています。 プロトタイピングを効率的に進めるにあたって、どの段階でどのツールを取り入れるかはとても重要な判断になってきます。 Keynoteによるプロトタイピングは導入コストが低いこともあり、話題にあがっているひとつでもあるので試してみたところ、やはりメリットデメリットはありますが、取り入れる段階によってはとても効率的なツールの一つでした。 今回は、実際VASILYではKeynoteによるプロトタイピングを どういう過程で取り入れてみたか を(失敗も含めて...)ご紹介をしたいと思います。 1.企画段階はペーパープロトタイピングから まずはじめにVASILYではどのように進めているかというと、 アプリの改善を進める場合以下のような段階に分かれます。 デザイナーは要件定義の段階から加わって、KPIの設定や要素の洗い出しもエンジニアやハスラーとともに進めていき、プロジェクトメンバー内の共通意識や方向性にズレが無いように プロトタイピングによるアウトプットで可視化 していきます。 要件定義→アイディア拡散&収束→設計まではとにかくスピード重視で素早く回していくので、ペーパープロトタイピングでの簡単なラフから始まり、要素や遷移、使用感などを確認するときは Invision などのツールを使っています。 2.設計から実装依頼までのコミュニケーション 問題は、その先の 設計から実装までのコミュニケーションをどううまく回していくか が、デザイナーにとっての一番の肝です。 ペーパープロトタイピングだけではなく実際にビジュアルに起こし、意図やニュアンス、動きを表現し伝えていかなければなりません。 Keynoteによるプロトタイピングを取り入れることで、エンジニアやテストユーザー、プロジェクトメンバー全員が、より分かりやすい形で、企画やデザインの意図を理解してもらえるのではと思い立ったのがきっかけで、まず以下のようなかたちで実際に取り入れてみました。 3.Keynoteを3つのフェーズでとりいれてみた ①設計フェーズ:全体の流れを把握してもらうためメンバーに共有する(対 プロジェクトメンバー) -状況 チュートリアルの改善プロジェクト。ペーパープロトだけでなく、伝わりづらい部分もあるのでKeynoteを使って全体の流れや動きを共有したい -作成したプロトタイピング -結果 × Keynoteでは端末でのインタラクションは適用されず、決められた動きしか見せることが出来ません。手描きラフで別途遷移の説明が必要になるほどの伝わりづらさだったので、設計フェーズでのKeynoteプロトタイピングの導入はあまり効果的とは言えませんでした。 このフェーズだと、メンバーからフィードバックをいただき、更なる修正も加わる場合が多いので、ことさらペーパープロトタイピングでスピード感もって回していく方が最適だと思われます。   ②ユーザーヒアリングフェーズ:テストユーザーに実際の動きや流れを見てもらう(対 テストユーザー) -状況 同じくチュートリアルの改善プロジェクト。実際に使ってもらうユーザーなので、よりニュアンスの伝わるもので試してもらいたい -作成したプロトタイピングは①と同様 -結果 △ 実際の端末で確認が出来て、より明確な指摘をもらえたことに対しては○と言えますが、やはり②と同様、決められた動きしか見せられないことや、説明を交えながら何度も繰り返し見てもらわないと状況が把握できずで、テストユーザーにとってかなりのストレスであったと言えます。 テストユーザーに触ってもらうには、ビジュアルを作成しInvisionで遷移確認のためのモックを作成するか、インタラクション型プロトタイプをつくれる Pixate や Framer などの方が完成度高いので、より良質なフィードバックをもらえると思います。(後者はデザイナーにとってはかなり習得コストかかりますが...)   ③実装フェーズ:画面スクロール時のアニメーションをエンジニアに伝える -状況 ある画面のデザインは完成したが、複雑な動きが存在するのでエンジニアにアニメーション実装を依頼したい -作成したプロトタイピング -結果 ○ 今までは、デザイナーが「スクロール時にユーザー情報がウィーンって左上に縮小して、タブもある一定の場所でFIXしつつ、タブ以下の情報はスルスル潜り込みながらスクロールしていく」というような擬態語で表現したり、類似したアプリを参考に身振り手振り伝えても伝わりきれなかった動きが、一瞬でエンジニアに理解してもらえました。 デザイナーもエンジニアにとっても完成図を共有しやすく意図が伝わりやすいので、普段の指示書とデザインデータにプラスして少しのひと手間加えてあげることで、実装スピードが一段と上がります。   まとめ 今回は、Keynoteプロトタイピングの効率的な取り入れ方を、事例を含めて紹介しました。 まとめると、 ①Keynoteプロトタイピングが一番活躍するのは実装フェーズ ②一定の動きしか見せられないので、遷移や使用感をテストするには不向き ③ペーパープロト+Keynoteプロトのセットでよりスピード感ある開発を実現できる という結果になり、開発の最終段階でコミュニケーションを円滑にする一つの手段として取り入れるにはとてもおすすめのツールでした。 ツールが色々あるのは知っているけれども、実際取り入れてみるとなるとどこでどう使ったら良いのかと模索状態でしたが、失敗することで課題も見えて次に試す術が見つかるので、様々なツールを試す価値は存分にあると思います。 デザイナーの役割によって、アプリ開発でのスピードは段違いに変わるので、もちろん個々人の経験やコミュニケーション力、知識力の差で変わることもありますが、その差を補うためにも ツールを積極的に取り入れていくことはデザイナーとしての成長を加速させる一つ になります。 VASILYでもまだまだ模索段階なので、一緒により良い開発の進め方を研究していける方を募集していますので、以下よりご応募お待ちしております。 https://www.wantedly.com/projects/5485 連絡先:info at vasily.jp
VASILYでインターンをしている茨木です。 3月9日に発表されたGoogle Chromeの最新ベータ版v42.0.2311.22でPush APIがサポートされニュースになりました。ブラウザからスマホにプッシュ通知が送れるというものです。 スマホ向けウェブサービスをやっている方ならみんな気になるこの機能、早速試してみました。かなりシンプルなので皆さんもぜひ触ってみてください。 以下のサンプルやサイトを参考にしました。 サイト http://updates.html5rocks.com/2015/03/push-notificatons-on-the-open-web サンプルプログラム https://github.com/GoogleChrome/samples/tree/gh-pages/push-messaging-and-notifications 実行環境 Windows8.1 (64bit) Google Chrome 43.0.2327.5 dev-m (64-bit) 必要なもの SSLサーバー(github.ioなど) バージョン42以上のGoogle Chrome (私は http://getgooglechromeoffline.net/tag/google-chrome-42-download/ から入手しました) 導入手順 Google Developer Consoleでのプロジェクトの作成 今回のサンプルではPush通知にGoogleのAPIであるGCM(Google Cloud Messaging)を使うので、まずGoogle Developer Console ( https://console.developers.google.com/project )でプロジェクトを作成します。画面にある「プロジェクト作成」をクリックするとモーダルウィンドウが出てくるので、「プロジェクト名」に好きなプロジェクト名を入力、「~利用規約を~同意」にチェックをしてください。プロジェクトIDはそのままで結構です。入力後、「作成」クリックするとプロジェクトが作成されます。 プロジェクトを作成すると画面上部にプロジェクトIDとプロジェクト番号が表示されます。今回使うのは「プロジェクト番号」ですので、これをメモしておきます。 APIのkeyの作成 左のメニューで「APIと認証」→「API」と選択します。そうするとAPIの一覧が出てくるので、「Google Cloud Messaging for Android」と「Google Cloud Messaging for Chrome」をONにします。 次に左のメニューで「APIと認証」→「認証情報」と選択し、「新しいキーを作成」をクリックしてください。モーダルウィンドウが出てきたら「サーバー キー」を選択します(間違えると動かないので注意してください)。 次に、許可対象IPアドレスを入力するテキストボックスが出てきますが、入力しなくても大丈夫です。モーダルウィンドウ下の「作成」をクリックすると、keyが生成されます。 Keyを生成すると表が出てくるので、その中の「APIキー」をメモしておきます。 サンプルプログラムの設定 アップロード サンプルプログラムは先ほど取得した「プロジェクト番号」と「APIキー」を設定するだけで動かすことができます。 main.jsの3行目の <YOUR API KEY> にメモしたAPIキーを入力します。 'use strict' ; var API_KEY = '<YOUR API KEY>' ; var curlCommandDiv = document .querySelector( '.js-curl-command' ); var isPushEnabled = false ; manifest.jsonの10行目の <YOUR PROJECT NUMBER> にメモしたプロジェクト番号を入力します。 設定が終了したら、用意したSSLサーバー(github.ioなど)に https://github.com/GoogleChrome/samples/tree/gh-pages/push-messaging-and-notifications 内のファイルをアップロードします。 プッシュ通知を登録 index.htmlにアクセスすると、問題なければ「Enable Push Messages」ボタンが有効になっており、これをクリックすると「cURL Command to Send Push」の下にコマンドが出てきます。これがPush通知のPOST送信用のコマンドになるのでコピーしておきます。結構長いですが、これで一つのコマンドです。上手く行かない場合は、ブラウザのバージョンを確認してください。 コピーしたコマンドをコマンドラインに貼り付け、実行します。成功した場合には { "multicast_id" :<数字>, "success" :1, "failure" :0, "canonical_ids" :0, "results" : [{ "message_id" : "<数字>" }]} と表示され右下にプッシュ通知が表示されます。 Androidの場合も、端末のChromeから同様の手順でコマンドを取得するところまで行ってください。それから取得したコマンドをPCに移して実行することでPush通知を実現できます。Androidの場合もやはりGoogle Chromeのバージョンは42以降が必要ですので、注意して下さい。 簡単な説明 main.jsはロード時、serviceWorker.registerでサーバー上のservice-worker.jsをService Workerとして登録します。そして、ボタンが押された場合に関数subscribe内のserviceWorkerRegistration.pushManager.subscribe()でsubscriptionの登録を行い、そのsubscriptionとAPI Keyを用いてcURLのコマンドを生成しています。 あとは、サーバー上でcURLを実行してpushサーバにPOST送信することで、登録を行った端末にPush通知が送られます。 問題点 現状ではPOST送信する側からメッセージなどのデータを送れず、service-worker.js内に予め書いたstaticなメッセージしか通知できません。仕様にはデータの送信も含まれているので、今後の実装が期待されます。 Google Cloud Messaging for Chromeは個人の場合10000リクエスト/日の制限があります。 備考 このサンプルではindex.htmlでAPIkeyの設定が必要ですが、それはコマンドの生成の為であり、クライアントサイドへのAPIkeyの記述は本来不要です subscriptionIdは署名が行われる毎(「Enable Push Messages」のクリック毎)に発行されます。当然クライアントごとに異なります。 サーバーサイドでservice-worker.jsを書き換えても、クライアントサイドのservice-worker.jsにはすぐには反映されません。ですので、service-worker.js を書き換えてメッセージを更新、といった使い方は難しいです。 VASILYではインターン生を募集しています。 ちょっとでも気になった方は是非一度来てみてください! https://fresh2016.vasily.jp/
はじめに iQONではアニメーションなどのアプリとしての演出の部分にこだわりを持っています。 突然ですが皆さんiQONでコーディネート画像をタップしたことはありますか? 実際のアニメーションの動き こんな感じでコーディネートに含まれる商品がバラバラと広がって行くアニメーションを実装しています。 今回はこのアニメーションの裏側をAndroidアプリでの実装を例に少しご紹介させて頂きたいと思います。 まだこの動きを見たことがない方はダウンロードして確認してみてくださいね! iOSアプリ Androidアプリ 仕組みの概要 構成 一枚の画像が分解されて商品(以下アイテムと表現します)画像に分解されているように見えますが、実は少し違います。 最初にユーザの目に入るコーディネートのサムネイルは一枚のImageViewで実装していますが、その下に分解後の各アイテム画像のImageViewを含むRelativeLayoutが用意されていて、その中で各アイテムのImageViewがアニメーションするという構成になっています。   ユーザの操作と分解の動作 ユーザがどんな操作をして、分解が動作して行くかをもう少し細かくみてみましょう。 1.コーディネートのサムネイルをタップ 2.コーディネートのサムネイルをGONE 3.下のRelativeLayout内でアイテム画像のImageViewがアニメーション開始 4.分解後に☓ボタンを押して元に戻す 5.元の位置に戻るアニメーション開始 6.アニメーション完了後コーディネートのサムネイルをVISIBLE ユーザがタップする度にこの1〜6を繰り返して分解したり、もとに戻したりしています 実装の紹介 ではもう少し具体的な実装の内側を大きく2つのステップに分けて紹介していきたいと思います。 1. 初期配置とバラバラにする時の移動先の決定 2. バラバラにするアニメーションと元に戻るアニメーション 初期配置とバラバラにした時の移動先の決定 コーディネートのサムネイルがユーザに見えている時には、以下のようなアイテムの配置情報(layout)をサーバから取得して、コーディネートのサムネイルと同じ位置関係でアイテムのImageViewが配置されています。 余談ですが、もちろんコーディネートのサムネイルもこのlayout情報をもとにサーバサイドで生成され、画像サーバにアップロードされています。 layouts:[ { item_id: 4249463,// アイテムID index: 1,// Z-index x: 1.0356848767906093,// X座標 y: 0.9997999119799084,// Y座標 width: 238,// 横幅のサイズ height: 194,// 縦幅のサイズ }, { item_id: 4249463, index: 2, x: 240.99108292274144, y: 0, width: 238, height: 194, },... ] サーバから上記のような情報を取得して各アイテム画像のImageViewを配置する時点で、アニメーションで移動する先も決定しています。 コーディネートのサムネイルと同じサイズの正方形を4×4に分割したセルが移動先だと思ってもらえれば大丈夫です。 移動先のイメージは以下のような感じです。   コードの一部を紹介すると以下のような感じになります。 class SplitItem { private ImageView imageView; private String itemId; private int index; // 移動する前の位置とサイズ private float orgWidth, orgHeight; private float orgX, orgY; // 移動先の位置とサイズ private float dstWidth, dstHeight; private float dstX, dstY; public SplitItem(JSONObject layout) { itemId = layout.getString("item_id"); orgWidth = layout.getDouble("width"); orgHeight = layout.getDouble("height"); orgX = layout.getDouble("x"); orgY = layout.getDouble("y"); index = layout.getInt("index"); // 移動先の情報を算出 initDstData(); } // 移動先の情報を算出するメソッド private void initDstData() { // 4*4のセルに分割した時の移動先のサイズと座標を算出 int cellLength = thumbnailWidth / 4; int xIndex = index % 4; int yIndex = index / 4; float sizeRatio; if (orgHeight > orgWidth) { sizeRatio = cellLength / orgHeight; dstHeight = cellLength; dstWidth = orgWidth * sizeRatio; } else { sizeRatio = cellLength / orgWidth; dstHeight = orgHeight * sizeRatio; dstWidth = cellLength; } dstX = cellLength * xIndex + (cellLength - dstWidth) / 2; dstY = cellLength * yIndex + (cellLength - dstHeight) / 2; } public void open() { // 初期位置から移動先の位置にアニメーションする処理(後述) } public void open() { // 移動先の位置から初期位置にアニメーションする処理(後述) } } バラバラにするアニメーションと元に戻るアニメーション 初期配置情報とアニメーションで動く先が決まったので、実際にコーディネートの画像をバラバラにするアニメーションを紹介していきます。 private ArrayList items; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); items = new ArrayList (); // サーバから返却されるJSONのlayoutsから各アイテムを初期化する処理 JSONArray layouts = coordinate.getLayoutJSONArray(); for (int i = 0; i < layouts.length(); i++) { JSONObject layout = layouts.getJSONObject(i); SplitItem splitItem = new SplitItem(layout); items.add(splitItem); } // 省略 // サムネイルのタップイベントの設定 thumbnailImageView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { v.setVisiblity(View.GONE); for (SplitItem item : items) { item.open(); // 各アイテムを移動先へアニメーションする } } }); // 一度バラバラに移動したアイテム画像を元に戻すボタン closeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { for (SplitItem item : items) { item.close(); //各アイテムを移動元へアニメーションする } } }); } バラバラにするアニメーション class SplitItem { // open時のアニメーションの実行 public void open() { PropertyValuesHolder holderX = PropertyValuesHolder.ofFloat("translationX", 0f, dstX - orgX); PropertyValuesHolder holderY = PropertyValuesHolder.ofFloat("translationY", 0f, dstY - orgY); PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1f, dstWidth / orgWidth); PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1f, dstHeight / orgHeight); ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(imageView, holderX, holderY, scaleX, scaleY); objectAnimator.setInterpolator(new DecelerateInterpolator()); objectAnimator.setDuration(duration); objectAnimator.start(); } } 上記のようにObjectAnnimatorを使って、移動先のセルに向かってサイズを変えながら移動するというアニメーションを実装しています。 元に戻る時のアニメーション 移動した後に☓ボタンを押せば元の位置に戻ります。もとに戻るときのコードもご紹介します。 class SplitItem { // close時のアニメーションの実行 public void close() { PropertyValuesHolder holderX = PropertyValuesHolder.ofFloat("translationX", dstX - orgX, 0f); PropertyValuesHolder holderY = PropertyValuesHolder.ofFloat("translationY", dstY - orgY, 0f); PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", dstWidth / orgWidth, 1f); PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", dstWidth / orgWidth, 1f); ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(imageView, holderX, holderY, scaleX, scaleY); objectAnimator.setDuration(duration); objectAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { // コーディネートのサムネイルを表示VisiblityをVISIBLEにする処理(今回は省略) } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); objectAnimator.start(); } } 先ほどのopenメソッドとは反対の処理ですが、ポイントはAnimationListenerのonAnimationEndの部分です。今回は省略しますがアニメーションが終了したらGONEにしていたサムネイルのvisiblityをVISIBLEにもどして最初にユーザ見ていた状態に戻す処理を実装しています まとめ 配置情報の管理はチームで協力 この機能の肝とも言える部分ですが、ユーザが作ってくれたコーディネートをどんなフォーマットでデータ管理するかをバックエンドチームと協力してDBで管理しています。 アニメーションの処理自体はそこまで複雑ではない ご紹介したとおり配置情報の仕樣さえしっかりチームで共有できてれば、アニメーションの処理自体はそこまで複雑ではありません 実際に大変なところ 今回はかなり省略しましたが、実際には画像をネットワーク越しに取得してImageViewに設定しなければなりません。 そうなるとListViewなどで同じ表現を実装する場合、スクロールをスムーズにしてもらうためにはアイテム一つ一つの画像の処理を相当工夫する必要があります。(1つのコーディネートにつきアイテムが約15個あるため全部、その都度素直に処理してしまうと高速にスクロールされると大変なことになります) このあたりぜひ聞いてみたいという方いらっしゃいましたら気軽にオフィスに遊びにきてください。 大変だった点、この機能の誕生の裏話などたくさんご用意してお待ちしております。 最後に ユーザがつい押したくなってしまうような表現を求めてアプリ、バックエンドに関わらずチームで実装の壁を超えて日々挑戦しております。 アイデアをどんどん形にしてユーザに届けたい方、ユーザにワクワクしてもらえるようなアプリを一緒に作りましょう。 興味のある方はこちらでお待ちしております! https://www.wantedly.com/projects/7595  
プロダクト開発においてスピードも重視するVASILYでは、 効率的・効果的に デザインを行えるよう様々なツールを活用しています。 今回は、これまで使ってきた中で オススメのアプリやサービス をいくつかご紹介したいと思います。 プロトタイピングをつくる アプリ制作は、 - どこからどこへ遷移するのか - どのようなアクション・アニメーションで遷移するのか など、実際に動いているものを検討してみないとわからないケースが多いものです。 実装後、「やっぱこれって変じゃない?」とならないためにも、VASILYでは プロトタイピングでの確認・検討を必ず行う ようにしています。    POP https://popapp.in/jp/ [iPhone / Android] ※2015年3月12日現在 POP(Prototyping on Paper)という名前からもわかる通り、 ペーパープロトタイピング に特化したアプリです。紙に書いた手書きワイヤーを「スマホで撮影」「リンクを設定」するだけで、画面遷移をするモックが簡単につくれます。 プロト制作に必要な最低限の機能で構成されているので、 アプリデザインの初期段階 や、 はじめてペーパープロトに挑戦したい方 、 ノンデザイナーの方 にもオススメです。   Prott   https://prottapp.com/ja/ [iPhone] ※2015年3月12日現在 基本機能はPOPと同じですが、 機能やUIをさらにリッチ にしたアプリです。 リンクや画面遷移の指定がより簡単で直感的に行え、アクションやアニメーションの種類もPOPよりやや豊富。プレビューの際にステータスバーの色が変えられるのも、かゆいところに手が届いている感じでグッドです。 また、Slackとの連携も可能なので、社内でSlackを導入している場合はより効率的な開発が可能になるかと思います。 ただ、2プロジェクト以上つくる場合は有料版への登録が必要なので、 ノンデザイナー含め複数人でプロトタイプを制作 したり、Slackなど 外部サービスとも連携させたい 場合にオススメです。    InVision http://www.invisionapp.com/ [iPhone / Android] ※2015年3月12日現在 ペーパープロトからちょっと進んで「 ラフデザインで動きも確認したい 」というときには、InVisionがオススメです。 POPやProttと同様のプロト制作機能を備えつつ、 縦スクロール可能なモックもつくれる ため、 より実際の使用感を意識したプロトタイプ がつくれます。もちろん、ペーパープロトをつくるだけでもまったく問題はありません。 ペーパープロト〜ラフデザインという 幅広い制作段階で使用できる ことから、VASILYデザインチームではInVisionでのプロトタイプ制作を積極的に取り入れています。     デザインを手軽に実機で確認する 実際のデザイン制作を進めていくと、マージンや文字の可読性など、どうしてもスマホで実機確認をしたくなる場面がやってきます。 画像を書き出してメールに送ったり、あるいはサーバにアップしたりと、スマホでデザインを確認するのはなかなか大変な作業です。 そんなときは、Macの画面をiPhone上に リアルタイムプレビュー してくれるアプリがオススメです。   Skala Preview http://bjango.com/mac/skalapreview/ [iPhone / Android] ※2015年3月12日現在 まずは、MacとiPhoneの両方に専用アプリをダウンロード。同じLAN内に接続することで、 PhotoshopのキャンバスをリアルタイムにiPhone上で見る ことができます。 もちろん修正もリアルタイムで反映されるため、細かな微調整を実機で確認しながら行えます。 都度書き出して、サーバにアップして、実機で確認…というわずらわしさをカットできるので、作業効率がぐっと上がります。   Live View [iPhone] ※2015年3月12日現在 Skala PreviewがPhotoshopとのリアルタイムリンクなのに対し、Live Viewは PCに表示されている画面ならどこでもリンク させることが可能です。 デザインはIllustratorじゃなきゃ。という方にはこちらのアプリがオススメです。   アプリのモーションを録画する デザインをするにあたり、みなさんも参考になりそうなアプリを日々キャッチアップしているかと思います。このメニューの表示の仕方が素敵だなとか、スクロール時のぬるぬるした動きが気持ち良いなとか。素敵な表現はぜひとも参考にしたいですよね。 とはいえ、アプリの細かな動きを言葉で表現するのはなかなか難しいものです。そんなときは、 実際にモーションを録画 してしまうほうが早いかもしれません。    Reflector [iPhone] ※2015年3月12日現在 まずはMacにReflectorをインストールし、iPhoneをAirPlayでミラーリング。すると、iPhoneで表示されている画面が、Macでも同時に見えるようになります。iPhone内の動きや音もきちんと同期されるので、 Mac上でリアルタイムに録画 することが可能になります。 動画はMP4形式で保存されるので、チーム内で情報共有をしたい場合はもちろん、Evernoteに蓄積して自分専用のモーション・アーカイブをつくるのもオススメです。    Telecine https://play.google.com/store/apps/details?id=com.jakewharton.telecine [Android] ※2015年3月12日現在 Androidアプリのモーションを録画したいという場合には、Telecineがオススメです。 ReflectorのようにPCをミラーリングデバイスとして使用する必要がなく、 録画・保存までAndroid内で手軽に行える ことが大きなポイントです。 ただし、Android 5.0以上でないと使えないのでご注意ください。   最後に 以上、VASILYデザイナーチームが選ぶ、アプリデザインを効率的に行うためのツール紹介でした。 VASILYではこのようなツールを最大限に活用し、 スピーディなプロダクト開発に貢献してくれるデザイナー を絶賛募集中です! https://www.wantedly.com/projects/5485 連絡先:info[at]vasily.jp