独断と偏見による、タイプ別エンジニア生き方戦略

この記事は マイネットエンターテイメント Advent Calendar 1日目の記事です。

はやいもので、気がついたら今年も残すところあと1ヶ月。12月の恒例行事といえばアドベントカレンダー!ということで、マイネットのアドベントカレンダーを今年もやることになりました。1発目は hirashi がお送りします。

 

 

この1年を振り返ると、社内外のいろんなエンジニアのひとたちと、面談やらなにやらで1on1で話す機会の多い1年でした。思い返してみると、エンジニアって本当にいろんなタイプの人がいるなあと感じます。

本稿では、そんな機会を重ねるなかで感じてきた、エンジニアのタイプ別分類と、それぞれの生き方戦略というか強みの伸ばし方、みたいな話をまとめてみようと思います。

 

モチベーション・達成感を感じるポイントによる分類

 

エンジニアとして働くこと・ものづくりをすることについて、

どこにモチベーションを感じるか。

どういうときに達成感を感じるか。

 

という観点で、下記の4つに分類してみました。 独断と偏見によるとても主観的な分類ですが、多くのエンジニアがおそらくいずれか(1つ以上)に当てはまるのではないかなと。

  • つくること自体に興味が強く、手段や方法にこだわるタイプ
  • 使ってくれるユーザーさんの喜ぶ姿が見たい、という思いが強いタイプ
  • 技術そのものを追求することに生きがいを感じるタイプ
  • 効率化やリソース最適化に情熱を燃やすタイプ

 

それぞれについて詳しく述べていきます。

 

続きを読む

この10年の技術的あゆみを振り返ってみる

本記事はMynet Advent Calendar 24日目の記事です。

本日クリスマスイブが誕生日のhirashiがお送りします。

マイネットの創業から各種開発に携わってきて、気がついたら10年目です。

会社スタートしたときはまだ27歳ピチピチの茶髪若造だったのが、知らないうちに歳を重ねてしまったものです。

ちょうどいろいろと節目のタイミングでもあるので、せっかくなので本記事では、自分および会社について、この10年の技術的領域のあゆみを、いくつかのトピックで振り返ってみたいと思います。振り返るとだいたいは反省反省の歴史ばかりで胃が痛くなるような話だらけなのですが、誰かにとってなにかしら有益な振り返りもあるかもしません。

OSSフレームワークを採用して、数年経ったら典型的な技術的負債になった話。

サービス立ち上げ時。ちょうどそのとき時流で話題になっていたものだったり、担当エンジニアが個人的に使ってみたいという思いで選定したようなOSSフレームワークを採用して、サービスつくって、幸いそこそこ軌道に乗って、サービス運営を続けて2〜3年もするとたいてい古さが見えてきます。あまり枯れていなかったがゆえに独自カスタマイズを加えていった結果、導入した人にしかコード触れないようになり、そのうちその人が別の担当に異動したりするとアンタッチャブルになっちゃいます。ああ、自分もいっぱいそういうものを引き継がせてしまったなあ。。。

これという銀の弾丸となるような対策は難しくて、負債発生するのは不可避なことで、サービスが長く続くことはむしろ幸運なことなんだと愛でる気持ちで向き合っていくのがよいと思っています。そのうえで、まずは選定時点で、そのような将来にわたっての重みになりそうなものは慎重に検討すること。また、どうしても古いものは適切にリプレイスしていくのがよいですが、ユーザーさんにどんな価値を今後提供していくか、それをどんなスピード感で実現していくか、というあたりの目的を見失わないことが大事と思います。

ほら、エンジニアってどうしても、手段に興味が沸く人が多くて、どうしても目的と手段とを取り違えやすいんですよね。自戒もこめて。

情報発信してるとなにかいいことあるよ、な話。

自分が最近ほとんどなにもブログ等のアウトプットしていないのでオマエが言うなですが。 以前には積極的にやっていた時期があって、振り返るとこういうことしてるとよいことがいっぱいあったなあと思います。

  • 文章に書いてアウトプットすることで、自分自身の知見の整理になる。自分の記憶を高めるにはエピソードを周囲の人に話すとよい、なんて話もありますが、これに直結する行為です。

  • 社内外に友達が増える。普段から何かしらアウトプットしてると、対面で会ったときの話も弾みやすいのです。

  • 思いもよらぬオファーが来たりする。講師としてセミナー登壇しませんかとか、その知見を書籍に執筆しませんかとか、自分には縁の無さそうな話がひょいっと舞い込んできたりするものです。

