TECH PLAY

株式会社ZOZO

株式会社ZOZO の技術ブログ

980

  iQONはAppStoreのレビュー4.5, GooglePlayのレビュー4.3と、嬉しいことにユーザーから高い評価を受けています。しかし、実際にユーザーが日々感じているアプリの良い点だったり不満点などの本音の部分は、レビューやTwitterなどのユーザーが投稿する文章の中に含まれています。特にサポートにお問い合わせをしてくださったユーザーからいいただくような改善を訴えるメッセージとは違い、Twitterやレビューに投稿している文章には現状ユーザーが感じている改善してほしい点やバグ等が、より意識せずに書かれている可能性が高いと考えられます。そのテキストの内容を解析できれば、開発者が認知している問題の中でもより多くのユーザーが改善してほしいと感じている点であったり、我々開発者が気づけていなかったバグの発見等が可能になると考えられます。 そこで今回、「評判分析はじめの一歩」と題して、iQONについて書かれている文章の評判分析を実験的におこなってみましたので、その内容と結果を紹介したいと思います。 評判分析とは?   評判分析とは様々なテキスト情報をテキストマイニングや機械学習の技術を用いて、肯定的な評価をしている記述と否定的な評価をしている記述に分類したり、実際にそのテキストを書いている人がどのような事を考えて記述しているか分析し、理解する事を言います。 ブログやSNSの発展や、AmazonなどのECサイトのレビュー機能が充実した現在、評判分析はユーザーの生の声を把握する事ができると期待されているため、研究分野でも盛り上がりを見せています。 実験概要 対象データ ・書かれている内容の精査等にコストをかけないため。 ・すでに評価の点数が振られている点。 評判分析とは様々なテキスト情報をテキストマイニングや機械学習の技術を用いて、肯定的な評価をしている記述と否定的な評価をしている記述などに分類したり、実際にそのテキストを書いている人がどのような事を考えて記述しているか分析し、理解する事を言います。 ブログやSNSの発展や、AmazonなどのECサイトのレビュー機能が充実した現在、評判分析はユーザーの生の声を把握する事ができると期待されているため、研究分野でも盛り上がりを見せています。 mattlawer/appstat の様なコマンドラインからアプリのapp_idを指定してレビューを取得する便利なサービスもあるのですが、今回は200件以上を取得したかったので、AppleのAPIのレスポンスをスクレイピングする形で取得しました。 評判分析ツール 「独立行政法人情報通信研究機構 旧知識処理グループ 情報信頼性プロジェクト」で作られているツールを使用して分析を行いました。(詳細はこちら :  http://alaginrc.nict.go.jp/opinion/ ) こちらは1行につき1文が書かれたテキストファイルを入力すると、① その評価情報を表す表現の抽出② その評価情報の意味的な分類③ ポジティブ / ネガティブ の様なその文章が表す「評価」に関する情報を抽出してくれるスクリプト群です。 以下のものが動く環境で、こちらのツールを使用する事ができます。 ・CRF++(Version 0.54 で動作確認) ・iconv (Version 2.5で動作確認) ・gawk(Version 3.1.6 および 4.0.0で動作確認。3.1.5ではエラーが発生します) ・gcc(Version 4.1.2で動作確認) ・perl(Version 5.8.8 および 5.10.1で動作確認) ・JUMAN(Version 6.0 および 6.01で動作確認。Version 7.0には対応していません。) ・KNP(Version 3.01 で動作確認。Version 4.0には対応していません。) 分析結果として表示される、評価タイプの種類と各タイプが持ちうる極性は以下の表の様になります。(+はポジティブ、-はネガティブを表します) 実験内容 ・スクレイピングで取得したレビュー300件を取得 ・アプリのバージョン × レビューの点数 ごとにファイルを分割 ・レビューの点数ごと/バージョン別に評価分析の結果をまとめる 取得した文章は一つのレビューの中で肯定的な意見と否定的な意見を混ぜて記載しているものが存在する理由から、分析スクリプトに流してもどのような意見を述べているか判定できない可能性が高いため、文ごとに区切って分析を行いました。 実験結果 全体の評価内容数 感情+        : 6 感情-         : 0 批評+        : 200 批評-         : 77 メリット+ : 150 メリット-  : 56 採否+         : 8 採否-          : 0 出来事+     : 0 出来事-      : 0 当為           : 28 要望           : 0 レビューという事もあり、「批評+-」「メリット+-」に関する意見が多い結果となりました。また、「当為」が多くレビューに書かれている事からアプリに何を求めているかを知る事ができそうです。 レビュー点数ごとの評価内容 レビュー5のコメント内で 評価内容 レビュー5のユーザーのコメントとしてはポジティブな意見が多く、特に ・コーデを作ることの楽しさ / 可愛さ ・買い物に便利 ・探していたものが見つかる 等の意見を多くいただきました。 レビュー1のコメント内で 評価内容 レビュー1をいただいたユーザーのコメントにはやはりマイナスな意見が多く存在しています。今回試してみたツールの結果のままでは、マイナスな意見に関してもいくつかプラスの意見として判定されてしまっているモノがありました。 大きく分けると バグ報告系 と 改善要望系 の2種類に分けられます。こちらが認知して今後改善を進めて行くバグや機能改修についても多くの意見をいただいていますが、「機能改善をする事自体に対する不満のご意見」がある事もわかりました。 最新バージョンのレビューでマイナス評価のもの 一番直近のレビューの中でマイナスの評価になったものとしては上記のお知らせに関してですが、お知らせを消す仕組みは現在のアプリにも実装されているので、そちらがユーザーにきちんと伝わっていない事がわかります。   結果からの考察と今後の話 今回は特に分析に関してカスタマイズ等をしていないので、評判分析の精度を上げる事には言及していませんが、ユーザーが投稿している文書を解析する事で ・ ユーザーが何に喜んでいるかがわかる ・ ユーザーが何に不満を感じているかがわかる 普段見逃してしまっているレビューの中にあるユーザーの意思をつかんでサービスに落とし込む事は確実にメリットがあります。 他にも、私たち開発者が気づけていないバグについて把握ができ、ユーザーがどこの機能を楽しんで、自分たちのアプリを使ってくれているか、自分たちのアプリの強みを知る事にもつながります。 今後進めたいこと 辞書/分析アルゴリズムの最適化 今回は「評判分析導入のはじめの一歩」という事で、分析ツールに特に学習データを与えて学習させたり、辞書をアプリをテキストマイニングするのに最適化するようなことはせず、一般的な評判分析用の辞書を使っています。 そのため、プラス/マイナスの評価の部分などでおかしいものが多々ありました。 ・分析がおかしい例 このように人間がみれば一発でマイナスの感情な意見でも、プラスな評価に判定されてしまっている解析結果が多々あります。今後はアプリの批評に関する言葉を学習させて辞書に追加していくなどして、より精度を上げた評判分析ができれば、アプリの改善等にプラスになるので進めて行きたいと考えています。 冒頭でもテキストマイニングを用いた評判分析の分野は研究で盛り上がっていると記載しましたが、調べている中で次に試せそうな面白い研究があったのでいくつかありました。ぜひ参考にしてみてください。 レビューの語の重みを考慮したテキストマイニングによるゲームソフトの評判分析   構文片を用いた意見・評判情報抽出手法 ツールによるビジュアライズ   今回はローカルのバッチを単発で回して処理をし、テキストファイルをアウトプットとしていました。今後は ・定期的に最新のレビューの分析結果を更新 ・ ツールでエンジニア以外も見れるように ・各アプリのバージョンごとでレビューにどのような変化が現れているかを見える化。 ・ レビューだけで無くTwitterなどに投稿されている文章も解析する ・プラス/マイナスそれぞれの評価の中で頻出な単語を形態素解析で分解しツールで見れるようにし、ユーザーが何かしらの媒体にポストしている内容でホットなワードは何かを見える化 等を進める事で、日々ユーザーに向けた改善をとれるような体制を作りたいと考えています。   最後に 今回はレビューに関する分析の実験内容をあげましたが、レビューやTwitter以外にも弊社アプリには様々なテキストデータや画像データがあり、ファッションに関するデータという意味では日本最大級のデータがたまっています。VASILYでは現在、これらのデータを使って今迄にない新しい価値を世の中に生み出して行きたいというエンジニアを大募集しています!ぜひ一緒にテクノロジーの力でファッション業界にインパクトを与えてきましょう! 募集要項 連絡先:info[at]vasily.jp
iQONのiOSアプリはまだ全てObjective-Cで記述されています。 Swiftへの移行については「たいしてパフォーマンスが上がるわけでもないし…」と思って渋っていました。 そんな中、オフィスの移転をきっかけに来客の受付システムをiPadアプリで作ることになりました。 スクラッチでアプリを作るのならSwiftで、ということでSwiftで作りました。 今回は、受付システムの社員を呼び出すデータ通信と、トップページの時計に使ったCADisplayLink実装を紹介します。 完成品 www.youtube.com 呼び出したい社員を選択すると、twilioから各個人の携帯電話に自動音声の電話がかかってきます。 電話呼び出しと同時にSlackにも通知が飛ぶようになっています。 実装 データの流れは下記のようになっています。 アプリからはherokuのAPIをリクエストするだけなので、エラーハンドリングも簡単に済みました。 データ通信 iPadから呼び出したい社員を選択すると、アプリからHerokuのAPIにリクエスト HerokuのAPIがSlackとtwilioにリクエスト 社員のケータイに電話(自動音声)とSlackの通知が飛びます アプリの実装 ネイティブ側では下記のようなことをやっています。 ・ Swift製通信ライブラリのAlamofireを使用 ・ CAGradientLayerでiPhoneのロック解除のようなシマーアニメーションを実装 ・ 時計のアニメーションをCADisplayLinkを使って正確に描画 ・ UICollectionViewFlowLayoutをオーバーライドして、セル追加アニメーションを実装 ・ 呼び出しリクエスト中にAudioToolbox.frameworkで効果音を無限ループさせる ・ 通信中の波形アニメーションにCADisplayLinkを使ってアニメーション ・ アプリアイコンは基本的に見せないので、identiconでデザイン工数ゼロ その中の一つ、今回はCADisplayLinkの実装を紹介します。 時計のアニメーションとCADisplayLink アナログ時計のような無限に動き続けるアニメーションを実装するとき、NSTimerやdispatch_afterを使って0.01秒ごとに位置を修正する処理を実行するような実装が考えられます。 受付システムの時計のアニメーションの実装には、CADisplayLinkを使用しています。 CADisplayLinkは画面のリフレッシュレートと同期して描画させるタイマーオブジェクトです。 vs NSTimer NSTimerを使ったり、dispatch_afterをループしても同じような処理を実装できます。 例えば、NSTimerのインターバルを1秒に設定して、その処理の実行時間が1.5秒だった場合、処理実行中は次のタイマー処理がスキップされ、処理が実行されるのは2秒間隔になってしまいます。(いわゆるフレームスキップ) これはCADisplayLinkでも同じことです。CADisplayLinkの場合でもスキップはありますが、あくまでも呼び出されるタイミングは画面の更新に同期するのでアニメーションを使うには効率が良いのです。 CADisplayLinkの実装 CADisplayLinkオブジェクトを生成時にターゲットとメソッド名を登録します。 生成したCADisplayLinkオブジェクトをNSRunLoopのメインループに追加すると、画面描画のリフレッシュごとに登録したメソッドが呼ばれます。 let displayLink = CADisplayLink(target : self , selector : Selector ( "update:" )) displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode : NSRunLoopCommonModes ) 円を描く実装 実装の一部を一つのViewControllerにまとめました。 GitHubにサンプルプロジェクト を置いてあるので動かしてみてください。 こんなアニメーションが動きます。 import UIKit class ViewController : UIViewController { private let secondLayer = CAShapeLayer() override func viewDidLoad () { super .viewDidLoad() // 円のレイヤー let frame = view.frame let path = UIBezierPath() path.addArcWithCenter( CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame)), radius : frame.width / 2.0 - 20.0 , startAngle : CGFloat ( - M_PI_2), endAngle : CGFloat (M_PI + M_PI_2), clockwise : true ) secondLayer.path = path.CGPath secondLayer.strokeColor = UIColor.blackColor().CGColor secondLayer.fillColor = UIColor.clearColor().CGColor secondLayer.speed = 0.0 // ※1 view.layer.addSublayer(secondLayer) // 円を描くアニメーション let animation = CABasicAnimation(keyPath : "strokeEnd" ) animation.fromValue = 0.0 animation.toValue = 1.0 animation.duration = 60.0 secondLayer.addAnimation(animation, forKey : "strokeCircle" ) // CADisplayLink設定 let displayLink = CADisplayLink(target: self, selector: Selector("update:")) displayLink.frameInterval = 1.0 // ※2 displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode : NSRunLoopCommonModes ) } func update (displayLink : CADisplayLink ) { // timeOffsetに現在時刻の秒数を設 let time = NSDate().timeIntervalSince1970 let seconds = floor(time) % 60 let milliseconds = time - floor(time) secondLayer.timeOffset = seconds + milliseconds // ※3 } } secondLayer.speed = 0.0 CALayer のspeedを0にして、自動でアニメーションが動かないようにする displayLink.frameInterval = 1.0 1フレームごとに処理を実行する secondLayer.timeOffset = seconds + milliseconds アニメーションの進捗具合を設定する CALayerの speed = 0.0 の状態で、 timeOffset を操作しているので、円が一周しても、また最初から描画が始まって無限に動き続けます。 Swiftで書いてみて思ったこと メリット コード量が減る    .h/.m → .swift 一つになります    型推論 Objective-Cでの不便なことが改善    文字列の扱い、Switch、enum Optional Value (?, !)   ビルドせずに静的解析でバグに気付くことができます iOSエンジニアに以外の可読性向上   Objective-Cの独特な記法がなくなって、iOSエンジニア以外でもなんとなく理解できるぐらい読みやすくなりました これがきっかけでAndroidエンジニアがplaygroundを触るようになりました 普段アプリをObjective-Cで作っている人には、たいして難しくない (簡単というわけでもないですが) 何より書いていて楽しい デメリット Xcodeでメソッドの定義に飛ぶショートカット(Cmd+Ctrl+J)があまり効かない   optionキーを押しながら、コードをクリックして回避しています ライブラリの利用が面倒    Alamofireをgitのsubmoduleを使ってインストールしましたがかなり面倒でした    CocoaPodsでSwiftライブラリが扱えるようになるのは、0.36以降の予定です     正式リリースが待たれます メリットの方が大きいと思ったのでiQONのコードも新しいものからSwiftで書いていこうと思います。
  みなさんはAndroidアプリのリリース作業を自動化していますか? 2014年GooglePlayベストアプリを受賞した弊社のファッションアプリ「iQON」では、リリース作業をCircleCIとDeployGateで自動化しています。今回、どのように自動化したのかを、昨年11月からVASILYで働き始めた堀江( @Horie1024 )がご紹介しようと思います。 概要 iQONの開発フローは、PullRequestベースで行われており、開発が完了したコードをreleaseブランチに随時PullRequestを送りながら開発を進めています。 releaseブランチを作成 releaseブランチにPullRequest 問題が無ければPullRequestをマージ 1~3を繰り返す そして、リリース作業は以下のような手順で行っていました。 releaseブランチをmasterブランチへPullRequest PullRequestをマージ masterブランチからAPKを作成 メールでAPKを社内配布 最終確認 GooglePlayにアップロード リリース完了  作業手順としては難しくないのですが、頻繁にあるリリースの度に作業が発生するのでとても面倒でした。 実現したいこと 今回、自動化することで以下の2項目を実現したいと思います。 APK作成の自動化 社内への配布の自動化 どう実現するか サンプルプロジェクト 解説用にサンプルプロジェクトを用意しました。 https://github.com/horie1024/CircleCISample 全体の流れ GitHubへ差分をPush CircleCIがビルドを開始 Slackへビルドの成否を通知 DeployGateへAPKをアップロード 検証端末にDeployGateアプリ経由でAPKを配信 APKをインストール APK作成の自動化 コマンドでのAPK作成 AndroidStudioでのプロジェクトのビルド iQONの開発には、AndroidStudioを使用しています。AndroidStudioでは、IDEとビルドシステムが疎結合になっているため、コマンドでのビルドが容易に行えます。 ビルドシステムには、Gradleが使われており、Android Gradle Pluginを使用してAndroidプロジェクトのビルドを行います。Gradleについてのより詳しい情報は、 Gradle 日本語ドキュメント 、Android Gradle Pluginについては、 Gradle Plugin User Guide をご覧ください。 ビルドスクリプト AndroidStudioでプロジェクトを新規作成すると、以下の2つのbuild.gradleがあります。 Project直下のbuild.gradle app module内のbuild.gradle app module内のbuild.gradleにAndroidプロジェクトをビルドするための設定が含まれています。 assembleタスク 新規作成したAndroidStudioでプロジェクトには、gradlewという実行形式ファイルが含まれており、gradlewを使いGradleのビルドを実行することが推奨されています。 gradlewでビルドを実行する場合、Gradleが自動でダウンロードされ、それを使用してビルドが実行されます。より詳しい解説は こちら をご覧ください。 Gradleによるビルドは、プロジェクト単位で一つ以上のタスクで構成されており、プロジェクトが実行可能なタスクの一覧は、tasksで表示できます。 $ ./gradlew tasks タスク一覧の中に、Build tasksがあり、APKを作成するには、assembleタスクを実行します。 Build tasks ----------- assemble - Assembles all variants of all applications and secondary packages. assembleDebug - Assembles all Debug builds assembleDebugTest - Assembles the Test build for the Debug build assembleRelease - Assembles all Release builds build - Assembles and tests this project. buildDependents - Assembles and tests this project and all projects that depend on it. buildNeeded - Assembles and tests this project and all projects it depends on. clean - Deletes the build directory. Androidプロジェクトは、デフォルトでdebug APKとrelease APKの2種類の出力を持っていて、assembleタスクを実行すると両方を出力します。出力を分割するには、 assembleDebug 、 assembleRelease タスクを利用します。今回は、GooglePlayへアップロードできる状態でAPKを作成したいので、assembleReleaseタスクでAPKを作成します。 ターミナルからのAPK作成 実際にターミナルからassembleReleaseタスクを実行すると、app/build/outputs/apk以下にAPKが出力されるのが確認できます。このassembleReleaseタスクを自動的に実行することで、APKの作成を自動化します。 $ ./gradlew assembleRelease 署名の設定 サンプルプロジェクトでは、設定していませんが、本来であればassembleReleaseでAPKを作成する場合には署名を付けます。Releaseビルドで署名を付けるには、app module内のbuild.gradleのコードを以下のようにします。詳細は こちら をご覧ください。 リリース作業手順の確認 リリース作業の手順を確認し、自動化する箇所を洗い出します。 releaseブランチをmasterブランチへPullRequest PullRequestをマージ masterブランチからAPKを作成 メールでAPKを社内配布 最終確認 GooglePlayにアップロード リリース完了 目的はAPK作成の自動化ですので、1~3の手順に注目すると、 「PullRequestのマージによってmasterブランチに変更が生じたらAPKを作成する」 とまとめられます。これを CircleCI を利用して自動化します。 CircleCI CiecleCIのようなCI as a Serviceでは、GitHubとの連携が前提になっているので、GitHubに差分がPushされたのをトリガーに、任意のコマンドを実行するといったタスクを簡単に定義できます。実際にCircleCIにどのような挙動をさせるかの設定は、 circle.yml という設定ファイルに書き、CI対象のリポジトリに設置します。 circle.yml circle.ymlの構造は6つのセクションに分かれており、上から順に実行されます。また、これらのセクション全てについて設定を書く必要はありません。 machine  : VMの設定の調整 checkout  : gitリポジトリからのclone dependencies  : ビルドの依存関係の解決 database  : テストのためのdatabaseの準備 test  : テストの実行 deployment  : デプロイの実行 各セクションは、bashコマンドのリストを持て、実行したいbashコマンドを指定していきます。もし特にコマンドを指定しない場合、CircleCIがリポジトリのコードから推測したコマンドが実行されます。コマンド実行時のオプションとして、以下3種類を選択できます。 pre  : CircleCIが推測したコマンドより前に実行 override  : CircleCIが推測したコマンドを独自に定義したコマンドで上書き実行 past  : CircleCIが推測したコマンドより後に実行 これら6つのセクションと3種類のオプションを覚えておけば、circle.ymlは問題なく書けると思います。より詳しい解説は、 Configuring CircleCI をご覧ください。 次に、実際にAndroidプロジェクトをビルドするcircle.ymlを書いてみます。 Androidプロジェクトのビルド Test Android applications を参考に書いていきます。 checkout セクションと database セクションについては、必要が無いので設定を書きません。 ▪︎VMの設定 ビルドを実行するには、 ANDROID_HOME の定義が必要になるため、circle.ymlのmachineセクションで定義します。environmentセクションで環境変数を定義でき、VMの /usr/local/android-sdk-linux にAndroid SDKが用意されてるので、そのPathを指定します。また、Javaのバージョンをopenjdk7に指定します。 machine: java: version: openjdk7 environment: ANDROID_HOME: /usr/local/android-sdk-linux ▪︎sdkのアップデート VMに用意されているAndroid SDKは初期状態のため、必要に応じてアップデートします。例えば、CI対象Androidプロジェクトのbuild.gradleの設定が、以下のようになっていた場合 android { compileSdkVersion 21 buildToolsVersion "21.1.2" } circle.ymlにSDK 21とBuild-tools 21.1.2をインストールするよう追記します。overrideとしてるのは、CircleCIが推測して実行する、gradle dependenciesによってビルドが失敗するためです。 machine: java: version: openjdk7 environment: ANDROID_HOME: /usr/local/android-sdk-linux dependencies: override: - echo y | android update sdk --no-ui --filter "android-21,build-tools-21.1.2" サポートライブラリや、GooglePlayServicesライブラリを使用する場合、filterに以下のidを追加します。 "extra-google-m2repository,extra-android-m2repository" また、filterに指定できるidは、以下のコマンドで確認できます。 $ android list sdk --all --extended ▪︎testセクション testセクションは、必ず実行されてしまうため、overrideでダミーの処理を行うようにします。 machine: java: version: openjdk7 environment: ANDROID_HOME: /usr/local/android-sdk-linux dependencies: override: - echo y | android update sdk --no-ui --filter "android-21,build-tools-21.1.2" test: override: - echo "Nothing to do here" Robolectric Gradle Plugin を導入している場合、testセクションでテストを実行します。 test: override: - ./gradlew test ▪︎APKの作成 deploymentセクションでAPKを作成します。deploymentセクションは、複数のサブセクションを持てます。サブセクションは、branchセクションを持ち、差分が生じたブランチが、指定したbranchにマッチした場合に、commandsを実行します。より詳しい解説は、 こちら を参照してください。 machine: java: version: openjdk7 environment: ANDROID_HOME: /usr/local/android-sdk-linux dependencies: override: - echo y | android update sdk --no-ui --filter "android-21,build-tools-21.1.2" test: override: - echo "Nothing to do here" deployment: master: branch: master commands: - ./gradlew assembleRelease ▪︎ビルドできるか試す CircleCIで実際にAndroidプロジェクトをビルド出来るか確認します。 CircleCIにログイン・サインアップ   GitHubのアカウントでログインもしくはサインアップします。 Add Projectsをクリック   左側のメニューのAdd Projectsをクリックします。 CIの対象とするプロジェクトの選択   1でアカウントを選択し、2でCIの対象とするプロジェクトを選択します。 初回ビルドの実行   プロジェクトを選択すると初回のビルドがスタートします。初回ビルドでは、circle.ymlが不正だったためビルドが失敗しました。 masterブランチに変更をPush masterブランチでcircle.ymlを修正し、再度Pushするとビルドがスタートします。circle.ymlが正しく修正されたため、無事ビルドが成功しました。 成果物 Androidプロジェクトがビルドでき、APKを作成できました。次にAPKをGooglePlayにアップロードできるよう、作成したAPKをダウンロードできるようにします。 CircleCIでは、ビルドを実行した結果発生する成果物は、 Build artifacts としてビルド毎にダウンロードできます。Build artifactsとしてダウンロードするには、ビルド成果物を保存する必要があります。 成果物を保存するには、generalセクションのartifactsセクションに成果物のパスを指定します。generalセクションは、6つのセクションとは別に、特定のセクションに属さない、ビルド全体に関する設定を書くことができるセクションです。 general: artifacts: - "app/build/outputs/apk/app-release-unsigned.apk" machine: java: version: openjdk7 environment: ANDROID_HOME: /usr/local/android-sdk-linux dependencies: override: - echo y | android update sdk --no-ui --filter "android-21,build-tools-21.1.2" test: override: - echo "Nothing to do here" deployment: master: branch: master commands: - ./gradlew assembleRelease masterブランチでのみタスクが実行されるようにする ここまでで、GitHubでPullRequestをマージもしくは変更をPushすることで、CircleCIのタスクが実行されるようになりました。ですが、master以外のブランチへPushした場合でもビルドが実行されてしまいます。そこで、masterブランチへのPushでのみビルドが実行されるようにします。 Specifying branches to build を参考に、circle.ymlに設定を追加します。generalセクションにbranchesを追加することで、masterブランチへのPushでのみビルドが実行されるようになります。 general: branches: only: - master artifacts: - "app/build/outputs/apk/app-release-unsigned.apk" machine: java: version: openjdk7 environment: ANDROID_HOME: /usr/local/android-sdk-linux dependencies: override: - echo y | android update sdk --no-ui --filter "android-21,build-tools-21.1.2" test: override: - echo "Nothing to do here" deployment: master: branch: master commands: - ./gradlew assembleRelease 以下のように、testブランチをPushしても 「Not Run」 となります。 Slack連携 Slackと連携させることで、ビルドの成功・失敗を通知させることができます。 SlackのConfigure IntegrationsからCircleCIを選択 通知したいchannelを選択 channelを選択するとWebhook URLが得られます。 CircleCIのProject SettingsでWebhook URLを登録 Notifications > Chat NotificationsからSlackを選び、Webhook URLを入力後、Saveをクリックします。 OOM問題 自動化してしばらく運用した中で実際に起こった問題なのですが、build-toolsのバージョンを21に上げたタイミングでOut Of Memoryでビルドが頻繁に失敗するようになりました。これは既知の問題のようで、CircleCIのドキュメントにも OOM killer ran として載っています。 Androidプロジェクトで発生した場合の対策ですが、プロジェクトによって原因が異なる可能性があるので確実ではありませんが、Gradleのバージョンを2系に上げることで改善できました。 社内への配布の自動化 DeployGateの利用 社内へのAPKの配布には、 DeployGate を利用します。DeployGateは、APKをアップロードすることで、専用アプリを介して開発中のアプリを簡単に配布できるサービスです。 DeployGateでのアプリの配布方法 DeployGateでのアプリの配布方法は、サインアップ後のチュートリアルがわかりやすく、すぐに理解できました。また、 こちらの資料 も参考になりました。 DeployGate APIの利用 社内への配布を自動化するにあたり、 DeployGate APIのPush API を利用することで、cURLコマンドで簡単にAPKをアップロードできます。 $curl -F "file=@sample.apk" -F "token=YOUR_API_KEY" -F "message=sample" https://deploygate.com/api/users/YOUR_USER_NAME/apps APIキーの取得 APIを利用するには、APIキーが必要になります。APIキーは、DeployGateへ新規登録することで発行されます。新規登録後に、 https://deploygate.com/settings#apiKey にアクセスすることで確認できます。 ターミナルでの実行 Macのターミナルから実行し、DeployGateにアップロードされることを確認します。DeployGateのダッシュボードにアップロードしたアプリが表示されれば成功です。 $ curl -F "file=@PATH_TO_YOUR_APK" -F "token=YOUR_API_KEY" -F "message=sample" https://deploygate.com/api/users/YOUR_USER_NAME/apps CircleCIでのビルドから社内配布までの流れの詳細 実際の配布の流れは、以下の通りです。 CircleCIがAPKを作成 CircleCIがDeployGateへAPIを使いAPKをアップロード DeployGateがDeployGateアプリへ配信 検証端末でDeployGateアプリを起動し、APKをダウンロード・インストール DeployGateからの配信を受けるには、DeployGateアプリをインストールしておく必要があります。 CircleCIとの繋ぎこみ CircleCIからコマンドを実行し、APKをDeployGateへアップロードします。masterへのPushからAPKがDeployGateへアップロードされるまでの流れは、以下のようになるはずです。 masterへ差分をPush CircleCIが検知し、APKを作成 DeployGateへのアップロードを実行 したがって、circle.ymlを以下のように記述します。 general: branches: only: - master artifacts: - "app/build/outputs/apk/app-release-unsigned.apk" machine: java: version: openjdk7 environment: ANDROID_HOME: /usr/local/android-sdk-linux dependencies: override: - echo y | android update sdk --no-ui --filter "android-21,build-tools-21.1.2" test: override: - echo "Nothing to do here" deployment: master: branch: master commands: - ./gradlew assembleRelease - curl -F "file=@app/build/outputs/apk/app-release-unsigned.apk" -F "token=YOUR_API_KEY" -F "message=sample" https://deploygate.com/api/users/YOUR_USER_NAME/apps deploymentのcommandsで、APKを作成とAPKのDeployGateへのアップロードを実行しています。ここで、APIキーが直書きになってしまっているので、CircleCIの環境変数に登録し、それを参照するようにします。 CircleCIの環境変数への登録 Project SettingsのTweaks > Environment variablesから設定します。NameとValueというformがあるので、NameにDEPLOY_GATE_API_KEY、ValueにAPIキーを入力し、Save variableをクリックします。するとDEPLOY_GATE_API_KEYとして環境変数が登録されます。 登録した環境変数を使用するように書き換えます。 general: branches: only: - master artifacts: - "app/build/outputs/apk/app-release-unsigned.apk" machine: java: version: openjdk7 environment: ANDROID_HOME: /usr/local/android-sdk-linux dependencies: override: - echo y | android update sdk --no-ui --filter "android-21,build-tools-21.1.2" test: override: - echo "Nothing to do here" deployment: master: branch: master commands: - ./gradlew assembleRelease - curl -F "file=@app/build/outputs/apk/app-release-unsigned.apk" -F "token=${DEPLOY_GATE_API_KEY}" -F "message=sample" https://deploygate.com/api/users/YOUR_USER_NAME/apps このように、登録した変数は、circle.ymlから参照できます。 コマンドの別ファイルへの切り出し circle.ymlを見やすくするために、コマンドを別ファイルにまとめてしまいます。 ./gradlew assembleRelease curl -F "file=@app/build/outputs/apk/app-release-unsigned.apk" -F "token=${DEPLOY_GATE_API_KEY}" -F "message=sample" https://deploygate.com/api/users/YOUR_USER_NAME/apps その結果、最終的なcircle.ymlは、以下のようになります。 general: branches: only: - master artifacts: - "app/build/outputs/apk/app-release-unsigned.apk" machine: java: version: openjdk7 environment: ANDROID_HOME: /usr/local/android-sdk-linux dependencies: override: - echo y | android update sdk --no-ui --filter "android-21,build-tools-21.1.2" test: override: - echo "Nothing to do here" deployment: master: branch: master commands: - ./deploy.sh 動作確認 うまくいけば、DeployGateアプリにビルドしたAPKが配信されます。 また、ArtifactsからAPKをダウンロードできます。 自動化した結果 APKの作成から社内配布まで自動化することができました。PullRequestのマージが行われるとAPKが配布されるようになり、リリース作業がかなり効率化されました。 自動化前 releaseブランチをmasterブランチへPullRequest PullRequestをマージ masterブランチからAPKを作成 メールでAPKを社内配布 最終確認 GooglePlayにアップロード リリース完了 自動化後 releaseブランチをmasterブランチへPullRequest PullRequestをマージ CircleCIがAPKを生成するタスクを実行 DeployGateを介して社内配布 最終確認 CircleCIからAPKをダウンロード GooglePlayにアップロード 7. リリース完了 まとめ CircleCIとDeployGateを利用して、リリース作業を自動化してみました。昨年11月から実際のリリース作業に組み入れ運用していますが、思っていた以上にストレスが減り、快適に開発を行えるようになりました。チームメンバーからの評判も良く、やってみて良かったと思います。 しかし、まだまだ改善できる点があり、継続して改善していこうと考えています。現在、GooglePlayへのAPKのアップロード自動化を組み込んだ開発フローおよびリリース作業手順をテスト中です(実験した内容は Qiitaに上げました )。また、今回紹介出来なかった内容もありますので、近日中にご紹介できればと思います。 最後に VASILYでは、自動化や新しい技術が大好きエンジニアを大募集しています!少しでもご興味のある方は是非オフィスに遊びにいらしてください! また、1月28日にSmartNewsさん、Wantedlyさんと ベストアプリ勉強会 という勉強会を開催します。私も会場にいますので、お気軽にお声掛けください! 募集要項 連絡先:info[at]vasily.jp
twitterで下記のようなコメントがあり、弊社も新卒採用などやっていることから、Matzさんに新卒エンジニアの給料について伺ってみました。   質問 新卒エンジニアの適正給料はこれだとするならいくら位だと思いますか?ざっくり。   Matz 日本ということ考えて…理系大卒で600万くらいかなあ でもねぇ、大卒はだいたい粒ぞろいだと言っているのと同じ。個人差は無いものとしているってことなので、それだとおかしいよね。 ただ、コンピュータサイエンスの学科を出て、プログラミングできるっていう学生が300万とか400万とかしか貰えないと、彼の持ってる技能はあまり評価されてないってことじゃないかなと。特殊技能を持っていると思われてないってことじゃないかな。 プログラミングするのに国家試験はいらないので誰でもなれるって思われてるんだけど、実際はプログラムかける人とかけない人の差ってものすごいあるじゃないですか。 プログラミングを好きで、ものすごい苦労して特殊技能として伸ばしてきた訳だから。かつ、それを仕事で使って会社に貢献するはずなのに、普通に大学出て、プログラミングできません、これから仕事ですから勉強しますって人と同じ扱いをされると、全然評価されてないってことで、どういうことなんだろなって。 スタープレーヤーだと大学卒業してすぐでも1000万超えるくらいじゃないと夢がないんじゃないかなって。 逆に、使えなかったらバッサリ切る。凄い大学出てたとしても。 新卒みんな丸ごと高いっていうのはおかしいけど、スタープレーヤーに対して普通の給料しか出していないと、「俺、普通の人と同じに働いていれば良いのかな」ってなる。できない振りするのは簡単なので。 だいたいプログラマって、野球選手が野球にかけるのと同じくらいの時間かけてるじゃないですか。優秀な人は。 野球もそうなんだけど、頑張った人がみんなプロ野球の選手になれる訳じゃないんだけど、少なくともトップの人は凄い成功して、あの人みたいになりたいっていう憧れの存在って、プログラマーだとあんまりないよね。 経営者の方では凄いエンジニアがいればそのエンジニアの開発能力をお金にかえて欲しいし、エンジニアの方は自分の能力を正当に評価してくれるところに行ってして欲しいし。 VASILYの皆様にも、持ってるテクノロジーをお金に換えるってところは頑張ってほしいよね。   ---------- 新卒の皆さん・採用担当の皆さんは就職活動がヒートアップしてきたと思いますが、是非是非参考にして見てください。 VASILYもテクノロジーをお金に換えるところ、頑張ります。 VASILYでは2016年の新卒採用を行っています。自分こそがスタープレーヤーだと思う方は是非是非ご応募ください! 2016年度 新卒採用
  質問 RubyKaigi、楽しみにしていますが、これをもっと楽しむためにはどうしたら良いですか? 参加者として楽しむには勉強しておくといいことなどあれば教えてください、登壇者を事前にネットストーキングすると楽しいとか。 Matz まあそれもいいかも(笑) カンファレンスの内容って、殆どのものってネットを調べればわかる情報なんですよね。 わざわざ会場に行ってっていうのは、スピーカーで話を聞くことよりもむしろネットワーキングっていうか、人と人とが繋がることのために行くんだと思ってるんですよ。 でも、それってあんまり言われないので、多くの人たちってただ行って話を聞いて「ああいい話を聞いた」って帰ってしまうことがかなり多いので、ちょっともったいないなと。 この業界の人はあんまり積極的に知らない人と交流する人ばかりじゃないんだけど。そこはちょっと置いといてですね、人と人のつながるみたいなことを主眼に置いたらいいんじゃないかなと思うんですよ。積極的にスピーカーに話しかけるとか、休憩室で隣に座った人に「あなた何やってんの」みたいに話しかけるとかいうことをすると120%か、まあ200%かもしれないけど、カンファレンスを有効活用できるんじゃないかと思うんですよね。 有料になってるので、せっかく行った分を取り返すためにはどうしたらいいかっていうと、そうやって人と話すっていうのが大事なんじゃないかなと。 質問 RubyKaigiでは、人と話せるチャンスはどこら辺なのでしょうか。 Matz 一番やりやすいのは懇親会ですよね。そこはあまり苦労しなくてもできるので、頑張った方がいいと思うのはコーヒーブレイクが途中にあるじゃないですか。あれをいかに有効活用するのかっていうのが狙い目じゃないかと思うんですよね。 そういった時間は知った人とつい喋ってしまう。VASILYの人とか、元々知ってる人とばかり集まって、「あの話はどうだったね」みたいな話をしがちだけど、それはオフィスに帰ってからもできるから。 そうじゃなくて、色んな人と喋るみたいなのをやるといいかなと。 まあ私にやれと言われても結構辛いんですけどね。性格上(笑) 質問 MatzさんはRubyKaigiで非常に多くの人と話すと思うのですが、どういった話をしている時が楽しいですか? Matz RubyKaigiの場合は来てる人みんなRubyに関心があるか好きな人なので、そういう意味で言うと誰と話してても楽しいのは楽しいんですね。 あと言うと新しいアイディアの話をするのは結構好きなので、「こんなことやってみたんですけど」みたいな話をするのは楽しいですね。それはいろんなレベルだけど、Rubyそのものに対してどうしたいみたいなアイディアもウェルカムだけど、「Rubyでこんなことやってみました」みたいな話も面白いですよね。特に「今までやったこと」みたいな話ばかりではなくて、新しいこと、「こんなこと試したい」とか「試してるんですけど」みたいな話を聞くのはすごい好きですね。 質問 Matzさんは今年もKeynoteがありますよね。外にも様々なプレゼンをされているんですが、エンジニアとしてのプレゼンのコツって何かありますか? Matz 心当たりあるのは数ぐらいしかなくて、やっぱり数をこなすと聴衆が何を期待しているのかとかが大体把握できて、だいぶ楽になりましたよね。 あとは、エンジニアなのでテクニカルな話はするんだけど、テクニカルのレベルを上げすぎちゃうと8割9割の人は付いて来れなくなるので、そこは「もっと詳しく知りたい人はあとで来てね」みたいな感じで抑えとくっていうのはエンジニア向きの話としても結構大事な気はしますね。 ジェネリックな話っていうか一般論の方がみんな好きなことが多いみたいで。だから詳細には立ち入らないんだけど「こんなことが今大事なのでこれちょっと頑張ろう」みたいな。いろんなのがあるんだけどここが大事、みたいな戦略的な話をするのは結構重要かなと思います。 あと、大体のことって一次元では決まらないじゃないですか。バトルもののランキングとかとは違って、このことは得意なんだけどこっちは苦手みたいなのは常にあるので、そういうトレードオフを意識した話をするようには心がけています。 一つのアイディアがどこでもオールオッケーみたいなケースってエンジニア的にはほとんどないんですよね。こういう前提条件があればこのアイデアはいいけど、前提条件が成立しないときはまた別のという風なことになるので、こういうトレードオフでこれを選んだんだけど、前提条件が成立しないときはそうでもないよねっていう話はよくしますね。 まあ、フェアでありたいと思ってますね。マーケティングの話とかだと、この技術で何でも解決みたいな、「これさえあればなんでも解決」みたいな話をしがちなんだけど、現実はそうではないのでそこはちゃんと言うようにしてます。   ---------- RubyKaigi 2014は9/18〜19。MatzさんのKeynoteは19日9:30〜です。 盛り上げて、楽しみたいですね! (弊社VASILYもGold Sponsorとして参加&Matzさんの衣装コーディネートもしますので、楽しみにしていてくださいませ!)
こんにちは、バックエンドエンジニアのBoBです。 今回は皆様にお知らせがあります。 弊社VASILYと、株式会社ハースト婦人画報社共催で、日本初のファッションをテーマとしたハッカソン 「THE FASHION HACK in TOKYO powered by Hearst Fujingaho & VASILY」 (以下、THE FASHION HACK)を 2014年9月6日(土)~9月7日(日) に開催する事となりました! 「THE FASHION HACK」は、エンジニアやデザイナーなどのクリエイターが1~3人1組のチームで、与えられた24時間で新たなアイディアのもと、実際に動くプロダクトを開発するハッカソンイベントです。 今回はファッションに普段から興味があるエンジニアの方にとっても、そうでないエンジニアの方にとっても、このハッカソンイベントがいかに魅力的かを伝えたいと思います。 1. ファッション×テクノロジーはまだまだ未開拓 今回のハッカソンイベントのテーマは、「ファッションとITテクノロジーを掛け合わせて、新しいものを作る」です。それ以外に特に縛りはありません。 近年、弊社iQONをはじめ、インターネットのファッションメディアがユーザー数をどんどん増やしたり、ハースト婦人画報社の様なファッション業界の会社がデジタル戦略を加速させたりと、テクノロジーでファッション業界にイノベーションを起こそうという企業が増えています。しかし現状、まだまだ開拓されていない領域です。 また、ウェアラブル端末であったりIoT(Internet of Things)の分野が盛り上がりを見せており、ファッション業界からも大きな注目を浴びています。メガネ専門店「JINS」を運営する株式会社 ジェイアイエヌが発表した、JINS MEMEは記憶に新しいかと思います。これらの分野は今後確実に伸びて行く分野であり、かつファッションとは切っても切り離せない分野となっています。 今回のこのイベントをきっかけに普段気になっている新しいテクノロジーに触れ、このチャレンジングな分野に興味を持ち、イノベーティブなものを形にしてもらえればと思います。   2. 普段はさわれない、国内最大級のファッションアプリ「iQON」のAPIを使った開発ができる ファッションコーディネートアプリ「iQON」は、投稿されたコーディネート数は100万件を超え、掲載されているファッションアイテムも400万件以上と、国内最大級のファッションコンテンツが集まるアプリに成長しています。 そのiQONのAPIを今回のイベント限定で、参加者に公開する予定となっています。 またハースト婦人画報社が所持している、ファッション記事に関するAPIも当日公開される予定となっています。 もちろんその他、ファッションに関わらず様々なAPIを使って開発していただいてかまいません。   ぜひ普段さわれないiQONやその他のAPIを自由に使って開発を楽しんでいただきたいと思います。   3. 通常のハッカソンでは出会えないような人との出会い ハッカソンの醍醐味としては、普段話さない人たちとの新しい出会いがあると思います。 特に今回のハッカソンに関してはエンジニアやデザイナーだけでなく、審査員やスタッフも含め、ファッション業界の方にも参加していただく予定となっております。詳しい審査員の情報は専用サイト( http://www.thefashionhack.com )をご覧下さい。   イベント後の懇親会も予定していますので、今ファッション業界ではどのようなテクノロジーに注目しているのか、今後どのようにファッション業界とテクノロジーの関係は変わって行くのかなど、普段なかなか聞けない話を聞く事ができるイベントとなっています。   4. 採用につながる可能性も。。。 私たちVASILYは、テクノロジーで世の中にインパクトを与えたいエンジニアを常に募集しています。 弊社エンジニアも当日会場にいますので、VASILYに興味がある方はぜひぜひ参加して話してみてください。 詳しいイベント概要の確認、ハッカソンへの申し込みは専用サイト( http://www.thefashionhack.com )からお願い致します。 皆様からのたくさんのご応募、お待ちしております!     
  VASILYではエンジニアが海外で行われるカンファレンスに参加できる制度があり、毎年iOSアプリのエンジニアが WWDC に、Androidアプリのエンジニアが Google I/O に参加しています。 今年もAndroidアプリのエンジニアである私、 @yoichinishimura が6月25日、26日に行われた Google I/O に参加してきました。 今年のI/Oでは、 Material Design やAndroid関連の多くの発表がありましたが、その中で私が一番に関心を持ったのが Android Wear でした。 半年ほど前に公開されたPreview版のSDKにはさほど興味は湧かなかったのですが、正式なSDKが公開されたのと、会場で参加者に配られた Samsung Gear Live を身につけていたら急に興味が湧いてきました。 今回は Android Wear とはいったいなんであるのか ということをI/Oのセッションや公式ドキュメントで学んだことと、私の考察を混ぜながら紹介したいと思います。 1. Android Wearとは I/Oに行く前は 『 Android Wear とはGoogleが提供するOSを取り入れた時計、つまりスマートウォッチのこと』と勝手に思っていたのですが、どうやらスマートウォッチのことだけじゃないと Wearable computing with Google というセッションで知りました。 セッション内で Google Glass が Android Wear に対応するという発表があったのですが、つまり対応後は Google Glass も Android Wear という扱いになるそうです。 つまり Android Wear とは Androidと通信することができるウェアラブル端末すべて を指すのです。Androidが起こすIoTの時代がやってきたと感じました。 Android Wear イメージ的には以下の画像の下部にあたると思います。 2. 既存アプリのAndroid Wearの使いどころ Android Wear の開発をはじめる方は Googleが出している公式ドキュメント を一読することをオススメします。 Android Wear の一番の基本機能は Context Stream と呼ばれるカードリストを表示させるところです。 「Google Nowに似たもの」とイメージすればわかりやすいと思いますが、1画面に1カードAndroidから送られるNotificationなどの情報を表示します。 デバイスの操作は、スマートフォンと同様にタップやスワイプ、Yes or Noなどの簡単な操作、音声入力などが行えます。 実際に使ってみるとよくわかりますが、スマートフォンに比べて行えることが限りなく少ないですし画面も非常に小さいです。 そのため既存アプリを上手く対応させようと思うとかなり苦労すると思います。 話は戻りますが Wearable computing with Google のセッションで以下の図が出てきました。 Wearableの開発をするにあたっては従来の デザイン・開発・デバッグ(テスト) の工程に加えて UXイテレーション が重要になるそうです。 開発者が意図するUXを実現してもらうため、Webやスマートフォン以上にWearableの開発にはUXに関わる時間を使う必要がありそうです。 iQONでも Android Wear 対応をするには既存機能の中で何の機能を実装するのが最適であるのかを考えてみたのですが、定期的にユーザーにGCMで送っている以下のような情報を Android Wear に最適な形で対応すると効果が高いと考えました。 Likeしているブランドのアイテム追加情報 アイテムが割引になった際のセール情報 Likeしているアイテムが売り切れ間近の在庫情報 常に見たい オトクでタイムリーな情報 を配信するのは Android Wear 冥利に尽きると思いますし、使い方としては間違っていないと思います。 先日 Android Wear 対応するのに必須な Google Play Services のバージョンアップも済ませたので、近日 Android Wear 対応したバージョンを配信する予定です。 3. Android Wear開発のはじめ方 最後に、簡単にはじめられる Android Wear 開発のはじめ方を紹介します。 Hello World!出力 まずは Android Studio をインストール 、もしくは v0.8.0 以上にアップデートしてください(SDKの更新も忘れずに)。 https://developer.android.com/sdk/installing/studio.html New Project を立ち上げて、 Select the form factors your app will run on まで進んで、 Phone and Tablet と Wear にチェック を入れてください。あとはポチポチとNextを押下してプロジェクトを作成してください。 プロジェクトが出来上がると mobile wear と見慣れないディレクトリ構成でプロジェクトが立ち上がります。 mobile の Configuration を選択し Android のスマートフォンの 実機 or エミュレータにビルドを行い、 wear の Configuration を選択し Android Wear の 実機 or エミュレータ にビルドを行うことで、各デバイスに Hello World! を出力することができます。 サンプルコードで学ぶ SDKマネージャーから Android 4.4W のパッケージをインストールすると以下のパスにいろんな利用シーンを想定した17個サンプルのプロジェクトができています。 それをビルドしながら実際に動かし、コードを読み進めていくと Android Wear でどういうことができるのかを効率良く学ぶことができます。 $ /samples/android-20/wearable/ 併せて 公式の開発ドキュメント を読みながら Android Wear から新しく登場したクラスを探ってみると更に理解が深まると思います。 4. まとめ ざっくりと Android Wear について紹介しましたが、実際に手にとって使ってみないと良さがわからないと思うので興味のある方は発売開始したスマートウォッチを手に入れてみてください。 時計や眼鏡は常に身体に身につけているものなので、手に取って操作しないといけなかったスマートフォンでは提供できなかった新しい体験が Android Wear では提供できると思います。 最後の最後に現実の話をしますと Android Wear と連携できるのは Android 4.3 以上の端末のみ です。 2014年7月14日現在、世界全体の25%のユーザーしか対応OSを使っていないですし、iQONに至っては12%のユーザーのみです。そこから Android Wear 端末を購入して使うユーザーがどのくらいいるかを考えるとかなり少ないと思います。 Android Wear 対応を急ぐことはありませんが、前述の通りシンプルそうに見えても効果を最大限に対応するにはかなり難しい印象です。 そのため今からスタートダッシュで開発を進めノウハウをためるのがベストだと思います。iQONも先陣を切ってウェアラブル対応を進めていきます。 iQONを開発しているVASILYでは先陣を切って世界にインパクトを与えたい Androidエンジニア を大募集しております。 【Android】世界で勝ちたいAndroidアプリエンジニア募集 また情報交換なども随時行っていきたいと考えているので勉強会などの開催、参加についても気軽に相談頂ければと思います。 info[at]vasily.jp や @yoichinishimura などで気軽に連絡いただければと思います。
  VASILY新入りエンジニアのEricと申します。 フィラデルフィアから来ました。好きな漫画はHUNTER x HUNTERです。 今回は使える英語コミットコメントを10個紹介します。 Fix, Change, Reviseなど、日本人の方には違いが分かり辛いものも、実際の使い所が分かるようにしてみました。 是非知って、使ってみてください! 1. Fix 意味:  直す, 修理する 使い方: バグなどが出たためコードを書き直す必要があう場合に使います。何か問題を直して、正確な操作に戻すニュアンスが強いです。 例:  Fix inequality in LinesComponent 訳:  LinesComponentにおける不等式を書き直す 2. Revise 意味:  変更する、修正する 使い方: バグは出ていませんが、不十分なところや修理が必要としたところがあって、コードをよりよくする時に使うことが多い。考え直して、改善するというニュアンスを持っています。 例:  Revise User class variable types 訳:  Userクラスにおいている変数の種類を修正する 3. Change 意味: 変更、変える 使い方: 前もっていた状態を変えるというニュアンスが強い。ある値を持っていた変数に別の値をつけたり、クラスなどの造りを変更したりする時に良く使います。 例:  Change var names for consistency 訳:  一貫性のために変数名を更新する 4. Update 意味: 更新する、新しくする 使い方: 最新の情報などに対応するためにコードやドキュメントを変更する時によく使います。最近変化した別のものにマッチするというニュアンスが入っています。 例:  Update documentation to show new methods 訳:  ドキュメントを新しいメソッドを反映するようにアップデートする 5. Upgrade 意味: 更新する、改善する 使い方: プロジェクトなどの部分やライブラリーのバージョンを更新する時によく使います。Updateとの違いは微妙ですが、バージョン番号などが変わる時に使うことが多いです。 例:  Upgrade to markdown-preview@0.74 訳:  `markdown-preview`をバージョン0.74に更新する 6. Clean up 意味: 片付ける、奇麗にする 使い方: 分かりにくかったり、無駄があったりしたところを片付けて、分かりやすくする時に使うことが多いです。 例:  Clean up specs based on feedback. 訳:  フィードバックに従ってスペックを片付ける 7. Remove 意味: 削除する、取り除く、除去する 使い方: 以前コードに入っていたものを取り除く時に使います。 例: remove decoratorType instance var  `decoratorType`というインスタンス変数を削除する 8. Revert 意味: 取り消す、元に戻す 使い方: プロジェクトなどを元の状態に戻す時に使います。 例:  Revert to version 1.0.9 訳:  バージョン1.09に戻す 9. Add 意味:  追加する、加える 使い方: 今までなかったものを追加する時によく使います。 例:  Add `has-selection` class to the editor div 訳: `editor` div に`has-selection`というクラスを追加する 10. Implement 意味: 実装する 使い方: ある追加したい機能やフィーチャーがあって、実際にコードを書いてプロジェクトに入れる時に使います。 例:  Implement removeDecorations class 訳:  `removeDecorations`というクラスを実装する   英語でコミットコメントが書けると、githubなどで世界中のプロジェクトに参加して多くのエンジニアと一緒に仕事ができるので刺激的です。 挑戦してみてください!   VASILY iOSエンジニアによるApple WWDC報告会を開催します! ・日時:6月27日(金)20:00〜22:00 (20:00〜21:00 Apple WWDC報告会、21:00〜22:00 オフィスで懇親会) ・場所:東京都渋谷区恵比寿3-42-13 1F     株式会社VASILY( http://bit.ly/1mQQMDs ) ・参加費:無料 ・定員:15名 ※ご参加は学生、社会人問わず Apple Developer Programs に加入していらっしゃる方のみとさせて頂きます。 ※定員制のため、応募者多数の場合は抽選とさせていただきます。ご招待する方のみ 6/20(金)にメールにてご連絡いたします。 VASILY iOSエンジニアによるApple WWDC報告会のお申し込みは こちら から
VASILY iOSエンジニアの庄司です。 同じくiOSエンジニア荒井とチーム全員(2名)でサンフランシスコで開催中のWWDCに来ています。 今回のKeynoteは既にご存知の通り、「OS X Yosemite」、「iOS 8」、新言語「Swift」など盛りだくさんの内容でした。 SwiftはRubyと似たような記法があり、 Rubyを使っている会社 の一員としてはとっつきやすくていい感じです。 さて、WWDCの開催期間は5日間ありますが、大きく報道されているKeynote以外にも たくさんのセッション が開かれています。 そのセッションの様子は WWDC2014の公式サイト で見ることができます。 Keynoteもセッションも現地に行かずに見れるのに、なぜ高い参加費を払って現地に来るのでしょうか。 今回は「現地に来るべき理由」をいくつか紹介します。 WWDCの現地に来るべき理由 1. Labs WWDCの会場にはAppleのエンジニアが直接技術的な相談に乗ってくれるブースがあります。(基本的に英語。一部日本語のわかるエンジニアもいるそうです) 日本にいるとAppleのエンジニアと話す機会はなかなかありません。英語に自信がなくてもコードでなら会話ができます。 僕達も実際にLabに行ってみました。英語が苦手でも一人ひとりに理解できるまで丁寧に教えてくれて、抱えていた問題が解決しました。   2. Bash WWDC期間中の一通りのセッションが終わった後、会場の隣の公園でWWDC参加者が一同に会するBash(打ち上げ)が開催されます。 去年はここで「こんなに日本人来てたのか」というぐらい、多くの日本人エンジニアと話すことができて、帰国後も交流が続いています。 毎年有名なアーティストが招待されており、企業のお祭の域を超えた盛り上がりを見せます。 3. Stump the Experts 毎年WWDC期間中に開催されるイベントで、ステージ上のAppleエンジニアvs観客のエンジニアのクイズ大会です。 クイズの問題はAppleに関する超マニアックなもの(例: 粉々になったプラスチック片を見せて、これは何年のmacのどのパーツ?)が多く、僕なんかはさっぱり答えがわかりません。 Keynoteと同じ会場で席は半分ほどしか埋まっていませんでしたが、Keynote以上の歓声と熱気がありました。 4. 現地で開催されているイベントに参加 AltConf WWDCのチケットの抽選に外れてしまったエンジニア達が有志で開催しているイベントです。WWDC期間中、会場の近くで開催されています。 僕が見に行った時は、ベルリンから来たエンジニアがXcode Pluginについて語っていました。 当然WWDCのように最新技術については触れられませんが、デザイン関連のセッションやマーケティング関連のセッションもあり、WWDCのチケットを持っていても行ってみる価値はあると思います。 おまけに、無料なのにTシャツまでもらえました。 AppAdviceのパーティー WWDC開催の前日、会場の近くでAppAdviceというアメリカのレビューアプリのメンバーが主催するパーティーに行ってきました。 日本人は僕達だけでしたが、WWDCのジャケットを着たエンジニアがたくさんいたので、積極的に交流してきました。 お互いに自身のアプリを紹介し合い、エンジニア同士貴重な情報交換が出来る場であったと思います。 パーティーの趣旨はよくわかりませんでしたが、iQONの宣伝も忘れずにしてきました。 こういったイベントはこの時期よくやっているようです。 5. 現地の企業に行く 知人の紹介でYahoo! inc.で「Yahoo! News Digest」のPMを担当している方とお知り合いになりました。 WWDCが落ち着いた後、Yahoo! Inc.のオフィスにお邪魔してくる予定です。 6. とにかく話しかける 正直なところ僕は英語が苦手です。英語だと会話のキャッチボールがうまくできませんが、自分からボールを投げ始めることはできます。 セッションに並ぶ行列中に話しかけたり、隣の席に座った人のmacを覗きこんで話しかけたり… この行動のおかげで去年あった外国人と現地で再会することができました。 7. 話しかけられる 写真にあるようにVASILYのメンバーに借りた着物を着て行きましたが、効果抜群でした。 みんなが笑顔でこっちを見てくれるし、多くのエンジニアに声をかけられました。話しかけてくれた人の中には「Oh, Jedi...」とオビ=ワン・ケノービと勘違いしている人もいました。 チャイナドレスのエンジニアがいたら僕も話しかけたくなるので同じ心理だと思います。 まとめ 5. については、たまたまいい人を紹介していただけたのでラッキーだったとは思いますが、それ以外のことはチケットを購入できれば自分の努力でどうにかなります。 また来年WWDC参加を狙っている方は全力で楽しむために、今回紹介した内容を試してみてはいかがでしょうか? 惜しくも抽選にもれてしまった方は、現地に行った方から話を聞くと何かしら発見があるかもしれません。 さいごに この記事を読んで来年こそはWWDCに行ってみたいと思ったあなた、一緒にiOSアプリを開発してWWDCに行ってみませんか? (もちろんチケットが取れればの話ですが) WWDC仕込みのメンバーとNo.1のアプリを作るiOSエンジニア募集  
  まつもとゆきひろさんが弊社の技術顧問に就任する事となりました。せっかくなので、「ベンチャーの重要性」「世界での勝ち方」「sierのヤバさ」「モルモン教とエンジニアリング」など、まつもとさんに色々聞きたかった事をぶつけてみました!  vasily officeにて 質問 我々は、小さい会社ながらも技術によって世の中にインパクトを与えようと頑張っています。 他にもそういった会社が増えていますが、思う所など教えてください。 まつもと その逆は大企業とかだけど、関わっている人が多くなればなるほど、辛くなりますよね。 僕はビジネスマンじゃないので、エンジニアが幸せかどうかしか分からないけど、自分で決められないエンジニアは不幸なんですよね。 この技術の方が絶対いいのに、「上司が説得できないから従来のやり方で頑張りましょう」みたいな空気で腐りながらやるのは、エンジニアにとっては不幸なんですよね。 小さなとこだと自分で何をするか決められるじゃないですか。それはエンジニアにとっては幸せなことだと。 あとは、ネットのスピード感を維持するっていうのはむしろ、小さな組織でなければできない。 「大企業があります。ビジネスがあります。大企業がそのシステムが欲しいから発注します。」みたいな構図はもう、なくなっていくかなと。 テクノロジーを担うのは動きの速い小さな組織しかないんじゃないかな、と思いますね。 質問 我々はテクノロジーを足がかりにして世界と戦おうとしています。 rubyは世界進出に成功したプロダクトだと思いますが、その経験を踏まえて成功するのに必要な事は何だと考えますか? まつもと なんでも人間が相手なので、人気が出るとか広く使われるとかは人が決める。一人一人がサイトだったりサービスだったり、あるいは言語だったりを選ぶ事によって決まるわけですよね。 それを選ぶかどうかっていうのは、好きかどうかとか、便利かどうかで、それって文化に依存する。 rubyの場合はプログラマーのマインドとか、ハッカー文化みたいなものに寄り添って、そこに最適化していて。そこが特定の国に依存してないものだったので、上手く行ったと。 現状、本当に世界規模で何かをやるには、国に依存しない共通な何かの文化を見つけ出して、それに最適化しないと難しいんじゃないかと思いますね。 iqonは成功するために日本の文化・ファッションの文法によりそっているわけですが、これを海外に持っていくためには、この日本への最適化そのものが障害になる可能性が出てきますよね。 あるいは、この日本のファッションという文化ごと世界共通語を目指すといいのかもしれないけど。 質問 エンジニア、プログラマーの幸せって何だと思いますか。 まつもと 満足しているかどうかってことかな。 自分の待遇だったり境遇だったりに対して、満足しているかどうかっていうのは一番大事だと思っていて、プログラマーとして満足なのは何っていうと、 一つは面白いものを作っているかどうか。 テクニカルもエキサイトできるかだし、もちろん収入もそうだし、忙しいかどうかも。時間の使い方だとか。 あとは自由とか裁量。 自分がほんとうに素晴らしい、正しいと思う事を聞いてもらえるか、説得できる余地があるかどうか。 エンジニアが何を言っても駄目だって諦めていると、そこは違うだろうみたいな。 で、最後にそれに繋がるものっていうのは、自分がエンジニアとして、技術を持っている人として、テクノロジーを使ったパワーを持っている人としてリスペクトされているか。 「お前の持っている技術だったら俺のやりたい事ができるかもしれない。一緒にやってくれないか」って言われたら、リスペクトされている気になるじゃないですか。そうするとすごい力が出てくる。 逆に「お前がどう思ってようが関係ない、俺の言った物作れ」っていうのはリスペクトされないわけですよね。 質問 リスペクトされていない状態の人達もいると思いますが、そういう人はどうしたら良いですかね? まつもと やめる。 すごく我慢する人が多くて、景気がすごく悪いのは続いていて、ここにいないと僕はニートになってしまうかもしれないっていう恐怖感はあると思うんだけど、そのリスペクトしない人に対して奉仕しちゃうと、どんどん立場が下がっていく。 最終的には奴隷になっちゃう。it奴隷に。 それはダメだと思うので、どうするかっていうと、昔からそういう時は反乱を起こす。そういうエンジニアをリスペクトしない連中は見捨てるっていう。 どうせエンジニアがいなけりゃシステム作れないんだから。 そういうのをみんなでやって、技術者を大切にしないと自分の事業は立ちいかないっていう状態に追い込まないと、良くならないと思うんですね。 例えば、金山さんが「ファッション界に変革を起こしたい、itは単なる道具」という態度であればvasilyと共感できなかったと思います。 しかし、実際には金山さんも魂はエンジニアで、「テクノロジーが世の中を変えることができる、我々のフィールドはファッションだ」というテクノロジードリブンな発想だったので、これならお手伝いできるなと思いました。 質問 ちなみに、「こういう会社は気をつけた方が良い」っていう物はありますか? まつもと 「si」っていう単語が使用されている会社は8割9割ダメ。 siの、お客様の為にシステムを作るっていう構造が、win-winになりにくい。 顧客は出来るだけ安く、出来るだけ良い物を作ってもらいたい。作る方は出来るだけ少ない仕事で出来るだけ沢山お金を貰いたい。一緒の方向を向いていないんですよ。 作る方も最初に作りますって言った仕様を満たす最低ライン出せば良くて、納めてしまえば終わりで、お客様が成功したかなんて関係ないんですよね。 ほとんどの会社はシステムを売ってはいないから間接部門なのでコスト部門なんですよ。 社内でも切れ切れって言われていて、ちょっとでも安くしないとって物なので買い叩かれるのですけど、それだとエンジニアは幸せになりませんよね。 最悪のビジネス構図です。 なので、siはもう見捨てるしかないかなと。 システムインテグレーションっていう業種が壊滅したらもうちょっとマシになるんじゃないかな。 大企業にいて苦しんでいる人には「辞めたらいいんじゃない」って毎回言っているんですけど、やっぱ怖いんですね。フリーランスになるとか小さな会社に行くとかは。 心理的な障壁が大きいんですね。「頑張って大企業に入りました。それを辞められますか」と。 心理的な障壁に甘えて、siみたいな幸せにならない構造を維持していくのはどうかというのはありますね。 質問 まつもとさんはモルモン教の宣教師だったのですが、それがプログラミングやエンジニアリングに与えている影響ってあるのでしょうか? まつもと 相対化できないので、同じ条件でそういう経験のない私と今の私とは比べられないので、なかなか難しいですが。 大学生の時に2年間宣教師として奉仕したんですけど、人と凄くたくさん話をしたんです。何万人と。 その時割と人間を見たんですね。そういうのが役に立っているかもしれないなと思う時はありますね。 itってテクノロジーなんだけど、最後のファクターは人間なんですよね。 テクノロジー的にはトレードオフを提供できる。このボリュームをこっちにあげるとこうなって、こっちをあげるとこうなってっていうのはできるけど、じゃあ、そのトレードオフのボリュームをこうしますっていうのは人間が決めるんですよ。 絶対的な真理はなくて、100人が100人納得する意見っていうのはないので、テクノロジーを人間に合わせる部分が大事なんですね。 そこは多くの人と話をしたのが役に立っているかもしれないですね。 あとは2人1組で活動する事が多くて、だいたい僕の相棒はアメリカ人とか外人が多かったので、英語を使っていましたね。 今、僕が英語を喋っているのも基礎みたいなのはそこでできたと思いますね。 この2つは影響があったんじゃないかと。 質問 ざっくりとした質問で恐縮ですが、プログラミングの将来ってどうなるのかをお伺いしたいです。 まつもと 近い未来だと、ソフトウェアの規模はどんどん小さくなるんじゃないかな。小さいのをたくさん作るみたいな感じになるんじゃないかと思います。 それは何かというと、フレームワークであるとか、ライブラリであるとかが充実してきて、それを組み合わせるだけで一通りの事ができるという。 その時にrubyみたいな言語ってのはクラスライブラリとかを提供していって、プログラマーはスコアリングの調整やapi取り込めば良いだけみたいになって。 ターンアラウンドタイムがだんだん短くなっていくんじゃないかと思います。 遠い未来だと、コンピュータが人間の言葉を理解して、人間に変わってコンピュータが計算してくれてとか、いい感じにビジュアライズまでして出してくれるとかっていう感じになると思うんですけど。 そうするとプログラミングの楽しみがなくなるんだけど、僕はちょっとおじいさんになってもプログラミングしていて、「おじいちゃんがまた年寄りの趣味やってるよ」とか、「もう何十年前のやり方なんだろうね」っていうのをプログラムやりながら言われてそうな気がします。 今でも現実に普通の人はいろんな事を探すより先にgoogleしますよね。 あれがどんどん自然言語に近づいていって、「何かが知りたい」とかいうと、今でも普通に文章を打ち込む人結構いるんだけど、適当に出てきて用が済んじゃうみたいな。あれがどんどん進んでいくんじゃないかな。 あとはフロントはsiriみたいになって、どんどん自然に。 質問 その仕組みを作る側にも人はいるんですよね。 まつもと もちろん、当面はいますね。 でも、それもどっかから自動化されて。コンピュータの知能が十分高まっていくと、聞いただけで言葉を理解して、持っている情報からそれっぽいのを作り出してくれるような気がしますけどね。 プロプログラマーの仕事は2極化して、アカデミックな最先端のことやるのと、本当に簡単な事をやるのと。 個別のニーズに合わせて、「この業種だからこういう組み合わせをする」というのをやるエンジニアは当面はいると思います。 でもそれは誰でも出来ちゃうんで、何を差別化するかっていうところはキーポイントですね。 まつもとゆきひろ氏を囲んで 以上、当日話した内容のごく一部ですが紹介いたしました。 他にもエンジニア同士で言語や仕事、マネジメントについてなど、踏み込んだ話で盛り上がりました。 vasilyでは、一緒に切磋琢磨できるエンジニアの仲間を探しています。 まつもとさんを迎え、vasilyはエンジニアにとってさらに成長できる環境になりましたので、ご興味ある方は是非是非↓こちらまで! 世界を変えたいエンジニア募集
VASILYのiOSエンジニアは今年もWWDCへ参加しています。 同時に、WWDCへ参加する世界各国のエンジニアたちとの交流も深めています。 和服で来ています     WordPressのエンジニアと   ものすごい熱気です   iOSエンジニアとしてノウハウや今後の動向を学ぶだけでなく、文化や魂など、色々お土産を持って帰ってくる予定です!    
Androidエンジニアの村田です。チームメンバーが増えてきてますますテストコードの必要性を感じています。 ということでAndroidアプリの開発でユニットテストを導入しました。 挫折しないでユニットテストを始めるための3つのポイントを紹介していきたいと思います。 なぜ始めたか? まずはじめにそもそもなぜ始めたのかの理由を3つ紹介します。 プロダクトの品質を仕組みとして保ちたかった コードレビューはしてるが、あくまで人の目の確認となり抜け漏れが生じてしまいます。クラスやメソッド単位での品質は仕組みとして担保したいと思いました。 チーム内でクラス、メソッドの仕様についての共通認識を高めたかった そのコードは何を受け取ったらエラーとするのか?テストコードを書けばわかりやすくなると思います。自分のためにもチームメイトのためにもなると思います。 仕様変更によるバグ発生の回避 たくさんの機能をハイスピードで開発、改善、廃止などしていくうちにちょっとしたコードの変更が思わぬバグを生むことがあります。 予めテストが書かれていればコードを微妙に変えても異変にすぐ気がつけると思います。 このように始める理由は結構ありきたりな理由でした。 それでは本題に入っていきます。挫折しないようにユニットテストを始めるための3つのポイントを紹介していきます。 1.現状分析&シンプルな導入 2.自分のコードは完璧じゃないと疑う 3.メリットを明確に納得する 現状分析&シンプルな導入 始めるにあたってアプリケーションのどの部分から始めるかを決めるのは大事です。 どこから始めるかをしっかり絞って優先付けをしていくことで迷わずにユニットテストを導入することができます。 iQONではクラス単位でざっくりどういう役割があるのかを把握することから始めました。 iQONのAndroidアプリのクラス数 Activityクラスの数:73 Fragmentクラスの数:32 Adapterクラスの数:15 View関連の共通クラス数:30 モデルなどのデータ処理系のクラス数:45 iQONの機能のほとんどはバックエンドのAPIサーバ+画像サーバとhttp通信をすることで実現しています。 この処理を担当するのが1番下のデータ処理系のクラス(45クラス)になります。 ユーザから見えにくい処理はユニットテストにうってつけ ViewとかActivityとか表面に見える部分のバグは実機テストするときに人の目でも検知できますが、データ処理系は目視では検出しにくく、本当にその挙動が正しいのか正確に判断できないことがあります。こういった処理はユニットテストでバグを検知するにはうってつけだと思います。 処理の重要度で優先付け データ処理関連はアニメーションなどの表現に関する処理に比べるとシビアなのでまずはテストすべきだと思います。どんなにすごいアニメーションで表現しても肝心の処理がうまく行ってなければ本末転倒です。 導入は簡単に、シンプルなものから始めてまずは文化づくりから ユニットテストを始めるときに、あんまりいろんなことを最初からやってはいけません。気持ちの問題ですが、最初からあれこれやり過ぎて導入、開始までで疲れてしまって挫折してしまうこともあるかと思います。まずは簡単な仕組みから始めてテストをかく習慣、文化を作ります。 実際に導入するテストツールはいろいろ検討しましたが、Javaで書かれたコードをユニットテストするのにもっともメジャーでシンプルなJUnitを採用しました。弊社ではAndroidStudioを使用しているので、実際かなり簡単に始めることが出来ました。 具体的な導入については次回以降のエントリーで紹介していきたいと思います。 という3つの理由からJUnitによるユニットテストを開始しました。 自分のコードは完璧じゃないと疑う ちゃんと動いていると信じていたメソッドも意外とまともに動いてない テストコードを書くとなると容赦ないパラメータをメソッドに投げるわけですが、変な値を投げると意外とうまく動いていないことがあります。 設計を見直す良いチャンス テストコードを書き始めると、「あれ?テスト書きにくいクラスがあるな」と思うことが出てきます。これはテストが書きにくいクラスなのではなく、設計があんまりよくないクラスの場合が多いと思います。ちょっとiQONであった実例を紹介します。 public void setJSON(JSONObject json) { try { if (!json.isNull("category")) { String category = json.getString("category"); if (2 < category.length()) { // 省略(categoryを適当に変更する処理) this.category = category; } else { // 省略(categoryを適当に変更する処理) this.category = category; } } else { // 省略 } // 省略(他のインスタンス変数に対する処理がこのあとも多数続く) } catch (JSONException e) { e.printStackTrace(); } } バックエンドのAPIから返ってきたJSONのレスポンスを上のようなメソッドに渡して、 インスタンス変数の値を設定する処理がありました。 このメソッドは300行近くに達し、とても見通しの良いコードではなかったです。本来ならsetterを実装してそこで値の検証をして、インスタンス変数に値を設定するのがいいと思います。 public void setJSON(JSONObject json) { try { if (!json.isNull("category")) { String category = json.getString("category"); setCategory(category) } else { // 省略 } // 省略(他のインスタンス変数に対する処理がこのあとも多数続く) } catch (JSONException e) { e.printStackTrace(); } } public void setCategory(String category) { try { if (2 < category.length()) { // 省略(categoryを適当に変更する処理) this.category = category; } else { // 省略(categoryを適当に変更する処理) this.category = category; } } catch (JSONException e) { e.printStackTrace(); } } このようすればsetCategoryメソッドでいろんな値を設定してインスタンス変数の変更に関するテストがかけるようになります。 ちょっと設計を改善すれば、テストが書けるようになります。逆にテストを書こうと思ったので設計の見直しができたと思います。(今回の例だともともと処理を切り分けたいと思っていた気持ちがより強くなりました) メリットを明確に なぜテストをやるのか?そのメリットを明確にしてやるべきだと思います。 私自身もテストを書くのに挫折したことがありましたが、挫折する理由の一つとしてメリットを納得できてなかったからだと感じています。 自分なりに納得したポイントをざっくり上げたいと思います。 1. コードの品質があがる 言わずもがな、テストをかけばその分コードの品質は上がります。これは保守するコストを下げるメリットがありますね。 2. テストを意識した、きれいな設計を保てる きれいな設計を保てるということは保守するコストもそれなりに下がっていきます。これもメリットですね。 3. テストが用意されているので思い切った開発ができるようになる テストが用意されているので、ちょっと処理の内容を変えてもテストを流せばおかしいところはすぐに検出できます。 この処理を変えたらあちこちに影響ないだろうか?と開発時に迷う気持ちを抑えられると思います。不安ならテストを流せばいいわけです。 あれこれ影響範囲を調べたりする開発時のコストを下げることもできそうです。これもメリットと言えるでしょう。 4. テストコードが仕様書になる どんな値を受け取って、どんな場合にエラーを返すか。みたいな仕様はテストコードを見れば一目瞭然なので、仕様書の代わりになります。ドキュメントとして細かい仕様をまとめるのも大事ですが、テストコードを代わりにしてみるといろいろ無駄が省けるかもしれません。 まとめ 現状分析&導入はシンプルに 現状のアプリの中でテストを書き始められる箇所がどこか見極める。大掛かりなしくみを最初からやろうとしないで小さなことから始めてみる。最初から飛ばし過ぎると導入の時点で疲弊して続きません。 自分のコードを疑う 自分が書いたコードは完璧じゃないと疑う。テストコードを書いてみて初めて気がつくことはよくあります。 メリットを明確に メリットを明確にして、きちんと納得して始める。 次回は実際の導入の紹介やTravisCIとの連携などを紹介していきたいと思います。 しばらくテスト関連のエントリーが続きますが、楽しみにして頂けたらと思います。 さいごに iQONではエンジニアを募集しています。一緒に世界を目指すAndroidアプリの開発をしませんか? 【Android】世界で勝ちたいAndroidアプリエンジニア募集 情報交換なども随時していきたいと思いますので勉強会などの開催、参加についても気軽に相談頂ければと思います。
iOSエンジニアの庄司です。 先月2月20日、『【第2回】iQONエンジニアセミナー ~進化し続けるiQONを支えるグロースハック的エンジニアリング手法とは?~』と題して、エンジニアセミナーを開催しました。 会場となった弊社オフィスにたくさんのエンジニアの方々に集まっていただきました。 当日は、弊社エンジニアが『グロースハック』をテーマに各プラットフォームがどのようにiQONのグロースハックに取り組んでいるかを発表しました。 当日の発表資料をSpeaker Deckにアップしたのでぜひ御覧ください。 グロースハックを支えるエンジニアリング CTO 今村 Androidファースト/より早く提供するための開発手法 Androidチーム (西村/村田) グロースハックさせるためのiOS開発手法 -ログ収集編- iOSチーム (荒井/庄司) iQONを支えるバックエンド バックエンドチーム (吉田/大貫) グロースハック的開発 最初の一歩 LT枠 (かとあつ) まとめ グロースハックにフォーカスしての各プラットフォームで発表しましたが、時間の都合上紹介できたのはグロースハックに関して取り組んでいることの一端にすぎません。 この他に取り組んでいることをどんどんこのブログで紹介していきたいと思います。 各資料の最後でも触れていますが、今回のセミナーや発表資料でiQON面白そう!と思っていただけた方をお待ちしております。 日本最大級のファッションサービスiQONをぶっちぎりにするエンジニア募集!
ウェブサービスに限らず、アプリの継続率を上げるためには離脱率や離脱ページの計測は重要です。 離脱率の高いページを知り、改善することでアプリの継続率やコンバージョン率の向上につなげることができます。 特に新規ユーザーの離脱ページを改善すると継続率への影響は大きいでしょう。 今回は、iOSアプリの離脱ページ計測について3点にわけて紹介します。 iOSアプリにおける離脱とは 最前面のUIViewの特定 最前面のViewController (離脱ページ) の特定 iOSアプリにおける離脱とは iOSアプリにける離脱とは、ホームボタンを押したり、かかってきた電話に出たりなど、そのアプリケーションの利用が終了することと同義です。 このタイミングは UIApplicationDelegate の applicationWillResignActive: というデリゲートメソッドで検知できます。 このタイミングで最終的に表示していたページ(=ViewController)を取得できればよいのですが、アプリケーションの実装によっては同時に複数のViewControllerが存在するため特定が困難です。 ここでは applicationWillResignActive: が呼ばれた時点で最前面にあるViewControllerを離脱ページとして計測すればよいでしょう。 最前面のViewControllerを取得するには、下記の2ステップです。 最前面のUIViewを特定 そのUIViewが属するUIViewControllerを特定 Google Analyticsなんかでもやってることでしょ? Google Analyticsでも画面ごとの離脱率を計測することができます。 ですが、今回は新規ユーザーについてのみ、どの画面で離脱したかを計測するために実施しました。 最前面のUIViewの特定 最前面のUIViewを取得するには UIWindow から始まるsubviewsツリーの最後のViewを再帰的にたどって行くことで最前面のUIViewが取得できます。 最終的に applicationWillResignActive: で取得するので、UIApplicationDelegateのクラスでメソッドを実装します。 アプリケーションの最前面のUIViewを取得する処理の実装例 @implementation MyAppDelegate - (UIView *)foregroundView { // UIWindowから始まるView階層を再帰的にたどる UIView *foregroundView = self .window; while ( 1 ) { UIView *tmpView = nil ; for (UIView *subview in foregroundView.subviews.reverseObjectEnumerator) { // 非表示のviewは除く if (!subview.hidden && subview.alpha > 0.f ) { tmpView = subview; break ; } } if (tmpView) { foregroundView = tmpView; } else { break ; } } return foregroundView; } @end 最前面のViewController (離脱ページ) の特定 さきほど取得した最前面のUIViewのレスポンダチェーンをたどって行くと、そのUIViewが属するUIViewControllerが取得できます。 UIResponderクラスのカテゴリのメソッドを実装します。 UIViewのレスポンダチェーンから自身が所属するUIViewControllerを取得する処理の実装例 @implementation UIResponder (Addition) - (UIViewController *)belongingViewController { UIResponder *responder = self ; while ( 1 ) { if (!responder) { return nil ; } else if ([responder isKindOfClass:[UIViewController class]]) { return (UIViewController *)responder; } else { responder = responder.nextResponder; } } } @end 最終的に最前面のUIViewControllerを取得して計測する処理は下記のようになります。 最前面のUIViewControllerを取得して計測する処理の実装例 @implementationMyAppDelegate - (UIViewController *)foregroundViewController { // 表示されている最前面のViewControllerを返す UIViewController *vc = self .foregroundView.belongingViewController; if ([vc isKindOfClass:[UINavigationController class]]) { // UINaviagtionControllerの場合はtopViewControllerを返す return ((UINavigationController *)vc).topViewController; } else { return vc; } } - ( void )applicationWillResignActive:(UIApplication *)application { // 会員登録から24時間以内のユーザーのみログを落とす if ([[NSDate date] timeIntervalSinceDate:USER.createTime] <= 60 * 60 * 24 ) { UIViewContrller *vc = self .foregroundViewController; // ロギング処理 } } @end 再帰処理をたくさんやってるけどパフォーマンスは? 計測処理が遅くてアプリケーションの使い勝手が悪くなるような事態は避けなければなりません。 今回は再帰処理を使っていますが、実機で確認してみたところ、1msecかからず返ってきました。 subviewsのツリーとレスポンダチェーンをたどるだけの処理なので、大した負荷はないようです。 まとめ 離脱率を計測するために、アプリケーション終了時に最前面のViewControllerを取得する処理を紹介しました。 レスポンダチェーンをたどることで、UIViewのサブクラスからView自身が所属するViewControllerを取得することもできますが、ViewがViewControllerの存在を意識する必要はないのでおすすめしません。 計測をすることはサービス改善の第一歩です。計測結果を解析してサービス改善に役立ててください。 最後に 今回の記事に物申す!という方はこちらまで↓ 【iOS】Appleも認めたiQONにさらに磨きをかけるエンジニア募集
近年O2O(Online to Offline)が販売促進のマーケティングなど様々な分野で脚光を浴びていますが、O2Oを絡めた機能をアプリで提供するために不可欠な要素として 位置情報サービス があります。 今回はAndroidアプリで位置情報サービスを効率よく使うための2つのTipsと実装例を紹介していきます。 1,『最新の位置情報サービスの罠』を対策しアンインストールを回避 2,最新機能『Google Play ServicesのLocation API』を使って効率UP Tips1:『最新の位置情報サービスの罠』を対策しアンインストールを回避 Android4.4(KitKat)から位置情報サービスの設定画面が変更されました。大きな変更は以下の2点になります。 高精度・バッテリー節約・GPSのみ の3つのモードから選択できるようになった 位置情報サービスを直近で利用したアプリが一覧表示されるようになった 特に後者の「アプリが一覧で表示される」変更に関しての対策は、今後の位置情報サービスを使うアプリ開発においては非常に重要な要素になってくると考えております。 実はこのアプリ一覧の各アプリの項目はタップすることができ、タップするとアプリ情報の詳細ページにIntentされる仕組みになってます。 ご存知の通り、このページに来るとアプリのアンインストールができてしまいます... 電池の減りの早さに悩んでいるときに「高い電池使用量」という文言を見かけ、アンインストールできるページにIntentしたら普通ならどうするでしょう? 位置情報サービスを使う際には 仕様にあわせて最適な選択をし、必要以上に位置情報サービスを使わない ということが今後は鉄則になってきます。 そうすることで アンインストールのリスクを回避 することができます。 iQONでの位置情報サービスを利用した事例 先日iQONでは、Androidで先行して天気コーデ機能をリリースしました。設定したエリアの天気予報と、天気予報にマッチしたコーデが見れる機能になっています。 当たり前な話ですが天気は地域によって全く異なってくるものです。そのためユーザーに自分が住んでいる場所を選択してもらう必要があります。 ただ、膨大な地域情報から自分が住んでいる地域を選択していく操作は手間がかかり、機能の利用率向上の足かせになります。 そこでiQONでは手動での位置情報設定に並行して、位置情報サービスを使った自動位置情報設定の機能を提供しました。位置情報の取得に関しては従来の仕組みである android.location.LocationManager android.location.LocationListener を使っています。 先ほども記載しましたが、位置情報サービスは 仕様にあわせて最適な選択をし、必要以上に位置情報サービスを使わない という鉄則が大事になってきます。 これを頭に入れた上で、以下の2つのポイントに気をつけて実装を行いました。 天気エリアは大まかな位置情報が取得できれば十分な機能を提供できるため精度は多少落ちても良い 一度位置情報が取得できれば機能に利用できるので位置情報を継続して取得し続ける必要はない public class WeatherSpotAutoSetActivity extends Activity implements LocationListener { LocationManager locationManager; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.weather_spot_auto_set); locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); String gpsStatus = android.provider.Settings.Secure.getString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED); if (gpsStatus.indexOf(LocationManager.NETWORK_PROVIDER) > 0) { // 位置情報の取得処理開始 // 大まかな位置情報が取得できれば要件を満たせるのでネットワークで取得する処理を走らせる(省電力) locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this); } else { // 位置情報サービスがOFFの場合は処理を終了 finish(); } } @Override protected void onDestroy() { locationManager.removeUpdates(this); super.onDestroy(); } @Override public void onProviderDisabled(String provider) { } @Override public void onProviderEnabled(String provider) { } @Override public void onStatusChanged(String provider, int status, Bundle extras) { } @Override public void onLocationChanged(Location location) { String latitude = String.valueOf(location.getLatitude()); String longitude = String.valueOf(location.getLongitude()); // 【重要】緯度経度が1度でも取得できた時点で処理を終了 locationManager.removeUpdates(this); // 取得した緯度経度を使って処理を行う(処理の詳細は省略) } } iQONではネットワークからの取得に絞っているため、位置情報サービスの設定画面には「低い電池使用量」としか表示されません。 Tips2:最新機能『Google Play ServicesのLocation API』を使って効率UP 前項でiQONは現状 android.location.LocationManager android.location.LocationListener を利用して位置情報を取得していると紹介させていただきましたが実はこれは古い仕組みで、2014年3月現在では Google Play ServicesのLocation API を利用するのが最も新しい形になります。 昨年のGoogle I/Oで新しく発表された機能です。 OSのバージョンがAndroid2.2以上、「Google Play 開発者サービス」がユーザーの端末にインストールされている必要がありますが、リアルタイムで位置情報を取得しないといけないアプリに関してはLocation APIを使用した方が圧倒的に効率が良くなります。 iQONにおいても今後の展開を考えて、近日中にLocation APIに乗り換える予定です。 Location APIは従来の仕組みと比べていくつか優れた仕組みがありますが、その中でも実際に使ってみて一番良いと思った仕組みは Fused Location Provider というものになります。 従来の仕組みでは位置情報を取得する際には LocationManager.GPS_PROVIDER や LocationManager.NETWORK_PROVIDER と指定する必要がありました。 Criteriaクラスをオプション的に用いて精度や消費電力を指定して組み合わせる方法もありましたが正直使い勝手はあまり良くありませんでした。 Fused Location Provider は、従来の仕組みとCriteriaクラスが統合されたような仕組みになっていて、精度と消費電力のどちらを優先させるかを選択するだけでProviderの指定は行う必要がなくなりました。 利用シーン1:高精度でリアルタイムに位置情報を取得したときの実装例 public class TestActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener { /* * implementsするLocationListenerはcom.google.android.gms.locationのもの * 既存の仕組みから移行する場合にはこれに気をつけないとハマります */ private LocationClient mLocationClient; private LocationRequest mLocationRequest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test_activity); // Google Play Services が利用できるか確認 int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this); if (ConnectionResult.SUCCESS == resultCode) { mLocationClient = new LocationClient(this, this, this); } else { // 従来の仕組みで代用するなりのプランを考える } } @Override protected void onStart() { super.onStart(); mLocationClient.connect(); } @Override protected void onStop() { mLocationClient.disconnect(); super.onStop(); } @Override public void onConnectionFailed(ConnectionResult connectionResult) { // 接続が失敗したときの処理はここに記載 finish(); } @Override public void onConnected(Bundle bundle) { mLocationRequest = LocationRequest.create(); mLocationRequest.setInterval(5000); // 5秒ごとに位置情報を取得 mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); // 高精度で取得 if (mLocationClient.isConnected()) { mLocationClient.requestLocationUpdates(mLocationRequest, this); } } @Override public void onDisconnected() { // 切断したときの処理はここに記載 if (mLocationClient.isConnected()) { mLocationClient.removeLocationUpdates(this); } } @Override public void onLocationChanged(Location location) { String latitude = String.valueOf(location.getLatitude()); String longitude = String.valueOf(location.getLongitude()); // 取得したLat,Lonを使って処理を行う(詳細の処理は省略) } } } 利用シーン2:ある程度の精度で常時位置情報を取得したいときの実装例 public class TestActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener { ... /* 他の箇所は利用シーン1と同様のため省略 */ @Override public void onConnected(Bundle bundle) { mLocationRequest = LocationRequest.create(); mLocationRequest.setInterval(3600000); // 1時間ごとに位置情報を取得 // setIntervalはあくまでも目安のため、状況によって間隔が変わる // そのため長時間取得取得する場合はsetFastestIntervalも設定する mLocationRequest.setFastestInterval(300000); // 取得の頻度は最低でも5分は空ける mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); // 精度と消費電力のバランス取って取得 if (mLocationClient.isConnected()) { mLocationClient.requestLocationUpdates(mLocationRequest, this); } } ... } 利用シーン3:大まかな位置情報を1度だけ取得したい場合の実装例 public class TestActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener { ... /* 他の箇所は利用シーン1と同様のため省略 */ @Override public void onConnected(Bundle bundle) { mLocationRequest = LocationRequest.create(); mLocationRequest.setPriority(LocationRequest.PRIORITY_NO_POWER); // 電力を使わない範囲で取得 // PRIORITY_NO_POWER よりも精度が高い PRIORITY_LOW_POWER でもテストをしてどちらを使うか決める if (mLocationClient.isConnected()) { mLocationClient.requestLocationUpdates(mLocationRequest, this); } } @Override public void onLocationChanged(Location location) { String latitude = String.valueOf(location.getLatitude()); String longitude = String.valueOf(location.getLongitude()); // 取得できた時点で処理を終了させる if (mLocationClient.isConnected()) { mLocationClient.removeLocationUpdates(this); } // 取得したLat,Lonを使って処理を行う(詳細の処理は省略) } ... } パラメータの値は実装する機能にあわせて細かくテストして最適な形にする。 まとめ 位置情報サービスは非常に便利ですが、今後は利用するアプリは一覧表示されて晒されることを理解した上で利用してください。 そのため 仕様にあわせて最適な選択をし、必要以上に位置情報サービスを使わない ことを徹底しないとアンインストールのリスクに繋がります。 また位置情報取得は従来の仕組みでもいいですが、 Google Play ServicesのLocation API はより細かい設定ができ使いやすいのでオススメです。 さいごに 日々成長している 日本最大級ファッションサービスiQON を一緒に成長させてくれるエンジニアを絶賛募集しております。 我こそはという方お待ちしております。 【Android】世界で勝ちたいAndroidアプリエンジニア募集 日本最大級のファッションサービスiQONをぶっちぎりにするエンジニア募集!
バックエンドエンジニアのBoBです。 今回はiOSプッシュを最適化するためには必要不可欠な、Apple Push Notification service(以下APNs)のFeedbackサービスについて紹介したいと思います。 はじめに iPhoneやAndroidにはプッシュ通知という仕組みがあることは皆さんもよくご存知かと思いますが、より洗練された、効果的なプッシュ通知を送るために自分たちのプロダクトが送っているプッシュ通知を分析する事はとても重要な事です。 今回はiPhoneのFeedbackサービスを利用して、プッシュ通知を送れていないユーザーを把握する事に関して 1. プッシュ通知の意義 2. Feedbackサービスの説明 という2点から紹介しようと思います。 プッシュ通知の意義 iPhoneやAndroidのプッシュ通知は、ユーザーのRetention(再訪率)を高める効果があります。 弊社グロースハックブログの 広告費0円でもDAU10倍!? ユーザーRetention(再訪率)を最大化する施策10選 の記事にも、Retentionを上げる意義やそのためのプッシュ通知の必要性などが書いてあるのでぜひ参考にしてください。 しかし、ただ闇雲にプッシュ通知をユーザーに送っていてもプロダクトのRetentionを高める事にはつながりません。 プッシュ通知を最適化するためには、実際にプッシュ通知の様々な部分を日々工夫し、その結果どのようなプッシュ通知がユーザーのRetentionを高める効果があるかを分析しなければいけません。 その分析を行うためには様々な数値を正確に知る必要があります。 プッシュ通知改善の為に日々変更を加える部分は大きく分けて以下の2点があるかと思います。 ・タイミング(プッシュ通知を送る時間) ・内容/見た目 これらを変更した際の効果を測定するためには以下のような数値を知る必要があります。 ・ 何人のユーザー(何個の端末)を対象としたプッシュ通知なのか ・ 実際には何人のユーザーに送信されているのか ・ 何人のユーザーがプッシュ通知からアプリを起動したか APNsのFeedbackサービスでは、 「実際にユーザーに届ける事ができなかったプッシュ通知の情報」 を取得することができます。 つまり、上記の例で言うと二つ目にある「実際に何人のユーザーに送信されているか」というデータを知るために必要不可欠な仕組みとなっています。 Feedbackサービスとは? APNsにはFeedbackサービスという、正常に処理できなかったプッシュ通知に関する情報を返す仕組みがあります。 Feedbackサービスに接続したときに得られる情報は、前回Feedbackサービスの情報を取得した以降、配送に失敗した通知に関するものだけで、Feedbackサービスが保持しているリストは読み取り後クリアされます。 Feedbackサービスの導入方法 Feedbackサービスはプッシュ通知の仕組みを実装していれば非常に簡単に導入でき、使い方次第でサービスのグロースに絶大な効果を上げる事ができます。 1. 各種設定 Feedbackサービスを利用する際は以下のサーバー、ポートにSSL接続します。 key, certの設定はプッシュ通知を送る際と同じpemファイルを設定します。 [ruby] require 'socket' require 'openssl' # 開発環境 apns-dev-key-noenc.pem, apns-dev.pem # 本番環境 apns-prd-key-noenc.pem, pns-prd.pem context = OpenSSL::SSL::SSLContext.new('SSLv3') context.key = OpenSSL::PKey::RSA.new(File.read("apns-prd-key-noenc.pem")) # プッシュ通知を送る処理と同じpemファイル context.cert = OpenSSL::X509::Certificate.new(File.read("pns-prd.pem")) # プッシュ通知を送る処理と同じpemファイル [/ruby] 2. 結果取得処理 ssl通信で結果を取得します。 [ruby] # 開発環境: feedback.sandbox.push.apple.com # 本番環境: feedback.push.apple.com sock = TCPSocket.new('feedback.sandbox.push.apple.com', 2196) # Feedbackサービスのポートは2196で固定 ssl = OpenSSL::SSL::SSLSocket.new(sock, context) ssl.sync = true ssl.connect while line = ssl.read(38) # SSLソケットから38バイト取得する  # feedbackで取得したデータを用いた処理をここで実装する p line.unpack('N1n1H140') end ssl.close sock.close [/ruby] 3. レスポンス 上記の結果は以下のような形で出力されます。 [ruby] [1392300474, 32, "#プッシュ対象のdevice id"] [1392303094, 32, "#プッシュ対象のdevice id"] [1392295196, 32, "#プッシュ対象のdevice id"] [1392301807, 32, "#プッシュ対象のdevice id"] ........ [/ruby] Feedbackサービスから取得できる情報で利用できるものはプッシュ通知対象のdevice idのみですが、この各レスポンスからどのユーザーのプッシュ通知かを判断し、分析をかける事ができま す。 まとめ 今回APNsのFeedbackサービスによる送信失敗結果の取得方法を紹介しました。しかしここで説明した内容を実装しただけではサービスのグロースには何もつながりません。 ここで取得したデータをいかにうまく使って分析していくかが重要であり、今回紹介した内容はプッシュ通知最適化の入り口でしかないのです。 現にVASILYではFeedbackサービスで取得したデータ以外にも様々なログを用いてユーザーに送るプッシュ通知を分析し、より効果的なプッシュ通知を目指して日々改善しています。 最後になりますが、VASILYでは現在エンジニアを大募集しています! 直近ですとiQONを支えているアグレッシブな技術がどのようなものか紹介する勉強会や、会社での飲み会が予定されています。 【第2回】iQONエンジニアセミナー ~進化し続けるiQONを支えるグロースハック的エンジニアリング手法とは?~ 【2/21】「iQON」急成長を支えるエンジニアと話したい人、WANTED VASILY/iQONに少しでも興味がある方はぜひご参加ください!
  理系的素養は有るけれどもウェブ経験が全く無い学生インターンが、9日間で立派なウェブ系エンジニア になったので、そのポイントをまとめました! ざっくり概要 Getting Started with Rails をやる ユーザに届ける体験をする Pull Request のやりとりをする SQLをいじる ウェブで使うミドルウェアとインフラを知る JSFIDDLE を触る ウェブ業界について知る そしたら取り敢えずウェブ系エンジニアとして仕事ができます!   インターンで来たのはこんな人 ウェブ系の採用イベントに参加した際に、多くの方が「Twitterの分析をしています!」とか「SNSの実装をしています!」とか「検索で使えるアルゴリズムの研究をしています!」と、ウェブ系のアピールをされている中、ただ一人ウェブとは関係ない、超目立つロボットを持ってきている方がいました。 激しく動き、歌い、光るロボット。 話を聞くと、「ウェブやりたい!でもウェブ良くわからないから作れるもの持ってきた!プログラムは大好き!ファイルシステムくらいなら自分で実装できる!ウェブやりたい!」 ってことだったので、東北と遠方の方ではあったのですが、インターンで2週間だけ来て頂きました。 やった事 インターンまでの宿題 Getting Started with Rails  をやる heroku  でアカウント作ってデプロイする ウェブの勉強するのにおすすめの書籍は何か?みたいな話になった時に、取り敢えず手を動かすのを優先して欲しかったのと、作った物を自分一人ででも世に出す方法を知って欲しかったので、上記二つを私からの宿題にしました。 初デプロイ(初日) Virtual Box  で開発環境構築 Github  開設 Railsで簡単なサイトの修正 ( The NERD Tree  使ってもらった) Capistrano でデプロイ 色々な説明は端折りながらも、スピード優先で実装からデプロイまでを経験してもらいました。内容は簡単なリンクURLの差し替えです。 最初のデプロイ時は手が震え、家に帰ってからは嬉しくてその箇所を何度も見直したそうです。 基本的な開発からデプロイまでの確認(2日間) MVCについてとRailsのMVCについて説明 どんな意図でどのファイルを修正するかシェア RailsでAPIの実装 Pull Request やり取り RSpec でテスト書く 最初に実装からデプロイまでの流れを通しで経験している(=デプロイの怖さ等も理解している)状態だと、Pull RequestやRSpecの必要性やMVCについて、言葉上の理解ではなく体感として理解してもらえたと思います。  バックエンド/インフラの理解(3日間) AWS, MongoDB, Redis, MySQL,memcached の概要と、iQONで何を担っているかシェア MySQLのクエリ最適化 をひたすらやる 障害対応見学 バックエンドとインフラについては、ヨッシーさんに見てもらいました。ハーコーな方なので、私一人からは教われない物を色々学んだはずです。 障害は予期していなかったものですが、彼にとっては幸運だったと思います。普段はチャットでコミュニケーションしつつ各々のペースで開発しているメンバーが、高い緊張感の中でノートPCを寄せ合って密に連携をとりながら各プラットフォーム対応していく状況でした。そこから感じ取るものは有ったと思います。  フロントの実装(3日間) JSFIDDLE  触る HTML書く( zen-coding  使ってもらった) CSS書く スマホ/PCブラウザ対応する JavaScript書く(Chromeの開発者ツール使ってもらった) JSFIDDLE はHTML/CSS/JavaScriptが書けて、メジャーなライブラリのインクルードも簡単に出来るサービスです。気軽に試せるサンドボックスで、初学者にとっては学習速度が必ず上がるものです。 彼の自前のherokuのプロジェクトにGoogle AnalyticsとAdsenseを入れてもらいました。インターン期間中には教えられなかった、計測と改善の楽しさを自分なりに体験して欲しいです。 他 tmuxで画面共有 vimの変な癖があったら治すため、vimlogとった(けどあまり使わなかった) ウェブ業界について、今イケてる(と我々が感じている)サービスとか、IPOとか、元気なベンチャーとか、大企業のウェブについてとかを、みんなで好き勝手教える 取り敢えずfacebookのアカウント作らせる 結果 どれくらいに到達したかを表にしました。レベルは今まで見てきた複数の職場で流通していたチケット(多分難易度はウェブ系では一般的)の内、こなせる率はどれくらいかを示しています。 領域 レベル 対応可能なケース NGそうなケース HTML CSS 40% HTML5でのvalidなマークアップはできる 既存コードを参考にした要素の追加削除とかも出来る 細かいデザイン等はキツい IEががががが。。。 JavaScript 60% 一般的な物はいける 複数の非同期通信が絡むようなのは楽しそうにやっていた CSSのデザインが絡むようなのはキツい Ruby on Rails 60% 一般的な物はいける MongoDBとか、何らかのミドルウェアが絡むとキツい 急ぎ案件はキツい 元は言わずもがな全て0%。9日間でここまで出来るようになるとは思っていなかったので、教えていて非常に楽しかったです。 彼自身の地頭や根性やバックグラウンドもあったとは思いますし、英語が出来ないと宿題の時点で詰んだりする訳ですが、やはり「ウェブをやりたい!」という熱い気持ちが成長を後押ししていたように見えました。 ざっくり概要(再度) Getting Started with Rails をやる ユーザに届ける体験をする Pull Request のやりとりをする SQLをいじる ウェブで使うミドルウェアとインフラを知る JSFIDDLE を触る ウェブ業界について知る そしたら取り敢えずウェブ系エンジニアとして仕事ができます! とは言え、当然まだまだまだまだ覚える事は沢山あるので、あくまでスタート地点に立ったというイメージです。 最後に 弊社は大企業でも官公庁でもないので、それらがエンジニアに与えてくれる物の多くは弊社では得られません(特に今は)。 ただし、「成長」の一点はどこよりも得られます(特に今は!)。 人生変わる程の成長がしたい!と思ったら、是非是非 VASILYサマーインターン へ! (遠方の方でも、旅費・宿泊費は会社で負担しています!)
  iOSエンジニアの荒井です。 今回はXcodeプロジェクトのライブラリ依存関係を簡単に管理出来る” CocoaPods ”の紹介をします。 未導入の方はオープンソースの管理が劇的に変わるので是非導入してみてください。 はじめに 最近iOSの勉強会などに参加すると、CocoaPodsを導入していることが前提で話が進む事が多くなりました。また、iOSエンジニアと話す際も「CocoaPodsで管理していますか?」という質問を頂きます。 導入していないことによって不便をきたすことがないようにしたいですね。 もちろん、単に時代の流れの面でだけでなく、メリットの部分が非常に多いと感じています。 今回のエントリでは 1. CocoaPodsのメリット 2. CocoaPodsの導入手順 に焦点を当てて紹介していこうと思います。 Cocoa Podsの導入メリット 冒頭にも書きましたが、CocoaPodsは「 Xcodeプロジェクトのライブラリ依存関係を簡単に管理出来るツール 」です。 iOSの開発をはじめて、オープンソースの利用をする時、GitHubなどからソースコードを持ってきて プロジェクトに入れてという作業を行うと思います。 これは非常に手間であり、特にRubyGemsやyumを使用した経験がある方からすると 「もっと簡単に管理できないか」と考えると思います。 CocoaPodsはこの問題を解決するアプローチの一つであり、コマンドを実行する事によってこれらの 問題を簡単に解決してくれます。   CocoaPodsの導入 CocoaPodsの導入は非常に簡単です。 いくつかのコマンドを実行するだけでインストールが完了します。 では早速流れを見てみましょう。 1. CocoaPodsのgemインストール $ sudo gem install cocoapods  ※ CocoaPodsはRubyのgemとして提供されているのでruby環境が必要です。 2. セットアップ 次に~/.cocoapods の作成を行います。CocoaPodsがライブラリ管理を行うディレクトリです。  $ pod setup 3. Podfileの設定 hoge.xcodeprojが存在するディレクトリにPodfileを作成します。 今回はiQONのPodfileをサンプルとして記載します。 iQONではAFNetworkingやソーシャル連携(Facebook)のテストを実施しました。 [ruby] platform :ios, '6.0' #プラットフォームの指定 iOS6.0以上 xcodeproj './iQON.xcodeproj' pod 'AFNetworking', '~> 2.0' #2.x以下のAFNetworkingライブラリを使用 pod 'AFNetworkActivityLogger', '~> 2.0' pod 'Facebook-iOS-SDK', '~> 3.0' [/ruby] CocoaPodsで導入出来るライブラリについてはこちらで検索をすると便利です。 http://cocoapods.org/ 4. インストール $ pod install 5. ライブラリの確認 Xcodeを一旦閉じてライブラリの確認を行います。この際、Xcodeでxcworkspaceを開きます。 上記のようにライブラリが確認出来れば完了です。 通常通りimportして使いましょう。 #import "Facebook.h" まとめ VASILYのiOSエンジニアは元々rubyを使っていたので、rubyのbundler感覚で使えるCocoaPodsの導入は全く抵抗がありませんでしたが、ライブラリ管理ツールの経験が無い方でも簡単に導入出来ると思います。 まだCocoaPodsを導入していない方は、ぜひこの機会に導入を行い、快適なオープンソースライフを過して頂ければと思います。 最後になりますが、VASILYではエンジニアを募集しています。 【2/21】「iQON」急成長を支えるエンジニアと話したい人、WANTED エンジニアセミナーも実施が決まったので、皆様ふるってご参加ください。 【第2回】iQONエンジニアセミナー ~進化し続けるiQONを支えるグロースハック的エンジニアリング手法とは?~  
どうも。バックエンドエンジニアの吉田です。 前回は1サイトをクロールする際の最適化戦略としてRedisベースの分散ロック機構を使った実例を紹介しました。 前回の記事:Redis::DistMutex – 時限付き分散ロックで効率良くサイトクロールをしよう 今回は複数サイトに対する処理をResqueを使って最適化した事例を紹介したいと思います。 ※ランダムにキューをlistenする話の予定でしたが、話がとっ散らかるので主題を変更しました。 主なキーワードとしては、「Resqueのキュー分割」、「Rubyでクラス定義を動的生成」といった感じです。 おさらい 前回使った図を使います。 iQONのクローラーは、提携サイトの商品一覧から商品ページのURLを取得し、ページをダウンロードする処理(fetchフェーズ)を必要な数だけWorkerプロセスとして起動しておき、Resqueを使って処理をしています。ダウンロードが終わったら順次解析する処理(processフェーズ)にResque経由で処理を引き継ぎます。これはクローラーによくある設計です。 今回のお題は、このダウンロード処理を複数サイトに対して並列に良い感じに処理させるための最適化事例とその実装例です。 キューの分割 このような設計にした場合、ページのダウンロード処理は複数のWorkerに分散されますが、複数サイトを同時にクロールした場合、enqueueされるURLの順序は予測できません。 このダウンロード処理用のキューが1つしかないと、キューは順番にdequeueされるので1サイトのURLが連続する可能性があります。1サイトに対するHTTPリクエストは時限付きMutexによって処理スピードが制限されるため、他ドメインの処理がブロックされてしまいます。 サイトごとにキューを用意すれば、キューの順序にかかわらず1サイトの処理が他サイトの処理をブロックすることもありません。 キューの状態を図にするとこんな感じです。 ただし、Resqueの場合はキューの指定をクラス変数によって定義する前提であるため、クロール対象数だけその定義を用意しなくてはなりません。dequeして得られたURLをダウンロードする処理はサイトに依らず共通であるのに、クラス定義を複数用意するのは非効率ですし、Workerの起動もサイト数分指定しなくてはいけません。 問題の解決策として、動的にクラス定義を生成する手法を採用しました。 動的にWorkerクラスを生成する ダウンロード処理をするクラステンプレートを用意して、それを設定ファイルに定義されたサイトのリストをもとに複数のクラス定義を動的生成します。 Workerを起動するときは定義されたサイトIDから全部のクラス定義を生成すればよく、enqueするときも同様の戦略で問題解決できます。 実際のコードを見たほうが理解しやすいでしょう。 iQONではサイトをdomainと読んでいるため、コードもそれに倣います。 クラス設計概観 実際のクラステンプレートは以下のように実装しています。 サイト別定義を動的生成するための抽象クラス定義 module Crawler :: Workers class DomainSpecificWorker def self . domain_id= (domain_id) name = "#{ instance_variable_get( :@queue ) } - #{ domain_id }" instance_variable_set( :@queue , name.to_sym) instance_variable_set( :@domain_id , domain_id) end def self . domainify (domain_id) klass_name = "#{ name.split( ' :: ' ).last } _ #{ domain_id }" unless Crawler .const_defined? klass_name Crawler .const_set(klass_name.to_sym, clone) klass = Crawler .const_get(klass_name.to_sym) klass.domain_id = domain_id end Crawler .const_get(klass_name.to_sym) end end end ページをダウンロードするWorkerクラスの定義 module Crawler :: Workers class FetchPageWorker < DomainSpecificWorker @queue = :fetch_page def self . perform (params) # 実際のダウンロード処理は別クラスに定義して、それを呼び出すだけ end end ※今見ると、DomainSpecificWorkerはモジュール化してMix-inしたほうがRubyらしい感じもしますね… Before(静的にクラス定義を用意する場合) enqueする時のコード例 args = { domain_id : 1 , url : ' http://... ' } Resque .enqueue( FetchPageWorker1 , args) Workerの起動スクリプト domain_ids = config.get( :domain_ids ) # ドメインごとのクラス定義を読み出し queues = domain_ids.map { | domain_id | Crawler :: Workers .const_get( " FetchPageWorker #{ domain_id }" ) } # ランダムに並び替えた優先順でワーカーを起動 worker = Resque :: Worker .new(*queues.shuffle) worker.work After(動的にクラス定義を用意する場合) enqueする時のコード例 args = { domain_id : 1 , url : ' http://... ' } Resque .enqueue( FetchPageWorker .domainify(domain_id), args) Workerの起動スクリプト domain_ids = config.get( :domain_ids ) # ドメインごとのクラス定義を動的生成 queues = domain_ids.map { | domain_id | FetchPageWorker .domainify(domain_id) } # ランダムに並び替えた優先順でワーカーを起動 worker = Resque :: Worker .new(*queues.shuffle) worker.work このようにWorker.domainify(domain_id)をコールすると、そのサイト固有のWorkerクラス定義を生成できるので、コードもシンプルに保つことができます。 さらにWorkerの起動スクリプト中で'queues.shuffle'することで、スクリプト実行の度に毎回優先順位の異なるプロセスが起動され、複数サイトの処理ももっとバランスよく並列に処理することができます。 まとめ サイトごとのキューを用意することでクローラー全体として並列度をキープ&処理効率の最適化ができます。さらに、ResqueのWorkerクラス定義を動的生成することで、実処理が共通なクラス定義を複数用意するという実装コスト・管理コストを下げ、コードの簡潔性も保つことができました。 前回はiQONのサービス規模も交えながらの事例紹介でしたが、今回はRubyやResqueの活用方法にフォーカスした事例紹介をしてみました。 ちなみに、VASILYではエンジニア向けのセミナーを近日中に開催を予定しています。。 VASILYというスタートアップがどういう会社なのか、どんなサービスをつくっているのか、技術的にどんな事に取り組んでいるのかということをお話する予定です。 日程・内容が確定しだいお知らせしますので、興味のある方はぜひご参加ください。
はじめまして。バックエンドエンジニアの 吉田 です。 2013年5月末の入社以降、大量のEC2インスタンスのVPC移行を担当した後、今はiQONの商品DBを支えるクローラーの改善に取り組んでいます。今回はその改善の1つとして開発したRedis::DistMutexという分散ロック機構のruby実装を紹介をしようと思います。 Redis::DistMutex 開発の経緯や細かい設計の話は後述するとして、まずはつくったgemの紹介をします。 Redis::DistMutex Redisベースの分散ロック機構 rubyのライブラリにある Mutex 互換 スレッド間だけでなく、プロセス間・ホスト間でも共有できるMutex 時限つきロックの作成が可能(redisのsetnxとexpireを活用) namespaceを指定できるので、特定の処理ごとにロックの作成が可能 redis2.6以上のみサポート(1秒以下のexpireの指定が可能な pexpire 依存するため) サンプルコード sample.rb require ' redis ' require ' redis-dist-mutex ' Redis :: DistMutex .redis = Redis .new mutex = Redis :: DistMutex .new( :test_app , expire : 1 , auto_release : false ) mutex.synchronize { puts ' started ' ; sleep rand( 5 ) } これを5つ以上の複数スレッドや複数プロセスで同時実行すると、'Kernel.sleep(n)'の長さに影響されることなく全体として一定間隔でsyncrhonizeに渡されたブロック内の処理が実行されます。 複数スレッドから使うサンプル スレッドごとにsynchronize内でランダムにKernel.sleepを実行 syncrhonizeに渡すブロック内でRedisにIDと現在時刻を記録 test_mutex.rb require ' redis ' require ' redis ' require ' redis-dist-mutex ' class TestMutex def initialize @redis = Redis .new Redis :: DistMutex .redis = @redis @mutex = Redis :: DistMutex .new :test_app , expire : 1 , autorelease : false end def create_thread (id) Thread .new do now = -> { sprintf( ' %.1f ' , Time .now.to_f) } @redis .lpush( ' hoge_test:start ' , " #{id}:#{now.call}") @mutex .synchronize { @redis .lpush( ' hoge_test:end ' , " #{id}:#{now.call}"); sleep 2 + rand } end end def start puts ' - ' * 40 @redis .del( ' hoge_test:start ' ) @redis .del( ' hoge_test:end ' ) 5 .times.map { | id | create_thread(id) }.each(& :join ) puts " Start : #{@redis.lrange('hoge_test:start', 0, -1)}" puts " End : #{@redis.lrange('hoge_test:end', 0, -1)}" end end if $0 == __FILE__ test = TestMutex .new 3 .times.each { | i | test.start } end 実行してみると、スレッドのIDは実行順とは無関係に記録されていて、それぞれ約1秒おき記録されています。 $ bundle exec ruby ./test_mutex.rb ---------------------------------------- Start: [ "0:1385368596.6" , "1:1385368596.6" , "2:1385368596.6" , "4:1385368596.6" , "3:1385368596.6" ] End : [ "0:1385368600.6" , "4:1385368599.6" , "1:1385368598.6" , "2:1385368597.6" , "3:1385368596.6" ] ---------------------------------------- Start: [ "0:1385368605.0" , "1:1385368605.0" , "2:1385368605.0" , "4:1385368605.0" , "3:1385368605.0" ] End : [ "4:1385368609.0" , "0:1385368608.0" , "1:1385368607.0" , "2:1385368606.0" , "3:1385368605.0" ] ---------------------------------------- Start: [ "0:1385368613.4" , "1:1385368613.4" , "4:1385368613.4" , "3:1385368613.4" , "2:1385368613.4" ] End : [ "3:1385368617.4" , "0:1385368616.4" , "1:1385368615.4" , "4:1385368614.4" , "2:1385368613.4" ] 実行時に別のターミナルで'redis-cli monitor|grep expire'を実行しておくと、1秒おきにロックが取得されるのがよくわかると思います。 initialize時のオプション 'expire: n'を設定しないとブロックの実行が完了次第、次の処理に移る rubyのMutexと同じ動作 'autorelease'はデフォルトtrueで、'false'を設定しない限り、処理が同期的になる 'true'だとsyncrhonize終了時に必ずロックを開放 今回の要件では、'expire: 1, autorelease: false'を指定 そうすることで、ロック取得から1秒後に必ずロックが開放される サイトクロール時の問題点 iQON掲載商品数 200万件以上 iOONでは大量の商品データを保有しているのですが、それらは提携しているECサイトをクロールしてDBに取り込んでいます。さらに、いったん貯めたアイテムの在庫情報も定期的に再クロールして更新しているので、相当数の商品ページを効率的にクロールする仕組みが必要になります。 クローラー最大のボトルネックはネットワークIO クローラーの処理は、大きく"ダウンロード"と"解析"の2つに分けられます。 ページの解析については、サーバーの性能やコードの見直しで高速化が容易です。一方、ページのダウンロードについては外部サイトの性能に依存するため、効率化が難しくなります。これを効率的に行う設計が必要になります。 既存のクローラーはサイトの性能にかかわらず直列でダウンロードして、一定時間sleepしていました。そのように素直に直列でダウンロードした場合、レスポンスタイムに依存してリクエスト間隔がばらばらになります。1ページ5000msかかると1000ページで5000秒(83分20秒)かかる計算です。逆に、200msで返してくれるサイトの場合は1000ページを200秒(3分20秒)で処理できますが、5req/secの負荷がかかります。 このようなサイトごとのレスポンス性能の差異に関係なく一定間隔でページをリクエストできれば、全体としてクロール効率を向上させ、対象サイトへの負荷も軽減することができます。 今回の新クローラー開発では、そのような問題にも取り組みました。 Resqueによるダウンロード処理の並列化 今回開発したクローラーは、起点となるページから抽出した商品詳細ページのURLをResqueにenqueueし、workerはそのURLのページをダウンロードしてDBに保存し、ページ解析用のworkerに処理を引き継ぎます。この時、worker数を増やせばそれだけ並列度を上げて効率よくダウンロードできるのですが、前述した通り、増やしすぎると対象サイトに負荷をかけることになります。 また、何らかの障害によってworkerがdequeueしていなかった場合、ダウンロードキューが大量に蓄積されます。障害から復旧した後でworkerを起動すると、蓄積されたキューが一気に処理されるため、worker数だけ同時にHTTPリクエストする状態が一定時間継続することになります。これはクロール対象サイト対してDoS攻撃しているようなものです。 そこで、このクロール頻度の制御に有効な手段として、時限付き分散ロック(redis-dist-mutex)を導入しました。 時限付き分散ロック Mutex(=Binary Semaphore) Rubyにも同梱されている"Mutex"は、 wikipedia ではこう説明されています。 クリティカルセクションでアトミック性を確保するための同期機構の一種である。 つまり、複数スレッドやプロセス間の排他制御に使用するロック機構の一種であり、ロックを取得したものだけが処理をすすめられ、取得できなければ開放されるまで待つことになります。 ただし、今回の要件では厳密な排他制御は必要なく、全体として一定間隔での実行を保証できれば十分です。ロック取得から一定以上の時間が経過したらそれ以上の排他制御は必要ありません。 そのような仕組みがあることによって、各worker内のHTTPリクエスト開始のタイミングのみの制御が可能になります。(※スケジューラーに近いイメージですが、スレッドやプロセス同士での協調動作となる点でスケジューラーとは異なります。) このように時限付き分散ロックを用意しておけば、複数のResqueWorkerから特定のサイトHTTPリクエストが集中することを防止できるようになります。例えば1リクエストに3秒かかるサイトに対しては3つのworkerが起動していれば、1秒おきにHTTPリクエストを発生させることができます。仮に1秒間隔でリクエストすることが条件ならば、起動しておくべきworker数は以下の式で算出できます。 worker数 >= 平均レスポンスタイム(秒) + α ちなみに、iQONのクローラーのざっくりした構成は以下の図のようになっています。 実際にはiQONのクローラーは複数サイトをクロールするので、サイトごとにqueueとmutexを用意して、複数のworkerから全サイトに対して効率よく処理されるように設計を工夫しています。各workerはそれら複数のダウンロードキューをランダムな優先順位でlistenすれば、全体のバランスがとれます。このあたりの設計の話については別の機会に紹介したいと思います。 以上、 Redis::DistMutex と、それを使ったクローラー事例の紹介でした。 最後に 今回紹介したものはiQONのクローラーのほんの一部分であり、iQONというサービスを支えるためのサーバーサイド技術は多岐にわたります。クローラーにかかわらずiQONを支えるサーバーサイド技術に興味のある方はぜひ恵比寿にあるオフィスに直接遊びにきてください。VASILYでは現在、一緒に働いてくれるエンジニアを大募集しています。 募集要項 連絡先:info[at]vasily.jp