そんなこんなが自分自身のこれまでの実体験としてあり。今回のアドベントカレンダーも、きっかけとしてよい取り組みですね。いいだしっぺのtakashabeさんありがとうございます。

自社の強みをどこに据えるか、な技術戦略の話。

自分たちの手数が無限大にあるわけではありません。また世の中には数多くの会社さんがあってそれぞれがいろんな価値を提供しています。 そんななかで、自分たちはどう生き残っていくか、というのを考えていくにあたって、全方位に手を出していくのではなく、自分たちが強みとすべきところに資源を集中させて、それ以外の部分は極力深みに立ち入らない、という取捨選択をこれまで繰り返してきました。それはベンチャー経営では当然のことですが、技術戦略についてもですね。

振り返ると、うまくいったもの、いかなかったもの、両方いろいろありました。たとえば、データセンターラックを全て解約して自社サービスをクラウドに完全移行させた話はうまくいったケース。またあるいは、一時期「Android集中」を謳っていてiOSへまったく展開しなかった、という時期もありまして、現在では方針転換して両バリしているので結果的にどうよという案件ではありますがw、一定期間中はうまくリソース集中投下ができていたケースでした。

環境変化や事業転換に対する心構えな話。

インターネット/ゲームにまつわる技術は変化がとにかくはやくて、過去の常識だったことが新しい価値観でガラッと変わってしまうことがままあります。また、上述の「選択と集中」により、会社の方針としてそれまでの方針が一新されることもしばしばあります。

そんな環境のなかで、いかに変化を受け入れて、それでいて第一線での活躍を続けていくか、という命題があります。自分もまだまだ上から偉そうなことを言える立場ではないですが、振り返って感じるのは2点。

1つは、変化を楽しむ気持ちでいること。こだわりを持つことはもちろんよいことですが、その一方で1つの価値観だけに変に固執するような思いがあったりすると、精神衛生的によろしくないことになりがちです。 もう1つは、新しいことをできるだけ最速で身に着ける方法論。普通の人がやってるのと同じような方法では、なかなか第一線には躍り出れないです。詳細はここでは省略しますが、なにかしら頭をひねって作戦を練って取り組んだことが結果的によかったな、と思えることが何度かありました。変化が激しい環境だらこそ、こういう急アクセルを踏む訓練が意義が大きいのだと思います。

マイネットのエンジニアとしてのこの先にあるもの、の話

マイネットはこれまで何度か事業転換をしてきているのですが、現在ではスマホゲーム運営のリビルド事業というものをやっています。あまり見慣れない事業名と思いますが、簡単にいうと、提供中のゲームタイトルを運営移管して、機能や施策の適切な再構築を行い、より長くユーザーさんに楽しんでいただけるようサービス運営をつづけていく、というものです。 エンジニアが具体的にやっていることというのは、挙げてみると広範です。再構築にはアプリケーション設計力がなによりも問われたり、エンドユーザーを魅了するフロントインタフェースが重要だったり、大量トラフィックを捌くサーバインフラや対負荷設計が必要だったり、安定提供にはDevOpsの領域が不可欠だったり、データ分析基盤ももちろん活用必要だし、運営には人間のオペレーションも必要ななかでの効率化安定化が重要だったり。

一昔前はWebサービスと言われたりしていましたが、今やwebなのかアプリなのか垣根がなくなってきていて、またスマホだけでなくウェアラブルとかのデバイスも普及していたり、既存の家電やゲーム機なんかもネットとつながるようになっていたりして、このへんを総称するよい言葉が見当たらないのですが(社長上原は「オンラインサービス」と呼んだりしていますが)。 こういった、「ネットとつながっていて、スマホやPCなどなんらかのデバイス/UIを通じてエンドユーザーに提供されるサービス」というのは、スマホゲームに限らず、今後もっともっと増えていくと思っています。

で、ゲーム運営リビルド事業を通じてわれわれが日々実践していることって、これらのサービスを提供していくうえでも、普遍的に不可欠な技術・知識・経験だと思うのですよね。目先は現在の事業に注力していますが、この事業ではない別のことをやるとしても、エンジニアとして様々な方面から求められる有益なキャリアになると思っています。

おわりに

ということで、この10年を振り返って思ったことを順不同でつらつらと書いてみました。読み返してみると、やっぱりどこかしら上から目線になってしまいがちで、どうもこっぱずかしいですね。。。まだまだ自分も日々勉強中の身ですので、次の10年も精進していきたいと思います。では!

EXPLAINを使おう

これはMynet Advent Calendar18日目の記事です。

今回はWEBシステムのボトルネックになりやすいクエリを最適化する際に使う EXPLAIN の見方について、江澤がお送りします。

EXPLAINとは何か

EXPLAINとは、RDBMSオプティマイザからクエリを実行する際、最適な実行方法を説明する実行計画を出力するコマンド

EXPLAINの実行計画出力の見方(今回はMYSQL)

EXPLAIN実行例

mysql> explain select count(ZipCode) from zipcode;
+----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows   | Extra       |
+----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+
|  1 | SIMPLE      | zipcode | index | NULL          | PRIMARY | 21      | NULL | 128363 | Using index |
+----+-------------+---------+-------+---------------+---------+---------+------+--------+-------------+

id

クエリ内のSELECTの連番。

select_type

項目 内容
SIMPLE UNIONやサブクエリを使用しない単純なSELECT
PRIMARY 外部クエリ(サブクエリを含む場合に、外部クエリと呼ばれる)
UNION UNION内の2つめ以降のSELECT
DEPENDENT UNION UNION内の2つめ以降のSELECT、相関関係
UNION RESULT UNIONの結果
SUBQUERY 相関関係のないサブクエリ
DEPENDENT SUBQUERY 相関関係のあるサブクエリ
DERIVED 派生テーブルSELECT(FROM句内のサブクエリ)
MATERIALIZED 実体化されたサブクエリ
UNCACHEABLE SUBQUERY 結果をキャッシュできず、実行の度に結果が変わる可能性があるサブクエリ
UNCACHEABLE UNION UNCACHEABLE SUBQUERYに属する UNION内の2つめ以降のSELECT

table

アクセスする対象のテーブル。

<unionM,N> と表示される場合はselect_typeが「UNION」で、idが「M」およびidが「N」に由来するテーブルであることを示す。

<subqueryN> と表示される場合はselect_typeが「SUBQUERY」で、idが「N」に由来するテーブルであることを示す。

<derivedN> と表示される場合はselect_typeが「DERIVED」で、idが「N」に由来するテーブルであることを示す。

type(レコードアクセスタイプ)

項目 内容
system 1行しかない。constの特殊なケース
const PRIMARY KEYまたはUNIQUEインデックスのルックアップによるアクセス。非常に高速
eq_ref PRIMARY KEYまたはUNIQUE KEY利用時のアクセス。結合時最速
ref PRIARY KEYまたはUNIQUE KEYが非利用時のアクセス。N行ある場合に使用
fulltext FULLTEXTインデックス利用時アクセス。お目にかかることはあまり無い
ref_or_null refにNULL値を含む行の追加検索時のアクセス。IS NULL使用時など
index_merge インデックスマージ最適化
unique_subquery IN句のサブクエリを効率化のためrefに置き換える
index_subquery unique_subqueryの一意でないversion
range インデックスを用いた範囲検索
index インデックスフルスキャン。やむを得ない状況で無ければ最適化したい
ALL フルテーブルスキャン。インデックスが使用されていない。最適化必須

possible_keys

オプティマイザがテーブルのアクセスに利用可能なインデックスの候補として挙げたキー。 NULLの場合は、関連するインデックスがない。

key

オプティマイザによって選択されたキー。

key_len

選択されたキーの長さ。キー長が短い方が高速。

ref

検索条件で、keyと比較されている値やカラムの種類。 定数の場合はconst。 結合時は結合する相手側のテーブルの検索条件として利用されているカラムを表示。

rows

クエリを実行するために調査するレコード数。 InnoDBテーブルの場合、推定値であり、正確ではない。

Extra

Extraは項目が多すぎるので良く見るものを抜粋。

項目 内容
Using filesort 結合時、インデックスを用いてソートがされていない(改善出来るのであれば出ないようにしたい)
Using index インデックスツリーの情報のみを使用している
Using index for group-by MIN()、MAX()またはGROUP BYまたはDISTINCTクエリを使用している際、インデックスが効率的に使われている
Using temporary 実行時、結果を保持する一時テーブルを作成(改善出来るのであれば出ないようにしたい)
Using where 結合時や重複排除時にテンポラリテーブルを作成する必要がある

あとがき

次回があれば実際にテーブルとデータを用意し、サンプルを用いて解説したい←最近join、unionしないDB設計が多いので忘れてきている。復習の為

勘違いなどがあれば御指摘を賜りたく←日本語的にも技術的にも拙い

重複でなんとなくDISTINCTを使用する前にGROUP BYを使おう←これが言いたかった、30秒もかかるクエリなんて心臓に悪いので実行したくない

AppiumによるUIテスト自動化

本記事はマイネット Advent Calendar15日目の記事です。

今回はネイティブ周りを担当している山木がAppiumによるUIテスト自動化についてお送りします。

Appiumの特徴

  • WebView、フルネイティブ、Unity、cocos2d-x 可能
  • iOSAndroid 両方利用可能(ただしテストコードの共有はできない)
  • 実機、シミュレーターでの実行 可能(Androidエミュレーター Genymotionも可)
  • コマンドからの実行可能
  • スクリーンショット可能(一部言語制限)
  • テストコードは C#RubyObjective-CJava、node.js、Python
  • 操作を記録しコード出力する機能もある
  • ネイティブコードにテストコード、SDKを仕込むこと無くテストが可能
  • デバッグビルドした ipa、apk があれば第三者によるテストコード作成、実施が可能
  • 成果物は吐き出してくれないので自前実装作成が必要
  • スクリーンショットでの画像比較も自前で実装すれば可能
  • 後述にもあるが UIAutomation/UIAutomator を内部で利用している
  • Gitにあがっているクライアントが最新であれば...GUIなどのカスタマイズも可能(現状古い)

公式

イメージ

事前にやったこと

  • Xcode インストール(ver7.1)
  • Xcode Command Line Tool インストール
  • Appium.app クライアントのダウンロード(ver1.4.13)
  • 必要ライブラリなどのインストール
$ brew install node
$ npm install -g appium
$ npm install wd
$ npm install -g mocha
$ npm install chai
$ npm install chai-as-promised
$ npm install colors
$ brew install maven
$ authorize_ios
$ brew install imagemagick <- 画像比較
  • Selenium.framework を "/Library/Frameworks" に配置(後述あり)

Androidの場合に必要

bash_profile(例)

export JAVA_HOME='/usr/libexec/java_home'
export ANDROID_HOME='/Users/[user]/Library/Android/sdk'

起動と終了

起動

$ appium &

終了

$ killall -9 node

ドクター

$ appium-doctor
$ appium-doctor --ios
$ appium-doctor --android

Appium Settings

Android Settings

あくまでも目安

  • App Path 設定
  • Device Name ON "該当device名"

iOS Settings

あくまでも目安

  • App Path 設定
  • Force Device 設定
  • UDID 不要
  • Advanced > Use Native Instruments Library ON

Developer Stettings

あくまでも目安

  • Enabled ON
  • Use External NodeJS Binary ON "/usr/local/bin/node"
  • Use External Appium Package OFF
  • NodeJS Debug Port ON "5858"
  • Break On Application Start OFF

各言語での利用方法

Objective-CiOS / Android

  • テストコード作成
    • Xcode Command Line Tool で 吐き出したコードを組み込み、実行ファイルを作る
    • Selenium.framework は Appium.app の中から抜き取った( Git
    • CommandLineToolの場合frameworkは組み込めないので "/Library/Frameworks"に格納とXcodeにバインド
    • [iOS / Android] テストコードは別々の必要がある(Elements階層が異なるため)
  • テスト実行

    • ターミナルで $ appium & 実行
    • ビルドしたCommandLineToolを 実行
    • [Android] エミュレーターを事前に起動する必要がある
  • 現状できたこと

    • [iOS] Waitかけれた [wd setImplicitWaitTimeout:10000]; //10sec
    • [Android] Waitかけれた sleep(10); //10sec
    • [iOS/Android] Tapイベント送れた
    • [iOS/Android] スクリーンショット取れた [wd screenshot] あとは自前で保存
    • [iOS] UIALogger.logStart()、UIALogger.logPass() など利用できた
[wd executeScript:@"UIALogger.logStart(\"Test Start\")"];
[wd executeScript:@"UIALogger.logPass(\"Test Success\")"];
  • 後できるといいこと
    • 画像検証 tuneupJS同様 imagemagickがあるといいかも?
    • テスト結果をなにかしら出力

node.js(iOS/Android

  • テストコード作成
  • テスト実行
    • node xxx.js
    • mocha xxx.js
  • 現状できたこと
    • Waitかけれた .sleep(10000)
    • Tapイベント送れた
    • スクリーンショット takeScreenshot() or saveScreenshot("フルパス")
    • mocha なら テスト結果をコンソールに出力できる(後述有り)
  • 後できるといいこと
    • tuneupJS との連携...難しそう
  • 補足
    • mocha xxx.js --reporter xunit > xunit.xml JUnit形式のレポート出力ができる

C#, Ruby, Java, Pythonについては未検証

テスト成果物

  • Appium は基本成果物は出力しない
  • iOSの場合は /tmp/appium-instruments に trace結果が出力される
  • node.js は mocha を使用することで コンソールに出力可能
  • Java は Sahagin を使用することでテストレポートの生成が可能(制約あり)

-> 自前での成果物生成するしか無い

mochaを利用した例

スクリーンショット 2015-11-05 18.24.09.png (112.1 kB)

AppiumをObjective-Cで使う場合のアレコレ

  • OSバージョン、デバイス名、ipa/apkパス を外からもらう
  • Appium を Objective-C側から起動、終了させる
  • Androidエミュレーター "Genymotion" を Objective-C側から起動、終了させる
  • Appium の Session をちゃんと閉じる
  • Genymotion、adb、UIAutomatorの機嫌について
    • 概ねの原因
    • 試行錯誤の解決方法
    • エミュレーターの準備ができたかチェックする方法
    • 独自エラーカウントでテスト中断機構
  • ログ出力は printfより NSLogのほうがおすすめ
  • テストレポートの出力

OSバージョン、デバイス名、ipa/apkパス を外からもらう

main の argc/argv から パラメーターを貰う

Xcodeの場合:

スクリーンショット 2015-11-19 11.57.22.png (52.7 kB)

CommandLineToolの場合:

$ appiumTest_Android -app ~/MainActivity-release.apk -os 4.4 -device Nexus5 -AndroidTest

main から受け取った引数をなんやかんやする

SECapabilities *caps = [SECapabilities new];
[caps addCapabilityForKey:@"appium-version" andValue:@"1.0"];
[caps setPlatformName:@"Android"];
[caps setPlatformVersion:[TestUtil getTestOSVersion]];
[caps setDeviceName:[TestUtil getTestDevice]];
[caps setApp:[TestUtil getAppApkPath]];

Appium を Objective-C側から起動、終了させる

Appium.sh を用意して、NSTask で起動する

// Appium起動
NSTask* appium = [[NSTask alloc] init];
NSString* appiumPath = [NSString stringWithFormat:@"~/Appium.sh"];
[appium setLaunchPath:appiumPath];
[appium launch];
sleep(5);
~
// Appium終了
[appium terminate];
while ( appium.running ) //状態を確認してまだならsleep
{
  sleep(1);
}
appium = nil;

Appium.sh

#!/bin/sh
#ユーザ環境のカスタマイズを読み込む
. ~/.bash_profile
#PATH="${PATH}":'/usr/local/bin':'/opt/local/bin'
#export PATH

#export JAVA_HOME='/Library/Java/JavaVirtualMachines/jdk1.8.0_60.jdk/Contents/Home'
#export ANDROID_HOME='/Users/[user]/android-sdks'
#export GENYMOTION_APP_HOME=/Applications/Genymotion.app

echo $PATH

#念のため殺しておく
adb kill-server
killall -9 node

#ログ無し、タイムアウト調整
#appium --log-level warn:error --device-ready-timeout 10000
appium

Androidエミュレーター "Genymotion" を Objective-C側から起動、終了させる

AndroidNexus5.sh を用意して、NSTaskで起動する 終了時は AndroidNexus5Stop.shを別途用意して NSTaskで実行する

// Androidエミュレーター起動
NSTask* task = [[NSTask alloc] init];
NSString* path = [NSString stringWithFormat:@"~/AndroidNexus5.sh"];
[task setLaunchPath:path];
[task launch];
sleep(40);
~
// Androidエミュレーター終了その1
[task terminate];
while ( task.running )  //状態を確認してまだならsleep
{
  sleep(1);
}
task = nil;

// Androidエミュレーター終了その2
NSTask* genymotionKillTask = [[NSTask alloc] init];
NSString* stopPath = [NSString stringWithFormat:@"~/AndroidNexus5Stop.sh"];
[genymotionKillTask setLaunchPath:stopPath];
[genymotionKillTask launch];
while ( genymotionKillTask.running )  //状態を確認してまだならsleep
{
  sleep(1);
}
genymotionKillTask = nil;

AndroidNexus5.sh

#!/bin/sh
/Applications/Genymotion.app/Contents/MacOS/player --vm-name "Nexus5" --no-popup

AndroidNexus5Stop.sh

#!/bin/sh
#パワーオフとADBも止める
/Applications/Genymotion.app/Contents/MacOS/player --vm-name "Nexus5" --no-popup --poweroff --stopadb

#VBoxManageも裏では起動しっぱなし、メモリの消費が高いので止める
/usr/local/bin/VBoxManage controlvm Nexus5 savestate

Appium の Session をちゃんと閉じる

  @autoreleasepool // autoleasepoolでスコープ・メモリ制御
  {
    SECapabilities *caps = [SECapabilities new];
    [caps addCapabilityForKey:@"appium-version" andValue:@"1.0"];
    [caps setPlatformName:@"Android"];
    [caps setPlatformVersion:@"xx"];
    [caps setDeviceName:@"xx"];
    [caps setApp:@"xx"];
    [caps addCapabilityForKey:@"deviceReadyTimeout" andValue:@"300"];
    [caps addCapabilityForKey:@"androidDeviceReadyTimeout" andValue:@"300"];


    [wd closeApp];
    [wd quit]; // quit を呼んであげましょう
    wd = nil;   // nil代入はおまけ
  }

Genymotion、adb、UIAutomatorの機嫌について

※ 実行中に強制終了をすると UIAutomator が機嫌を損ねる可能性があります。   次回起動時に UIAutomator関係でエラーになりやすくなります。

[31merror[39m: UiAutomator quit before it successfully launched
[36minfo[39m: [debug] Cleaning up appium session
[31merror[39m: Failed to start an Appium session, err was: Error: UiAutomator quit before it successfully launched
[36minfo[39m: [debug] Error: UiAutomator quit before it successfully launched
    at [object Object].<anonymous> (/usr/local/lib/node_modules/appium/lib/devices/android/android.js:205:23)
    at [object Object].<anonymous> (/usr/local/lib/node_modules/appium/lib/devices/android/android-hybrid.js:249:5)
機嫌が悪くなる概ねの原因
  • Genymotion のエミュレーター複数台、裏で生きていた
  • adbに複数認識されているとUIAutomator error になる
  • エミュレーター起動前にAppium側からAPKインストールなど処理が動いていた
  • VBoxManagerによるメモリ圧迫(4台4プロセスx800MB消費)
試行錯誤の解決方法
  • Genymotionエミュレーターの終了処理を実装する
  • デバイス切替、起動時に adb の再起動&複数台生きている状態を無くす
  • VBoxManagerの都度終了処理を実装
  • NSTask終了時のrunningチェック でフロー確立

上記、解決方法で かなり改善した...が稀にまだ機嫌が悪い時がある PCスペック・メモリは高いほうがいい... エミュレーターの起動が遅くなるとSkip or wait, sleep せざるを得ない

エミュレーターの準備ができたかチェックする方法

  SECapabilities *caps = [SECapabilities new];
  ~省略
  SERemoteWebDriver *wd = [[SERemoteWebDriver alloc] initWithServerAddress~];

  sleep(30);

  NSLog(@">> currentActivity : %@", [wd currentActivity]);
   NSLog(@">> isAppInstalled : %@", [wd isAppInstalled:@"jp.co.mynet.eof"] ? @"YES" : @"NO");

  // この時点でエミュレーターが起動し、アプリインストール完了していないとNG
  while (  ![wd isAppInstalled:@"jp.co.mynet.eof"]  )
  {
    sleep(1); //起動準備ができていない while or Skipさせる
  }

独自エラーカウントでテスト中断機構

エミュレーターの起動エラーなどが現状では起こりうる あまりにも長時間のテストの場合、時間が勿体無いので途中でテストを停止させる 例えばエラー5回で テストを中断し、正しい終了を行い、Assertを投げるなどの機構を作る

ログ出力は printfより NSLogのほうがおすすめ

Jenkins で実行、出力を行う場合 printf は、スタック後回しにされまとめて出力される NSLog は、即出力され時系列を維持できる

テストレポートの出力

自前で頑張る!! ほかいい方法があれば随時募集!!

  • Junitのフォーマットでxmlを吐き出し、Jenkinsに読み込ませる
  • ImageMagickの比較は htmlを生成し生成物から参照させる

参考

コミットメッセージの話

本記事はマイネット Advent Calender 11日目の記事です。 本日はito-kenがお送りします。

はじめに

ゼロからずっとプロダクトに関わっている人でない場合、

メッセージもコーディング規約と同じノリで

「既に決まっているフォーマットに乗っかって書く」事が多いと思います。

フォーマットが決まっておらずカオスになっていたり、

ツールとの依存関係上やPJ発足からの慣例、もしくはリーダーの単純な趣味嗜好で

「必ず○行で書け」という状態もありえます。

まだ良い方

  • 「○○(人名)本日作業分」
  • 「○○終わりませんでした。明日相談させてください」

つらい

  • 「update」
  • 「次の人後よろ」
  • 「メモ」

こんな殺意湧き上がるメッセージの犠牲者も多いのではないでしょうか。 このような犠牲者を生み出さない為に、我々に出来る事は何でしょうか。

規約や約束事が決まる(or 見直す)タイミング

開発が運用と並行してしまうと、何を変えるにもそれなりの労力が要ります。 最初が肝心ですが、ツールを変える時などはチャンスでしょう。

多いタイプ

  • 1行目   要約(必要な場合チケット番号+α)
  • 2行目   空欄
  • 3行目以降 コミットメッセージ本文

gitの標準ですね。 これを基準にメッセージを書いている方が多いかと思われます。

メッセージ規約ドキュメント作ったよ!読む?

皆さん一度だけ読んでくれます。その後、誰も読まないと思います。 「誰でも一発で把握できる」位の分量にするのが良いかと。

  • チケット番号の有無
  • 簡単なフォーマット
  • 対象機能や対象ファイルの明記の有無
  • 見ただけでだいたい把握できる例文がいくつかあると尚良し
  • 外国人メンバーが居る場合は英語での例文もあると幸せ

といったところでしょうか。

おわりに

炎上中や、開発者が少数の場合はメッセージが荒れがちですが

チームで余計な手戻りや連携ミスなどを回避する為にも

自分自身も気をつけつつ、整備されているかは気をつけたい所ですね。

fluentd + Chatworkでカジュアルにログ監視する

本記事はマイネット Advent Calendar4日目の記事です。

1日目に引き続き@takashabeがお送りします。

今回はfluentdを利用して開発環境やステージング環境のエラーログをChatworkに流す手法についてご紹介します。

背景

弊社では他社で開発した歴史あるソシャゲを引き取って運用するプロジェクトが多数あります。単純にログを見る用途だとSentryなどの専用ライブラリを使ったほうが高機能ですが、その場合アプリケーションの改修が発生するので導入コストが高い可能性があります。

一方fluentdでログを送るだけだと、(もし入っていなければ)fluentdをサーバに入れるだけなので導入と運用コストが低いというメリットがあります。

やること

Chatwork側のAPI準備

ChatworkのAPIは現状プレビューとなっており、以下から申請する必要があります。 http://developer.chatwork.com/ja/

申請後、数時間〜するとメールが届いてAPIが解放されます。その後以下に従ってtokenを発行して控えておきましょう。 http://developer.chatwork.com/ja/authenticate.html

fluentdインストール

fluentd自体をgemで入れてもいいですが、ここでは何かと便利なtd-agentを入れておきます。

$ curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh

# pluginの導入
$ td-agent-gem install fluent-plugin-out_chatwork fluent-plugin-rewrite-tag-filter

fluentd設定

td-agent.confに以下の様な感じで設定します。 ここではvirtualhostで2環境(dev-01,dev-02)作っているapacheのエラーログを流すことを想定しています。

## エラーログをtailして監視する
## formatで指定したマッチした文字列を後処理で利用する(<message>など)
<source>
  type tail
  format /^(?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])? \[client (?<client>[^\]]*)\] (?<message>.*)$/
  pos_file /tmp/dev-01_error_log.pos
  path /var/log/httpd/dev-01-error.log
  tag dev-01.apache.error
</source>
<source>
  type tail
  format /^(?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])? \[client (?<client>[^\]]*)\] (?<message>.*)$/
  pos_file /tmp/dev-02_error_log.pos
  path /var/log/httpd/dev-02-error.log
  tag dev-02.apache.error
</source>

## 例えば定常的に出てしまうログなど、特定のログを排除する。合ってもなくても良い
<match *.apache.error>
  type rewrite_tag_filter
  rewriterule1 message (<排除したい文字列>)  ${tag}.clear
  rewriterule2 message (.+)               ${tag}.accept
</match>

## 上記のrewrite_tag_filterでacceptとなったログのみ処理する
## 数が増えてきたらforestでまとめるのも良いでしょう
<match dev-01.apache.error.accept>
  type         chatwork
  api_token    <API_TOKEN>
  room_id      <ROOM_ID>
  message      "[info][title](cracker) [dev-01] [<%= Time.at(time).strftime("%Y/%m/%d %H:%M:%S") %>] [<%= record['level'] %>] (cracker)[/title]<%= record['message'] %>[/info]"
</match>
<match dev-02.apache.error.accept>
  type         chatwork
  api_token    <API_TOKEN>
  room_id      <ROOM_ID>
  message      "[info][title](F) [dev-02] [<%= Time.at(time).strftime("%Y/%m/%d %H:%M:%S") %>] [<%= record['level'] %>] (F)[/title]<%= record['message'] %>[/info]"
</match>

ここまで設定して上手くログを流すことが出来れば以下のようにChatwork上にログが流れるでしょう。

ログ画像

まとめ

fluentdを利用してChatworkにログを流す方法について紹介しました。

ソシャゲ運用ではプランナーがマスタを入れて確認というフローが多いため、チャット上にログが流れることによってプランナーが単独でエラーを解消する、もしくはエンジニアに助けを求めるということがカジュアルに出来るようになり便利かと思います。

やってることは単純ですが他PJでも採用事例が増えており、こういったもので少しでも楽をしていきたいですね。

ISUCONに参戦してきた話

本記事はマイネット Advent Calendar 1日目の記事です。

最近寒くなってきてサンダルが若干辛くなってきた@takashabeです。 間が空いてしまいましたがISUCON5に弊社エンジニア勢で参戦してきたので、その内容を紹介しようと思います。

優勝賞金100万円!今年もやります ISUCON5 開催と日程のお知らせ #isucon : ISUCON公式Blog

予選

社内でメンバーを募って最終的に9名3チームで予選に出場しました。そのうち1チームが予選突破し、本選出場出来ました。 今回メンバー募集する際には「チームメンバー募集!」というよりは「ISUCONO出たい人一緒に勉強しましょう!」みたいなノリで集めたため、チーム分けどうしようか悩みましたが、ガチで勝ちに行きたいということで各々1人で過去問を解いてスコア順にチーム編成しました。

予選前に何をやったか、予選で何をしたか詳しくは各メンバーのブログをご参照ください

本選

本選は僕も以下ブログに書いた通り、ただただ実力不足だったなという印象でした。 一応Failせずにスコアは出ましたが実装しきれずに終わったことや、盲点だったことが多かったという感じです。

反省会

本選終了後、ISUCON未参加の方も交えて社内でISUCON大反省会と称したISUCONステマを行いました。 反省会に参加してくれた方から「来年は出たい」といった声が多く聞かれ、こういうコンテストに参加する文化が社内に少しでも広まれば良いなぁという気持ちです。そして反省会中で社内ISUCONやるぞという話をぶち上げたので、ただいま死んだ魚の眼をしながら問題を準備しております。

発表資料

おわりに

普段パフォーマンスは重要と思いつつ、実際にパフォーマンス問題に対処したことがない方は多いのではないでしょうか。パフォーマンスチューニングというと大抵社内のデキる人が対応して終わり、みたいなパターンも多いかと思います。

フラットな場でガチなエンジニアと同じ課題に取り組み、他者が何をやったのか、自分が何をやれて何が出来なかったのかが分かる環境がISUCONにはあります。

今回ISUCONに参加したメンバーも口々に学びがあったと発言していて、僕自身も非常に学びあったし、特に本選後の懇親会がめちゃくちゃ面白かった記憶があります。次のISUCONも開催されるかも的な話も出ているので、出たことのない方は是非出てみると良いかと思います。

そして最後に@tagomorisさん、@kamipoさん、@941さんはじめ出題運営に携わった方々、準備が非常に大変だったかと思いますが最高に楽しかったですありがとうございました!!!