Quantcast
Channel: コネヒト開発者ブログ
Viewing all 377 articles
Browse latest View live

CakePHP2用のMaster/Replica接続管理プラグインをOSS化しました

$
0
0

こんにちは、サーバーサイドやっております 金城 (@o0h_)です。
「腸よ鼻よ」を応援しています。人間ってたくましい。

腸よ鼻よ(1)

腸よ鼻よ(1)

webでも読めます https://ganma.jp/chohana

さて、掲題のとおりですが、以前にママリのマスプロモーションを実施した際の負荷対策として作成した機構をプラグインとして公開しました。

packagist.org

MySQL用のデータソースで、1つのモデル*1に対して複数のデータベース接続を提供する機構です。 例えば「参照しか走らないリクエストは参照用DBに接続して、更新系はマスターDBに接続する」といったような使い方を想定しています。

簡単に利用方法を紹介します。

  1. Pluginを配置する
  2. 「複数の接続先」をconfig(database.php)に書き込む
  3. (Controller等で)必要に応じて接続先情報を変更するメソッドを実行する

Pluginの配置

Composerによるinstallに対応しています。
これを、pluginsもしくは app/Pluginに配置してください。 詳細はCookBookを参考にしてください

https://book.cakephp.org/2.0/ja/plugins/how-to-install-plugins.html#composerまた、Pluginを配置した後、プラグインをloadするのをお忘れなく。

接続情報の設定

DATABASE_CONFIGの設定を変更します。 例として、書き込み対応の「master」参照専用の「secondary」という、2つのDB接続を利用するケースを想定します。

  • masterは host:master-db-host; port: 3306; login: root; pass: password
  • secondaryは host:secondary-db-host; port: 3306; login: root; pass: password
  • DB名はどちらも my_app
  • デフォルトでは secondaryに接続を向ける

その際には、以下のような記述になります。

<?phpclass DATABASE_CONFIG {public$default=array('datasource'=>'MasterReplica.Database/MasterReplicaMysql',
        'connection_role'=>'secondary',
        'connections'=>array('_common_'=>array('login'=>'root',
                'pass'=>'password',
                'database'=>'my_app',
            ),
            'master'=>array('host'=>'master-db-host',
            ),
            'secondary'=>array('host'=>'secondary-db-host',
            ),
        ),
    );
}
  • まず、datasourceを MasterReplica.Database/MasterReplicaMysqlに変更します。
    app/Plugin/MasterReplicaにプラグインを配置している想定です
  • connection_roleに、「デフォルトでどこに接続させるか」という名前を指定します
  • connectionsという配列で、「接続名 = key」として、それぞれの接続情報を設定します
    • _common_は特別なkeyで、全接続先に対する共通設定値をもたせます。今回は login, pass, databaseが共通です
    • master, secondaryに対して「個別に設定したい情報」をもたせます。この内容が、 _common_と結合されて利用されます
    • なお、接続名(master, secondary)には任意の名前を利用することが可能です

これで接続情報のセットアップができました。

実際に接続先情報を変更する

Datasource経由で、「接続先変更」メソッドを実行することで、接続先の変更ができます。 switchConnectionRole($接続先名)を利用してください。

例えば、以下のコードは実際にプラグインのテストコードとして利用しているスニペットをベースにしたものです。 cf: MasterReplicaMysqlIntegrationTest.php#L78-L95

<?php$conn=$this->Post->getDataSource();

// masterに向けて書き込みを行う$conn->switchConnectionRole('master');
$newData=['user_id'=>10,
    'title'=>'Lorem ipsum dolor sit amet',
    'content'=>'Lorem ipsum dolor sit amet',
];
$this->Post->save($newData);

// secondaryに向ける$conn->switchConnectionRole('replica');

$savedData=$this->Post->read();

実用例①

「ログインしているユーザーはmasterに向ける(データの更新が可能)」という場合は、AppController::beforeFilter()で操作するというのも手です。

<?php// ログイン処理後$role= AuthComponent::user()?'master':'replica';
MasterReplicaMysql::setDefaultConnectionRole($role);

実用例②

IntegrationTest実行時に副作用として「接続先が切り替わったまま」になるので注意してください。
具体的に言うと、「参照DBに向けたままだとtableの作成やtruncateに失敗する」ので、setUp()/tearDown()ができません。

ControllerTestCaseを継承したサブクラスを用意し、 testAction()メソッドを上書きすることで対応が可能です。

<?phpprotectedfunction _testAction($url, $options=[]){$sourceList= ConnectionManager::sourceList();
        foreach($sourceListas$source){$obj= ConnectionManager::getDataSource($source);
            if(get_class($obj)!=='MasterReplicaMysql'){continue;
            }if(in_array($obj, $this->masterReplicaSources, true)){continue;
            }$this->masterReplicaSources[]=$obj;
            // DB再接続時にテーブル情報のキャッシュが副作用をもたらすケースがあるので// キャッシュ利用フラグを折っておく$obj->cacheSources =false;
        }try{$result=parent::_testAction($url, $options);
        } finally {// _testAction内での副作用を消すため、// 初期接続先をmasterにした上でDB接続を初期化するif($this->masterReplicaSources){
                MasterReplicaMysql::setDefaultConnectionRole('master');
                foreach($this->masterReplicaSources as$obj){$obj->switchConnectionRole('master');
                }}}return$result;
}

CakePHP2のプラグイン作成にあたって

FriendsOfCake/travis が便利で、活用させていただきました。

github.com一部、(Cake3がメインストリームということで・・)工夫する必要がある箇所もありますが、それでも.travis.ymlをかなり簡潔に保てて利便性が高いと思います。

詳しい内容は 実際のYAMLファイルを御覧ください。

おわり!

かなり足早にではありますが、簡単なご紹介をさせていただきました。
PRやIssueをお待ちしております!!!!💪💪💪 簡単な質問等であれば、お気軽にTwitterでリプ飛ばしてください。

コネヒトではCakePHP3も同様の機構を利用しており、こちらも現在OSS化作業を進めているところです。
近日中に披露できれば、と思います。

*1:databaseのconnection定義


iOS版ママリの開発環境をXcode10.2/Swift5にアップデートしました

$
0
0

はじめまして、2019年5月に入社したiOSアプリエンジニアのあぼ(@suxisuxido)です。入社後は『既存チーム』と呼ばれる、ママリアプリの既存機能の改良などを行うチームで、iOS側の開発を担当しています。

コネヒトでは先日、iOSの開発環境をXcode10.1/Swift4.2から、Xcode10.2/Swift5にアップデートしました。今回はそのことについて書こうと思います。

ライブラリのAPI変更への対応

各種ライブラリのバージョンアップに伴い、コードの変更が必要です。ワーニング解消も含めると、RxSwiftまわりの変更が一番多かったです。

RxSwift.VariableをRxRelay.BehaviorRelayに変更

便利なVariableはRxSwift4.0.0-rc.0からdeprecatedになっています。今回のタイミングで、同様の使い方ができるRxRelayのBehaviorRelayに変更しました。

-import RxSwift
+import RxRelay

-letisConnecting= Variable(false)
+letisConnecting= BehaviorRelay(value:false)

- isConnecting.value =true+ isConnecting.accept(true)

https://github.com/ReactiveX/RxSwift/releases/tag/4.0.0-rc.0

throttleやdelayオペレーターの引数をTimeInterval からDispatchTimeIntervalに変更

こちらはRxSwift5からdeprecatedになっています。もともとDouble型で計算した結果を使っていた場合はロジックの変更が必要ですね。

- throttle(1.2)
+ throttle(.milliseconds(1200))

- throttle(3)
+ throttle(.seconds(3))

https://github.com/ReactiveX/RxSwift/releases/tag/5.0.0

Swift5の新機能Raw stringsの利用

Swift5の機能のひとつ、Raw stringsを使って、正規表現から"\"のエスケープを削除しました。

- NSRegularExpression(pattern:"^(\\d+)歳")
+ NSRegularExpression(pattern: #"^(\d+)歳"#)

正規表現はどうしてもバックスラッシュが多くなりがちなので、積極的にRaw stringsを使ってスマートに書いていきたいですね!

https://github.com/apple/swift-evolution/blob/master/proposals/0200-raw-string-escaping.md

Xcode10.2ではiOS12.2のデバッグが可能に

じつはこのとき、いくつかの検証端末が意図せずiOS12.2に上がってしまっていたため、Xcode10.1のままではデバッグができず不便でした...。

Xcode10.2にあげたことによってiOS12.2のデバッグが可能になり、弊iOSチームは救われました(´・∀・`)

https://developer.apple.com/documentation/xcode_release_notes/xcode_10_2_release_notes

モンキーテストの実施

一通り対応が終わったら、既存チームでモンキーテストを行いました。

Xcode10.2/Swift5対応がアプリへもたらす影響範囲は大きく、かつ私自身はまだママリアプリの機能について知識が少なくデグレにも気づきづらいので、知識ある人に触ってもらって怪しいところを事前に拾うのが狙いです。

結果的にこのモンキーテストで1つデグレが見つかったので、やっておいてよかったな〜と思っています。

f:id:aboy_perry:20190722160533p:plain
モンキーテストご協力のお願いのようす

おわりに

影響範囲が大きく、気を張って行う必要があるという意味ではそこそこ大変でした。このほかにも、単体テストで保証しやすいのに実際にはテストが書かれていなかった箇所に、単体テストを追加したりしました。

こういう開発環境アップデートは遅くなればなるほどあとからやるのが大変になったりしますし、そのあいだ新機能が使えないままなので、タイミングをみて今回実施できてほんとによかったです!

社内ツールをCakePHP4でつくりました

$
0
0

f:id:itosho525:20170430222524j:plain

こんにちは。CTOの@itoshoです。

夏の甲子園が始まっていますね。ランチの時間はSPORTS BULLさんのアプリでバーチャル高校野球を観ています。 というわけで、フレッシュな高校球児に負けないようにフレッシュなCakePHP4で社内ツールをつくった話をしたいと思います。

なぜ、CakePHP4でつくったの?

現在、コネヒトのバックエンドのシステムはCakePHP3系が主流になっています。サービスが拡大していく中で今後他の言語やフレームワークを導入していく可能性はありますが、社内にPHP及びCakePHPのナレッジが蓄積されているので、当面CakePHPがコネヒトの技術スタックの主軸の一つであることは変わらないと考えています。そうすると、CakePHP3からCakePHP4へアップグレードしていくのは当然の流れですので、早めにCakePHP4を触っておきたいと思いました。

しかし、CakePHP4はまだstable版がリリースされておらず、4.0.0 beta1が最新のバージョンになります。*1ですので、もちろんプロダクション環境への導入はリスクがあります。しかし、社内ツールであればある程度そのリスクを許容することが出来ますし、むしろ、バグを踏めばCakePHPにコントリビュート出来るチャンスがあると考え、今回CakePHP4を採用することにしました。

どういうツールをつくったの?

この社内ツールについては、また別の機会に詳しく紹介したいと思いますがつくったものは以下のようなシンプルなwebアプリケーションです。

  • 画面数: 12
  • テーブル数: 5
  • 機能のほとんどがシンプルなCRUD処理

隙間時間で開発していたので完成するまで1ヶ月弱くらいかかりましたが、ガッツリ時間を取ることが出来れば2〜3日で完成出来るボリュームかなと思います。

CakePHP4どう?

上述の通り、ほとんどの機能がbakeの自動生成してくれるコードに毛が生えたレベルで、ガッツリ使い倒したわけではありません。ですので、今後また別の印象を持つかもしれませんが、一つのwebアプリケーションをつくった印象としては「CakePHP3に慣れていれば、すんなり入っていける」と感じました。

そして、CakePHP4.0のロードマップを読むと、冒頭に以下のような記述があります。

CakePHP 4.0 will be a breaking change from 3.x. Unlike 3.0, 4.0 will primarily be a clean-up release. Instead of introducing large backwards incompatible changes, 4.0.0 will focus on removing all the deprecated features we've accumulated throughout the life of 3.x. Any method/feature emitting run-time warnings in 3.6 will be removed from 4.0. 4.0 will not add significant new features as the bulk of the release will be spent on cleanup efforts.

要約すると、4.0は2系から3系の時のような破壊的な変更をせず、非推奨機能の削除を中心に行い、後方互換性のない新機能は次の4.1で行うという方針になっています。つまり、4.0は4系にソフトランディングするためのリリースになっているので「すんなり入っていける」という感想を持つことが出来たというわけです。*2

その上で「おっ!」と思ったところを簡単に紹介します。

  • テンプレートファイルがctpファイルからphpファイルになった
    • おそらく一番最初に気付くのがこの変更ではないでしょうか。拡張子が変わっただけかもしれませんが、PHPStorm使いとしては特別な設定をせずに、PHPファイルとして扱えるようになりました。
  • デフォルトレイアウトにMilligramが採用された
    • Milligramは何度か使ったことがありますが、軽いし、デザイン性も高いし、カスタマイズもしやすいので個人的には嬉しい変更でした。
  • リソースのルーティングのルールが一部変更になった
    • 複数の単語からなる、例えば TodoItemsControllerを作成した場合、これまでは todo_itemsというルーティングでしたが todo-itemsというルーティングになりました。なお、これまで通りアンダースコア形式にしたい場合はオプションで 'inflect' => 'underscore'を指定します。
  • (bakeでコードの自動生成をすると)戻り値の型を宣言するようになった
    • 個人的にはこれが一番嬉しいなと思いました!別に元から書こうと思えば書けたのですが、これまでCakePHP本体が戻り値の型宣言をしていなかったので、それに追従する形でコネヒトではアプリケーションコード側も書いていなかったのですが、CakePHP4からは気にすることなく戻り値の型も宣言出来ることになりそうです。
  • (bakeでコードの自動生成をすると)strictモードが指定されるようになった
    • これもよりタイプセーフなコードを実現するための一環ですね。strictモードの挙動については以前Qiitaにまとめた記事があるので、もし興味があれば併せてご覧ください。
  • newEmptyEntity()メソッドの登場
    • これまで新しくEntityを作成する時は newEntity()メソッドを利用していたのですが、bakeで自動生成されるコードをみると今後は newEmptyEntity()メソッドを利用するほうがベターのようです。文字通り空のEntityを作成するメソッドなのですが newEmptyEntity()メソッドとの違いはEntity生成時にバリデーションを行うか行わないかの違いがあります(newEmptyEntity()メソッドはバリデーションを行わない)。

いますぐCakePHP4を使うには?

ここまで読んで、今すぐ私もCakePHP4を使ってみたい!と思ってくれた方は、以下のコマンドでプロジェクトの作成が可能です。ちなみに、CakePHP4からPHP7.2以上が必要条件になっていますのでご注意ください。

$ composer create-project --prefer-dist cakephp/app:4.x-dev app

冒頭に述べましたが、CakePHP3に慣れていれば、すんなり入っていけますし、上記に挙げたようなよりよい改善も含まれているのでstable版が出たら、プロダクションコードもなるべく早めにアップグレードしていきたい!という気持ちを新たにしました。また、これはCakePHP4に限った話ではないですが、やはりちょっとしたプロトタイプ的なwebアプリケーションをつくりたい時に非常に低コストで出来る点は改めて最高だなと思いました。*3

というわけで、今後もCakePHPと共に「人の生活になくてはならないもの」をつくっていくぞ〜!一緒につくってくれる仲間も絶賛大募集中です!

参考サイト

*1:2019年8月13日現在

*2:言うは易く行うは難しだと思うので、これを実現してくれているCakePHPコアチームの皆さんは本当に凄いと思います。

*3:実は当初Go×ReactでSPAっぽくつくろうと思ったのですが、つくりたいものに対して若干オーバーエンジニアリング感があったので、辞めたという経緯があります。

機械学習と人が協力してママリのコミュニティを支えているよ、という話をしました

$
0
0

f:id:taxa_program:20190809124049p:plainこんにちは!MLエンジニアの野澤(@takapy0210)です。

今更ですが東京喰種トーキョーグールというアニメを最近見始めました。
内容はもちろん面白いのですが、OPの歌い出し「教えて 教えてよ 〜」部分の声質がとても印象的で、どうにか真似できないかと練習しているところです。

さて、今回はこちらの勉強会でもLTさせていただいた「機械学習と人が協力してコミュニティを支えているよ」という話をしようと思います。

目次

LT資料

下記が資料です。

speakerdeck.com

以降、スライド内容を抜粋しながら進めていきます。

ママリ内での課題

ママリとは、全国のママがお互いに悩みを相談し合うことのできるQ&Aサービスです。
ユーザの熱量がとても高く、運営としてもコミュニティの雰囲気をとても大切にしています。

真摯な悩みを投稿するユーザが多くいる一方で、下記のように不適切な投稿をする人がいるのも、また事実です。

f:id:taxa_program:20190809124215p:plain
不適切な投稿例

このような中でコミュニティを健全な状態に保つためには、不適切な投稿は運営側で検知し、然るべき対応を行う必要があります。しかし、1日の投稿数が数千件を超える中で、全てを人間が目視チェックするのは現実的ではありません。

そこでママリでは、不適切な投稿を検閲するために機械学習を用いています。

f:id:taxa_program:20190809124754p:plain
ママリでの機械学習活用事例

機械学習と人が協力しているってどういうこと?

現在の検閲フローでは、機械学習モデル(以降、モデル)が「不適切な確率50%以上」と推論した投稿に関して、人間が目視チェックするようになっています。 f:id:taxa_program:20190809125736p:plain

なぜ、モデルの推論結果のみで処理せず、最終的に人間がチェックする必要があるのでしょうか。

今回の例に出したような「簡単に稼げる方法教えますよ」等の質問であれば、わざわざ人間がチェックする必要もないと思うのですが、例えば下記のような質問はどうでしょう。

f:id:taxa_program:20190809130101p:plain

この質問では「簡単に稼げる方法教えますよ」という意味の一文が入っているものの、実際は不適切な投稿ではなく、ユーザが抱えている悩みであることが見て取れると思います。

このような投稿をモデルで推論したときに、「誰でも簡単に稼げます!安心安全!今なら初期費用ゼロです!⭐未経験者歓迎⭐」という部分の影響で、不適切な投稿として判断されてしまう可能性があります。

f:id:taxa_program:20190809150943p:plain

そのため、冒頭でも述べた通り「不適切な確率50%以上と推論された投稿については、人間の目視チェックを通して対応する」というフローになっているわけです。

こうすることで、50%未満と推論された投稿については、人間の目視チェックが不要となり(=コスト削減)、機械学習が苦手とする微妙なニュアンスをもった投稿の対応に人間が注力できるようになります。

そして上記のように、機械学習と人それぞれの良いところを生かし合うことで、コストを削減しつつ、コミュニティを健全な状態に保つことが可能になっている、というわけです。

モデルについて

現在運用しているモデルについても少し触れたいと思います。

ママリ内には多くのテキストデータが存在しており、そのデータをコーパスとしてgensimを用いて単語の分散表現を計算しています。

f:id:taxa_program:20190809161150p:plain
単語の分散表現をgensim.word2vecを使用して計算

下記に、gensim.word2vecを用いて言語モデルを作成するコード例を載せておきます。
word2vecのオプションについてはこちらに分かりやすく掲載されているので、気になる方は見てみてください。

from gensim.models import word2vec
import multiprocessing

cpu_count = multiprocessing.cpu_count()  # CPUのコア数を取得する
corpus = word2vec.LineSentence('corpus.txt') # コーパスの読み込み# 学習
model = word2vec.Word2Vec(corpus, 
                          size=200, 
                          window=5, 
                          hs=1, 
                          min_count=5, 
                          sg=1, 
                          iter=5,
                          workers=cpu_count
                         )

# モデルの保存
model.save('w2v.model')

この言語モデルは、例えば、単語の類似度を測ることが可能だったりします。
試しに、ママリのコーパスで学習させたモデルに対して、「夫」の類似単語TOP10を出力してみます。

# 夫 と類似している単語
model.wv.most_similar(positive=['夫'])

>>>
 ('主人', 0.9211037158966064),
 ('旦那', 0.8511199951171875),
 ('旦那さん', 0.7135452032089233),
 ('ダンナ', 0.6880300641059875),
 ('私', 0.6779747009277344),
 ('妻', 0.642198920249939),
 ('実父', 0.6189703941345215),
 ('わたし', 0.6182470321655273),
 ('父', 0.6118142604827881),
 ('お互い', 0.5914695262908936)

「お互い」という単語が出てくるあたり、コミュニティサービスから作ったモデルならではに感じますね。

そして、上記で得られた単語の分散表現をネットワークの入力として、双方向のLSTMを用いてモデルを構築しています。

f:id:taxa_program:20190809161511p:plain
ニューラルネットワークの概要

下記は実際にモデル構築に使ったコードの一部です。
ネットワーク自体はシンプルな構成になっており、出力層の活性化関数にsigmoidを用いることで確率を出力しています。
また、emb_matrixに上記で学習させた単語の分散表現を渡すことで、ネットワークのEmbedding層に初期設定しています。

# モデルの一部defbuild_model(emb_matrix: np.ndarray, input_length: int):
    model = Sequential()
    model.add(Embedding(
        input_dim=emb_matrix.shape[0],
        output_dim=emb_matrix.shape[1],
        input_length=input_length,
        weights=[emb_matrix],
        trainable=False))
    model.add(Bidirectional(LSTM(64, recurrent_dropout=0.15)))
    model.add(Dropout(0.25))
    model.add(Dense(64))
    model.add(Dropout(0.3))
    model.add(Dense(1, activation='sigmoid'))
    return model

今後取り組んでいくこと

先にも述べた通り、現在ママリでは1日に数千件の質問が投稿され、学習に使えるデータは日に日に増加しています。
このような場合、最新のデータでモデルを定期的に更新することは、モデルの精度を維持・向上させるためにとても重要だと考えています。

しかし現在は、このモデル更新を全て手動で実施しているため、「機械学習基盤イケてるでしょ?」とは言い辛い状態です。(とはいえ、全てAWSのサービス内で完結しているため、手順フローを踏襲すれば誰でも/いつでも更新できる状態ではあります)

今後はこの手順を自動化すべく、AWSの各種サービスを組み合わせながら、自動更新フローを構築していく予定です。

最後に

今回はママリでの機械学習活用事例についてお話しました。

機械学習は、解決したい課題によってはとてもインパクトのあるものになり得ますが、課題の種類によっては、機械学習をフル活用するよりも、人間と機械学習お互いの長所を活かし合うことが大切だと考えています。

今後も、機械学習を使うことを目的とするのではなく、あくまでも課題解決の手段として活かせる部分には存分に活かしサービスグロースに貢献していければと思っています!

最後の最後に(告知)

すみません、最後にちょっと告知させてください...!

今月末に開催されるAWSの下記イベントで登壇する予定です。
当日はコミュニティサービスにおいて、どのようにAWS×自然言語処理を活用しているかというお話する予定です。
ご興味のある方いましたら、ご参加お待ちしております!
ml-loft.connpass.com

また、すでに満席となってしまっていますが、下記イベントでもLTする予定ですので、参加者の皆さまよろしくお願いします!
globis.connpass.com

SwiftFormatで自動コード整形

$
0
0

こんにちは!エンジニアの柳村です。主にママリのiOSアプリの開発を担当しています。

今回はSwiftFormatの導入についてお話します

背景

コネヒトではSwiftはraywenderlichのswift style guideをコーディングのスタイルガイドとしてコードを書いています。

ですが、各開発者が全てのスタイルガイドを覚えているわけではなく、ときどきスタイルガイドからはずれていたりインデントや改行が他のコードと不一致だったりしてコードレビューで指摘したりするといったことがありました。

レビューではコーディングスタイルを気にするよりももっと他のことに注意を払ってレビューすることに力を注ぎたいのでコードフォーマッターを導入してみることにしました。

フォーマッターの選定

Swiftのコードフォーマッターはいくつか存在しています。

まずは試しにcocoapodsに対応していて導入が楽なSwiftFormatにしてみました。

なぜcocoapodsがよかったかというと、フォーマッターのインストールはhomebrewなどを使って各自の環境にインストールするという手段もありますが、複数人で開発を行う場合に各自のインストールしているバージョンに違いが発生する恐れがあります。cocoapodsにしておけばインストールされるバージョンが固定されるので意図しないバージョンが使われてしまうといった恐れがなくなるメリットがあるからです。

設定

cocoapodsでインストールして、Debug Configurationでビルドした際に自動でフォーマットするように設定していきます。

Podfile

   pod 'SwiftFormat/CLI', :configurations => ['Debug']

Project

XcodeのTarget->Build Phasesに以下を追加します

if [ $CONFIGURATION = "Debug" ]; then
"${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat" .
fi

.swift

.swift-versionがなければ作成しSwiftのバージョンを記載します。

5.0

.swiftformat

.swiftformatにルールを記載することでデフォルトのルールを変更することができます。

デフォルトのルールのままだとdiffがすごいことになったので以下の一部のデフォルトのルールをdisableにしました。

--disable trailingCommas, strongOutlets, unusedArguments, hoistPatternLet, blankLinesAroundMark

おわりに

このように導入はさっくりとできてしまうので、気軽に試せるのでおすすめです!

次回はswift-formatの使い方について紹介したいと思います、お楽しみに!

コネヒトは技術コミュニティになくてはならない開発組織を目指すために「ス・マイル制度」をはじめました!

$
0
0

こんにちは!CTOの@itoshoです。いきなりですが、今日はコネヒトで新たに発足した「ス・マイル制度」のお披露目をしたいと思います!

tl:dr

  • ス・マイル制度という新しい取り組みをはじめました!
  • 目的は…
    • 技術コミュニティに貢献したい
    • そのためにコネヒトらしい手法でアウトプットの支援を実現したい
  • コネヒトらしい手法とは…
    • アウトプットに対して、マイルという報酬を個人ではなくチームに発生させる
    • マイルはチームの共有資産として誰でも自由に使うことが出来る
    • マイルをどう活用すれば、チームが成長するかを全員で議論する

ス・マイル制度のロゴ

ス・マイル制度とは?

一言で言うと、アウトプット支援的な制度なのですが、従来のアウトプット支援制度と違うのはアウトプットに対して、直接インセンティブを払うのではなく、一度「マイル」という形で開発部*1に貯金される点です。そして、ス・マイル制度ではその貯めたマイルを誰でも自己研鑽のために自由に使うことが出来ます。

例えば、あるエンジニアがOSSにコントリビュートをすると一定のマイルが貯まります。そうすると別のエンジニアはそのマイルを利用して、海外のカンファレンスのチケットを買うことが出来るようになります。

f:id:itosho525:20190821230230p:plain

ス・マイル制度で実現したいこと

このス・マイル制度は、僕がCTOに就任する少し前*2から構想を練り始めた制度です。コネヒトは以前からアウトプットを推奨しており、KotlinやCakePHPのコントリビューターが在籍しています。しかし、推奨はしていたものの、特に会社として目に見える形で支援していたわけではありませんでした。ですので、CTOになったタイミングで会社としてちゃんとアウトプットを支援・評価したいと思ったのが、ス・マイル制度を立ち上げたきっかけです。

また、これまでコネヒトは多くのOSSの恩恵を受けたり、他社の事例を参考にしたりすることで、サービスを成長させてこれたと言っても過言ではありません。ですので、技術コミュニティや他社に「恩返し」したいと強く思いました。そして、コネヒトが得たものを技術コミュニティに還元出来れば、それをもとに技術コミュニティが発展し、技術コミュニティが発展すれば、それにより更にコネヒトも成長するという、Win-Winのループをつくることが出来るのではないか?と考えました。

コネヒトは「人の生活になくてはならないものをつくる」というミッションを掲げています。その中で、開発組織としても技術コミュニティや他社になくてはならない開発組織をつくることがス・マイル制度のゴールになります。

f:id:itosho525:20190821230121p:plain

制度設計時に気をつけたこと

順番が逆

ス・マイル制度で意識したことはコネヒトらしさです。そのために制度は一人で考えず、メンバー全員の意見を聞くようにしました。*3その中で、当初ス・マイル制度は、アウトプットに対して直接金銭的なインセンティブを払うというシンプルなものを考えていました。しかし、何人かのメンバーから「アウトプットを支援すること自体は良いがインセンティブの順番が逆ではないか?」という意見をもらいました。

これはどういうことかと言うと、技術コミュニティに貢献するためには、何らかのアウトプットが必要になります。そして、アウトプットをするためには、学習=インプットが必要になります。しかし、(ものにもよりますが)インプットには一定お金がかかる場合があります。ですので、アウトプット後にお金があるのではなく、アウトプットする前(=インプットをしたい時)にお金があるべきではないかというわけです。

お金のモチベーションは長続きしない

また、ほとんどのメンバーから金銭的なインセンティブは大きなモチベーションにはならないという意見も貰い、確かにダニエル・ピンクのモチベーション3.0ではないですが、お金のモチベーションは長続きしないと考え、最終的に「マイル」という形で、マイルをみんなの共有資産とすることで、お互いを支え合いながら、必要な時に必要な支援が受けられるように設計しました。

更にアウトプットの価値や、組織として何に投資するべきかを全員で議論するために、アウトプットは申請制にし、そもそもアウトプットとして認められるか?認められる場合、どれぐらいの価値があるか?というのをメンバーが承認する形にしています。*4

インプットについては、他薦を認めつつ、こちらもメンバーの申請制にしており、申請すれば自由に使える純粋な福利厚生ではなく、そのインプットによって、期待される成果やアウトプットなどを約束するようにし、その使途を議論出来るようにしました。

こうして、最終的にとてもコネヒトらしい制度が出来たと自負していますが、このような制度をつくる時は、他社の制度やHowをそのまま輸入するのではなく、自分たちのカルチャーにアジャストした形にしないと上手くワークしないということを改めて学びました。

早速盛り上がっているよ!

ス・マイル制度運用を開始してから、1ヶ月弱が立ちましたが早速多くのアウトプットが申請〜承認されています。ちなみに、管理ツールは内製しているのですが、この管理ツールは先日ブログでも紹介した「社内ツールをCakePHP4でつくりました」ツールになります。

ス・マイル制度は制度をつくることがゴールではなく、運用を開始してからが本番だと思います。ですので、運用ルール自体も今後継続的に改善していく必要があると考えています。そのため、運用に関しても僕一人ではなく、コアとなる運用メンバーを中心に開発部全体で行っています。ス・マイル制度の本当の評価や価値は今後生まれてくるものだと思いますので、また時々進捗などをお知らせ出来ればと考えています。

f:id:itosho525:20190821230523p:plain

最後に

現代のソフトウェア開発がOSSや技術コミュニティと切っても切り離せない関係にある中で、企業が金銭的な見返りや打算抜きに技術コミュニティと手を取り合っていくことの重要性はこれからますます高まっていくと僕は考えています。というわけで、ス・マイル制度が、企業と技術コミュニティをお互い持続可能な形で支え合う取り組みに出来るようこれから大切に育てていきたいと思います。そして、コネヒトを技術コミュニティになくてはならない笑顔があふれる*5開発組織にしていくぞ!

*1:開発部はコネヒトでエンジニアとデザイナーが所属する組織です。

*2:CTOに就任したのは2019年の6月。

*3:多数決で決めれば良いものではないので、最終的な意思決定はCTOの僕が行っています。

*4:もちろん、最初は判断基準がないと困ると考えたので、例えば、広報立ち会いの登壇は50000マイルというようなベースとなる基準表を用意しています。

*5:ちなみに、ス・マイルのスにはスペイン語で「あなたの」という意味も込めています。

swift-formatで自動コード整形

$
0
0

こんにちは!エンジニアの柳村です。

前回SwiftFormatの導入について紹介しましたが、今回はswift-formatについて紹介します。

ここではappleのほうのswift-formatの使い方を紹介していきます。ただこちらはSwift 5.1以上に対応しているので、Swift 5.0向けにgoogleのforkのほうのswift-formatの使い方も紹介しますがこちらはあまりオススメできません。

インストール

各プロジェクト個別にswift-formatをインストールすることを前提にします。もしグローバルにインストールしたい場合はmintもしくはNSHipsterのforkをhomebrewでインストールしてください。

プロジェクトのルートディレクトリで以下を実行してswift-formatを実行可能な状態にします。

※ 事前にxcode-selectなどでXcode11を指定してください

$ git clone git@github.com:apple/swift-format.git swift-format
$ cd swift-format
$ swift build -c release
$ cd ..

設定

以下のコマンドを実行してデフォルトのフォーマットの設定を.swift-formatに書き出します。

$ swift-format/.build/release/swift-format --mode dump-configuration > .swift-format

swift-format実行時にこの.swfit-formatに書いた設定に基づいてフォーマットされます。 デフォルトはこのようになっています。

{"blankLineBetweenMembers" : {"ignoreSingleLineProperties" : true},
  "indentation" : {"spaces" : 2},
  "lineBreakBeforeControlFlowKeywords" : false,
  "lineBreakBeforeEachArgument" : true,
  "lineLength" : 100,
  "maximumBlankLines" : 1,
  "respectsExistingLineBreaks" : true,
  "rules" : {"AllPublicDeclarationsHaveDocumentation" : true,
    "AlwaysUseLowerCamelCase" : true,
    "AmbiguousTrailingClosureOverload" : true,
    "AvoidInitializersForLiterals" : true,
    "BeginDocumentationCommentWithOneLineSummary" : true,
    "BlankLineBetweenMembers" : true,
    "CaseIndentLevelEqualsSwitch" : true,
    "DoNotUseSemicolons" : true,
    "DontRepeatTypeInStaticProperties" : true,
    "FullyIndirectEnum" : true,
    "GroupNumericLiterals" : true,
    "IdentifiersMustBeASCII" : true,
    "MultiLineTrailingCommas" : true,
    "NeverForceUnwrap" : true,
    "NeverUseForceTry" : true,
    "NeverUseImplicitlyUnwrappedOptionals" : true,
    "NoAccessLevelOnExtensionDeclaration" : true,
    "NoBlockComments" : true,
    "NoCasesWithOnlyFallthrough" : true,
    "NoEmptyAssociatedValues" : true,
    "NoEmptyTrailingClosureParentheses" : true,
    "NoLabelsInCasePatterns" : true,
    "NoLeadingUnderscores" : true,
    "NoParensAroundConditions" : true,
    "NoVoidReturnOnFunctionSignature" : true,
    "OneCasePerLine" : true,
    "OneVariableDeclarationPerLine" : true,
    "OnlyOneTrailingClosureArgument" : true,
    "OrderedImports" : true,
    "ReturnVoidInsteadOfEmptyTuple" : true,
    "UseEnumForNamespacing" : true,
    "UseLetInEveryBoundCaseVariable" : true,
    "UseOnlyUTF8" : true,
    "UseShorthandTypeNames" : true,
    "UseSingleLinePropertyGetter" : true,
    "UseSpecialEscapeSequences" : true,
    "UseSynthesizedInitializer" : true,
    "UseTripleSlashForDocumentationComments" : true,
    "ValidateDocumentationComments" : true},
  "tabWidth" : 8,
  "version" : 1}

設定項目についてはdocumentに説明が書いてありますがruleについては詳細にかかれていないのでルール名から察するしかありません。

実行

以下のコマンドでコードをフォーマットできます。

$ swift-format/.build/release/swift-format -r ソースコードのフォルダのパス -i
  • -rで指定したフォルダ内を再帰的にswiftのファイルを探します。個別のファイルを指定したい場合は-rは不要です。
  • -iを指定するとswiftのファイルをフォーマットします。(これを指定しないとコンソールにフォーマット結果が出力されるだけです)

その他のオプションについては以下をご参照ください。

OVERVIEW: Format or lint Swift source code.

USAGE: swift-format [options] <filename or path> ...

OPTIONS:
  --configuration         The path to a JSON file containing the configuration of the linter/formatter.
  --in-place, -i          Overwrite the current file when formatting ('format' mode only).
  --mode, -m              The mode to run swift-format in. Either 'format', 'lint', or 'dump-configuration'.
  --recursive, -r         Recursively run on '.swift' files in any provided directories.
  --version, -v           Prints the version and exists
  --help                  Display available options

POSITIONAL ARGUMENTS:
  filenames or paths      One or more input filenames

実行してみるとわかりますが、プロジェクト全体にかけると結構遅いです。(ママリだと1分かかりました)

また、exclude directoryとかの指定ができないので3rd Partyのソースコードなどがあった場合に特定のディレクトリのみを除外するといったことができません。

これではちょっと頻繁に実行するのはちょっと厳しいです。

そこで以下のように差分のあるswiftのファイルのみをswift-fomatに食わせるようなスクリプトをprojectのbuild phasesやgitのprecommit hookに設定するといい感じになると思います。

$ git diff --name-only | grep .*\.swift$ | xargs swift-format/.build/release/swift-format -i

まとめ

Swift 5.1以降に限定されますがswift-formatでフォーマットできました。

swift-formatは2019年8月現在まだreleaseは一つもされてないのでまだこれからといった感じはしますが、ママリのアプリではXcode11およびSwift 5.1に対応するタイミングくらいに導入しようかと考えています。

------✂------✂------✂------✂------✂------

オマケ:Swift 5.0でswift-formatを使いたい場合

Swift 5.1でやる場合との違いはインストールです。

インストール

googleのswiftのforkの特定のコミットを利用します。

$ git clone git@github.com:google/swift.git swift-format
$ cd swift-format
$ git checkout 74995aa1473b213977a14c0cb477ce89875ee27b
$ git submodule update --init
$ swift build -c release
$ cd ..

あとはSwift 5.1の手順と同じです。

実行結果

ただappleのと比べると実行結果には違いがあります。(5.0で動かすために古いコミットを指定しているのが原因なのですが)

例えばRxSwiftのインデントがこんな感じになってしまいます。。 たぶん不具合でrespectsExistingLineBreaksの指定が効いていないのではないかなと思います。

-        cameraButton.rx.tap-            .flatMapLatest { [weak self] _ in-                return UIImagePickerController.rx.createWithParent(self) { picker in-                    picker.sourceType = .camera-                    picker.allowsEditing = false-                }-                .flatMap { $0.rx.didFinishPickingMediaWithInfo }-                .take(1)-            }-            .map { info in-                return info[.originalImage] as? UIImage-            }-            .bind(to: imageView.rx.image)-            .disposed(by: disposeBag)+        cameraButton.rx.tap.flatMapLatest { [weak self] _ in+            return UIImagePickerController.rx.createWithParent(self) { picker in+                picker.sourceType = .camera+                picker.allowsEditing = false+            }.flatMap { $0.rx.didFinishPickingMediaWithInfo }.take(1)+        }.map { info in+            return info[.originalImage] as? UIImage+        }.bind(to: imageView.rx.image).disposed(by: disposeBag)

あと@unknown default@unknowndefaultになってしまうバグがあってコンパイルに通らなくなってしまったりするのでお気をつけください。

Kotlin Fest 2019に参加しました!

$
0
0

2017年11月にAndroidエンジニアとしてjoinした関根です! 2019年8月24日に開催されたKotlin Fest 2019に参加してきました! 今回の記事では、私が聞いた公演を感想をおりまぜて紹介させて頂きます。

f:id:katsutomu0124:20190827142731p:plain

弊社はスポンサーとしての参加に加え、改めて学ぶContractsという内容で弊社富田が登壇をさせていただきました。 余談ですが、スポンサー参加や登壇への弊社の意思の一端が現れたこちらの記事を是非とも合わせてお読みください!!

tech.connehito.com

オープニングセッション

さて、肝心のKotlin Festは、長澤太郎さんの挨拶とSvetlana Isakovaさんのオープニングセッションで幕が上がりました。

Svetlana IsakovaさんはJetBrains所属のエンジニアであり、世界で1番Kotlinに詳しい一人です。 そんな彼女からKotlinの新機能について紹介をしていただきました。 スライドを読み進めていくと、Contractsも紹介がありました。Contractsという機能は、富田の講演テーマでもあり、実際に当日はこのオープニングセッションでも言及しご紹介いただきました! Svetlanaさん誠にありがとうございました!!!

Kotlin コルーチンを理解しよう 2019

最初は、弊社でも今後の利用を検討しているCoroutineを理解するべく、八木俊広さんによる講演を聴講しました。

Kotlin Coroutineの基礎をわかりやすく解説していただいたことに加えて 設計の方針とCoroutineのテストのアプローチまで幅広くお話を聞けました。 特にCoroutineのテストはブロッキング処理が絡むこともあり、実践的なアプローチの内容が聞けて非常に学び深かったです。 八木さん、貴重なお話をありがとうございました!

改めて学ぶContracts

続いて、応援の為、富田の講演を聴きに行きました。

speakerdeck.com

本人はとても緊張をしていたようですが、蓋を開けてみれば非常に好評を頂き、会場には笑いが溢れており流石の富田という講演でした。 当日の登壇のために社内2回のリハーサルをして臨んだのですが、社内メンバーからのFBも反映されており、限られた時間の中で、最後まで手を抜かずに良いものを届ける富田の人柄を再認識しました。 ちなみに余談ですが、社内のランチイベントでは、Kotlin Fest当日の講演のように彼の話でメンバーは笑わせてもらっています。 トミーさん登壇お疲れ様でした!(社内ではトミーさんと呼ばれています)

サーバーサイドKotkinでGraphQLをやってみよう

続いて、弊社でも度々話題になるGraphQLについて学ぶべく、しらじさんによる講演を聴きに行きました。

speakerdeck.com

GraphQLの基礎からKotlinでの記述方法に加え、プロダクションで必要になる認証情報の扱い方など導入に到るまでのガイドラインのようなお話でした。 個人的に、印象的だったのが、GraphQLでのテストについてクラスを綺麗に分けることが前提なのでmockすればOKという結論が印象的でした! 聴講者を巻き込みながら、会場を盛り上げるしらじさんの講演は得るものが多く楽しく学ばせて頂きました。 しらじさん、貴重なお話をありがとうございました!

フロントエンドもKotlinで書きたい! -WebページをKotlin/JSで作った軌跡-

普段フロントエンドを触る機会が少ないので興味を持ち、にしこりさぶろ〜さんの講演を聴きに行きました。

speakerdeck.com

Kotlin/JSには無知識で参加したのですが、サンプルアプリをもとに解説していただいて丁寧でわかりやすい講演でした。 すでにKotlin/JSで2つのサイトを作られていて、導入する上での注意点などにも触れて頂き、非常に説得力の高いお話でした Kotlin/JSはまだまだ情報が少ないので、みんなでアウトプットして行こうというメッセージはコミュニティを活性化する大事なメッセージだと思いました。 にしこりさぶろ〜https://blog.hatena.ne.jp/connehito/connehito.hatenablog.com/edit?entry=26006613406773967#さん、貴重なお話をありがとうございました!

Kotlin/Nativeはなぜ動くのか

最後に、Kotlin1.3の目玉の一つと言えるKotlin/Nativeの中身を理解するために荻野陽太さんの講演を聴きに行きました。

speakerdeck.com

ネイティブバイナリに変換される過程をステップごとに解説するお話で、学問的な内容に感じておりましたが、丁寧に解説していただいたおかげでよく理解できました。 今回のお話の内容の理解が深まったことで、個人的な学びに加え、Kotlin/Nativeの様なプラットフォーム非依存の技術の知識を社内に持ち込めるきっかけを頂きました! 荻野さん貴重なお話をありがとうございました!

Kotlin Festに参加して感じたこと

その他、聴講以外の感想に触れて本記事を終了したいと思います。

スタッフの運営スキルが高い

まず感じたことはKotlinFestに関わるスタッフの運営スキルが高いことでした。 誘導が非常に丁寧で、会場の移動もスムーズに行うことができました。 また富田の登壇時もアテンドも非常に真摯にご対応いただきました。 スタッフの皆様、当日お疲れ様でした!ありがとうございました!

コミュニティ運営を愛している

クロージングの際に、主催の長澤 太郎さんから案内があり、Kotlinコミュニティの主催者を壇上にあげ、1分間のアピールを募るという時間がありました。 JKUG以外のKotlinコミュニティを盛り上げていこうと言う長澤さんのメッセージと、それを暖かく迎える参加者の皆様から、Kotlinとコミュニティを盛り上げていこうと言う愛を感じられた一幕でした。 長澤さんを初めとした主催の皆様と参加された皆様。当日はありがとうございました!

最後に

改めて、主催の長澤さんを初めとした運営の皆様、登壇者の皆様、当日参加された皆様、本当にお疲れ様でした! 次回は私も登壇をできる様に精進いたします!また来年よろしくお願いします!


コネヒトはiOSDC Japan 2019に協賛いたします!

$
0
0

こんにちは、iOSアプリエンジニアのあぼ(@suxisuxido)です!

本日は、iOSアプリ開発者の祭典iOSDC Japan 2019に協賛するお知らせです。

コネヒトはiOSDC Japan 2019に協賛いたします!

iOSDC Japan 2019に、シルバースポンサーとTシャツスポンサーとして協賛いたします。Tシャツがどんな仕上がりなのか、今から楽しみでワクワクです!

iosdc.jp

スポンサーするにあたって、コネヒトは「人の生活になくてはならないものをつくる」というミッションを掲げているので、技術コミュニティについても同様に、サポートして一緒に盛り上げていくことができたら、と思っております。*1

イベント概要

  • 開催 2019年9月5日(木)~9月7日(土)
  • 場所早稲田大学 理工学部西早稲田キャンパス63号館
  • 対象 iOSおよび関連技術のエンジニア
  • タイムテーブルhttps://fortee.jp/iosdc-japan-2019/timetable
  • 主催 iOSDC Japan 2019 実行委員会 (実行委員長 長谷川智希 @tomzoh ) / WASEDA-EDGE人材育成プログラム

iOSアプリ開発者の興味がわくような発表テーマが目白押しなので、チェックしてみてください。メインの発表だけでなく、9月6日(金)と9月7日(土)は、突然思いついたネタをその場で発表できるようなアンカンファレンスが開催されます。また9月6日(金)のLT終了後、よくあるアルコール片手の懇親会ではなく「茶会」という形で参加者同士が交流できる企画もあるそうです!楽しみです!

iOSDCトークンはこちら!

#コネヒトとiOSDC

iOSDCチャレンジで使うトークンです!すごく楽しそうな企画ですので、まだの方はチェックしてみてください。

iOSDCには私とiOSアプリエンジニアのyanamuraが参加予定ですので、会場でお会いした際にはぜひお話しましょう!

potatotips #64に参加・LTしてきました

$
0
0

こんにちは!エンジニアの柳村です。

2019/8/27に開催されたpotatotips #64 に参加・LTしてきました。

potatotips.connpass.com

今回の会場はReproさんでした。 代々木駅から出てすぐの好立地で、おしゃれな内装でしたよ(写真を取り忘れました・・

iOS関連の発表について簡単にレポートします。

Object指向でFatViewControllerをなくす

@coffeegyunyuさん

StoryboardにObjectを配置してViewControllerの代わりにこちらで処理をさせる方法についてでした。StoryboardのObjectってなんに使うのかあまりわかってなかったのですが、ようやく使い方が判明してすっきりしました。

コントロールセンターとたたかう

@5mingame2さん

ゲームの操作中にコントロールセンターがでてきちゃうのを防ぐ方法について紹介されていました。ブースでいろんな人に試遊してもらって想定外の操作がされていないか観察するという試みはとてもいいなと思いました。

Conditional Content in SwiftUI

@yanamura_

SwiftUIで条件によってViewを出し分ける方法となぜその方法だとうまくいくのかについてお話しました。 私の個人のブログのほうでも書いてますのでよろしければご参照ください。

ちなみにママリではまだSwiftUIは使ってませんが、移行していきたい気持ちです!

iPhoneでFeliCaを読み取ってみた

@tanakasan2525さん

いろんな種類のFeliCaの読み込みを簡単にするライブラリをつくられたそうです。FeliCaの読み込み使ったアプリを作るのが楽になる便利ライブラリのようでした。

DIKitで人間がクラス間の依存関係解決するのを終わらせる

@yuuxenoさん

DIKitで依存関係の実装コードを自動生成することについて紹介されていました。人の手で実装するとどうしてもミスはしてしまいがちなので自動化できるところは自動化していくというアプローチはとてもいいなと思いました。

SwagGenのススメ

@yuta24さん

<<資料は公開されたら反映します>>

OpenAPIで書いたAPI定義からSwagGenを使ってコードの自動生成について紹介されていました。swagger-codegenと比較してtemplateが良い感じのようでした。

macOS アプリを2年ぶりにメンテしたら原型がなくなった話

@AkkeyLabさん

個人で開発していたmacOSのアプリをFluxを採用したりAnalyticsを導入したりして書き換えた際のTipsを紹介されていました。Firebase AnalyticsがMacアプリには対応してないというのは意外でした。

Flutter Pluginを作る

@konyavicさん

ReproのSDKのFlutter対応版をつくられたときのtipsを紹介されていました。Cordovaとかと比べるとFlutterのpluginはとても簡単につくれるとのことでした。

さいごに

わたしは子育てがあって最近はなかなか勉強会に参加できないのですが、久しぶりに参加してLTしたり懇親会でいろんなひととお話したりできてとても楽しかったです!また、参加できないときもpotatotipsはLT資料をアップロードしてくださる発表者が多くてとても助かっています!

ここで突然のお知らせです!

なんと、きたる2020年2月には弊社コネヒトにてpotatotips #67が開催予定です!!!

ぜひLTしにきてくださいね!お楽しみに!!!

iOSDC Japan 2019に行ってきました!

$
0
0

こんにちは、iOSアプリエンジニアのあぼ(@suxisuxido)です!

2019年9月5~7日に開催されたiOSDC Japan 2019に行ってきました。今年はダークモードの横断幕とパネルが用意されていたり、お茶会やiOSDCチャレンジといった新たな試みも追加され、進化したお祭りになっていました!

f:id:aboy_perry:20190911200700j:plain
ダークモードのパネル

コネヒトはシルバースポンサーと、Tシャツスポンサーとして協賛していたので、ノベルティを受け取るやいなやTシャツを袋から取り出し写真を撮ってはしゃぎました。

f:id:aboy_perry:20190912115131j:plain
ノベルティのTシャツ

本編となる各セッションの資料と動画は、公式のタイムテーブルから辿れる各プロポーザル詳細ページにリンクが貼ってあるので、そこから見ることができます。本ブログでは感想も交えながらいくつかのセッションをご紹介したいと思います。

Advanced Segue 2019年のSegue事情

Segueを使ったことがない私にとっては、Segueの使いかたや最新のSegue事情を一気にインプットするいい機会だったので聴きに行きました。

私は、Segueは画面遷移で利用するもの、と思ってしまっていたんですがそれは間違いで、実際にはperform()メソッドをオーバーライドすればそこでできることは全てできるので、自由度が高く可能性がある機能だな〜と感じました。Storyboard上でSegueを利用する際には接続先が必要で、例として空のStoryboard Referenceを置いて繋げるというやり方がセッションで紹介されていましたが、空のStoryboard Referenceを使うことのコスト感については、セッション後にスピーカーがつぶやいている内容が興味深いです。

iOSアプリのリジェクトリスクを早期に発見するための取り組み

リジェクトによる手戻りは大きく、またタイミングによっては休日に対応する必要があるため、できるだけ早期に発見したいところです。DeNAのSWETグループではReviewガイドラインをシステムに落とし込んで運用することで早期発見を実現しているとのことでした。システム化はもちろん、開発者からSWETグループへメモを送れるようにしてコミュニケーションコストを削減している点もすごいな〜と思いました。

また、最新のReviewガイドラインのキャッチアップ方法としては、コネヒトでもすでにhttps://developer.apple.com/news/を購読してslackに流すようにしていましたが、セッション内で紹介されていた「Reviewガイドラインのdiffを取る」という取り組みが結構手軽にできて良さそうなので、試しにマネしてみることに!

f:id:aboy_perry:20190911175358p:plain
社内情報共有ツールDocbaseに作ってみたReviewガイドラインページ

実機の管理とおさらば!AWS Device FarmでiOSのテストをしよう!

タイトルの「実機の管理とおさらば!」に惹かれて聴きに行きました。なぜテストを書くのか、どうやって書けばいいのかというところに触れつつ、最終的にAWS Device Farmを使った自動テストの運用方法がイメージできる内容になっています。

現実的には全パターンのデバイスを検証機として用意できない場合のほうが多いでしょうし、特定のOS、特定のデバイスでしか再現しない不具合を事前に検知するというのはなかなか難しく、例えばユーザーからの報告で気づいてもそのデバイスが無ければデバッグできません。セッションを聴いて、AWS Device Farmのようなアプリケーションテストサービスを使うことも選択肢のひとつだな〜と思えるようになりました。ちょうどまたAppleから新たなiPhoneが発表されましたし、iOS/Androidともにデバイスは本当にたくさんありますからね・・・。

1ヶ月半でプッシュ通知許諾率を17%から40%にあげた話

こちらはLTです。タイトルの時点で色々と仮説をたてて聴いていたんですが、結論は「起動直後に承諾ダイアログを表示する」というものすごくシンプルなものでした!

会場でもドカッと笑いが起きていて、私も笑ったのですが、これすごく学びがあるな〜と思っています。問題を解決するときに、その問題を無駄に複雑に考えてしまってはないかという切り口での考察も重要なのかもな〜と考えさせられました。

セッション以外にも楽しいことがたくさん!

セッション以外にも楽しいことがたくさんあって、例えば、お茶会のときにpixivの方からいただいたリジェクトに効く御守りは、早速チームのカンバンにぶら下げています。Androidアプリでも審査が入るようになったこともありますし、iOS/Android両方に効果があるといいなーと願っています!

f:id:aboy_perry:20190910152350j:plain
リジェクト除けの御守り

スポンサーブースも楽しくて、RoomClipで利用されているデザインガイドラインの写真を撮らせていただいて、社内のデザイナーに共有できたり。Uberが公開しているクロスプラットフォームアーキテクチャRIBsと、昨年のiOSDCで話題になったMicroViewControllerを組み合わせて本番運用している話を聞けたりしました!

それから、大学時代の先輩や、前職で一緒に仕事していた人と久しぶりに会って話すことができて、これもiOSDCの魅力だな〜としみじみ感じました。

おわりに

クロージングで主催者が「We are a community. We are not a school.」と言っていたのが強く印象に残っています。今年もこの楽しいコミュニティは関わる人全員で作られました。スタッフの方々、早稲田大学、スポンサーやスピーカーの方々、そして参加者のみなさんに感謝しています!

帰ってブログ書くまでがiOSDCでおなじみのiOSDCですので、本ブログの公開をもって私のiOSDCは終了です!ありがとうございました!

PHPカンファレンス北海道2019に協賛&参加してきました!(資料まとめ)

$
0
0

こんにちは!サーバーサイドエンジニアの @fortkleです!
今回は、つい先日開催された「PHPカンファレンス北海道」にCTOと2人で参加してきたのでレポートしたいと思います。

PHPカンファレンス北海道2019

2019年9月21日(土)に札幌で開催された「PHPカンファレンス北海道2019」。
前回開催が2016年だったので約3年ぶりの開催だったようです。

今回、縁あってシルバースポンサーとして協賛もさせていただきました。
コネヒトとしては、地方で開催されるカンファレンスに協賛するのはおそらく初めてで、PHPの技術コミュニティの発展に少しでも支援できたのであれば幸いです!

ちなみに、 コネヒトではメインプロダクトである「ママリ」を始めとして開発のメイン言語としてPHPを活用しており、フレームワークとしてはCakePHPを採用しています!

企業ブースとスポンサーセッション

f:id:fortkle:20190921103955j:plain
CTOと5周年記念PRキャラクター「ママリちゃん」

当日は1Fに企業ブースを出させていただきました。ブースまで来てくださった参加者の皆様、ありがとうございました!
「ママリ」は、特に男性にとってはほとんど接触機会がなく知名度も低いのですがそれでも「ママリ知ってます!」や「ペチコン東京で見ました!」というお声をちらほら頂けたのが非常に嬉しかったです!

f:id:fortkle:20190921161049j:plain
スポンサーセッション中のCTO伊藤

また、スポンサーセッションもさせていただきました。 当日のスライドも貼っておきます。

発表の中では、

  • ① なぜコネヒトはカンファレンスのスポンサーをするのか?
  • ② 技術コミュニティへの貢献を支援するための取り組み

という2点について紹介させていただきました。
2点目については、以前こちらのブログでも紹介した「ス・マイル制度」が取り組みの柱となっているのでもし興味がある方がいらっしゃいましたらご覧ください!

tech.connehito.com

当日のセッション

アンカンファレンス、懇親会LTを除くセッションについて、すでに公開されている資料をまとめてみたので参考にしてみてください! ※ 敬称略

最後に

主催の @makies さんを初めとした運営の皆様、登壇者の皆様、当日参加された皆様、本当にお疲れ様でした! 来年は通常セッションに弊社からも登壇者を出せるように頑張っていこうと思います!

余談

北海道といえばやっぱりラーメンですよね...
最高でした...

f:id:fortkle:20190920200107j:plain

味噌ラーメンも...

f:id:fortkle:20190922121030j:plain

カンファレンス終了後の懇親会では豪華な料理が振る舞われていました...

f:id:fortkle:20190921190119j:plain

北海道最高!!!また行きます!

#builderscon tokyo 2019 で当日ボランティアスタッフをしてきました

$
0
0

こんにちは!2019年7月にサーバーサイドエンジニアとして入社しました @takoba_です!!

だいぶ遅くなりましたが、先月末に開催されました「builderscon tokyo 2019」に当日ボランティアスタッフとして参加してきましたので、そのレポートをします。

昨年から当日スタッフとして参加するようになりましたが、カンファレンススタッフとして参加するカンファレンスは一味違う楽しみ方ができるのでオススメです!そんな魅力が伝わるとうれしいです...!

"builderscon tokyo 2019"とは?

©️builderscon
©builderscon

buildersconでは毎年、他のカンファレンスでは聴けないようなトークが多くエントリーされています。過去にはサウンドエンジニアリングをテーマとしてセッションで、楽器(エレキギター)を持ち込んでデモを披露したスピーカーの方もいらっしゃいました...!

buildersconは、「知らなかった、を聞く」をテーマとした技術を愛する全てのギーク達のお祭りです

buildersconではトークに関して技術的な制約はありません。特定のプログラミング言語や技術スタックによるくくりも設けません。必要なのは技術者達に刺激を与えワクワクさせてくれるアイデアのみです。

builderscon tokyo 2019

今年は、 2019/08/29 〜 2019/08/31 の3日間、東京・北千住にある東京電機大学(東京千住キャンパス)1号館にて開催されました。立派なキャンパスだった...!

カンファレンススタッフをやると一味違う楽しみ方ができる!

カンファレンススタッフとしてカンファレンスに参加すると、参加者とはまた違った楽しみがあります。

まず、カンファレンスの裏側を気軽に覗くことができます。 自分が普段参加してるカンファレンスが「なるほど?こうやって運営されてるのね??」という部分が知れたりするのは、学びあるな〜〜〜と参加するたびに思います。
同じカンファレンスでも、以前実施した際の振り返りをもとにアップデートされている箇所があったりしますし、"ビジネス"としてのカンファレンスが垣間見えたりするのも、運営側に回ったことで得られる情報だったりするので、もし興味のある方は是非ともカンファレンススタッフをやってみてほしいと思います...!

また、一緒にスタッフをやった方々ともたくさん交流できたのは楽しかったです!スタッフとしてカンファレンスに関わっている方は、だいたい同じ属性を持っている方々なので話題も多様にありますし、終わった後にたびたび飲みに行ったのもよい思い出です。

ちなみにコネヒトでは、カンファレンス参加が業務扱いになります!!! [PR]

「カンファレンススタッフ業も業務扱いになりますか...?」と恐る恐る上長に聞いてみたら「なります!!」と二つ返事でお答えいただきました!

また、コネヒトは積極的にカンファレンスへのスポンサードもしております。当ブログにもたくさんの参加エントリがありますので、お時間ある時に見てみてください〜〜

当日スタッフはどんなことをやるのか

そんな魅了たっぷりなbuilderscon当日スタッフですが、具体的にはどんなことをやるのでしょう。その一部を、以下で簡単にご紹介します。

会場設営 / 撤収

©️builderscon
こういったホールに椅子と机を敷き詰めたりするお仕事です ©builderscon

お貸しいただいた会場を、カンファレンス実施するための状態に模様替えします。今回は広いホールを3部屋に分割するかたちで椅子や机をレイアウトしたりしてました。

また、今回は主催者の趣味によって、すっごいでかい"移動式9面サイネージパネル"が設置されました。こいつの搬入と組み立てがなかなか大変だった...。その分、完成して最初に映ったときに謎の達成感があっておもしろかったです。

各部屋の運営

©️builderscon
ホールを仕切って各部屋に変換したあとの図 ©builderscon

"部屋"とは、各種トークが開催される会場のことを指します。各部屋には、だいたい4-5名程度の担当スタッフを配置して、以下のようなことをしてました。

  • 会場設営
    • プロジェクター、タイマー、マイクetc. のセットアップ/動作確認
  • 司会/会場内アナウンス
  • マイクランナー(質問したい方へマイクを持っていく係)
  • 録画用ビデオカメラの監視
    • ブレた映像が残らないよう、人がぶつからないように注意します
  • 会場内整理
    • トークが終了するたびに参加者のみなさまの出入りが発生します
    • 部屋の動員状況によっては、心苦しいですが入場制限をしたりもしてました

ぼくは今回部屋担当になったんですが、4-5名で回していればシフトを組んで自分が観たいセッションの時には抜けられるようにすることも可能です。
(とはいえ、たまたま割り当てられた部屋が聴きたいセッションばかりで構成されてたので、ほぼほぼ自分の担当する部屋にいた気がします。)

ちなみに、初めてスタッフとして参加しても、丁寧なマニュアルが用意されていたり、"部屋番長"と呼ばれる歴戦の猛者がサポートしてくれるので安心です。

ノベルティ仕分け、配布準備

©️builderscon
ノベルティは受付でお渡ししてました! ©builderscon

来場する皆さまへお渡しするノベルティ*1が大量に届くので、それらを仕分けたり配布可能な状態にして受付に配置したりします。

昨年はチラシやパンフレットなど一部のものがバラバラに届いていたので、人海戦術でそれらを配布可能な状態にセットアップする作業が発生してましたが、今年はすでに配布可能な状態で届けられていて進化を感じました...!

おわりに

いかがでしたか?カンファレンススタッフやってみたくなりませんか???

個人的には、割と歴史が長いbuildersconのスタッフはカンファレンススタッフデビューするのにオススメだと思っています!興味のある方は、例年6月末くらいから募集し始めますので、buildersconのTwitterアカウントをフォローしてお待ちください👀

そして...ようやく...ブログを書いたので...ぼくの「builderscon tokyo 2019」が終わりを告げたぞ...!(毎度のごとく遅筆なのなんとかしたい)

*1:トートバッグ、Tシャツ、個人スポンサー向けグッズetc.

AWS × slackを用いたDDL自動実行フローを構築しました

$
0
0

こんにちは!MLエンジニアの野澤(@takapy0210)です。

10月から軽減税率が始まりましたね。みなさんの身の回りで混乱は起きていませんでしょうか?
そんな中、軽減税率に関するこんな記事を見ました。専門家の人たちでも判断に困る事例があるようなので、難しいですね。

さて、本日はAWS × slackを使って、DDLの自動実行フローを構築した話をできればと思っています。

目次

DDLって何?

リレーショナルデータベースを対象として、テーブルなどの構造を制御することができる言語です。 「CREATE」「DROP」「ALTER」などが書いてあるアレです。

コネヒトでは、SchemafileでDBスキーマを管理することができるRidgepoleというツールを用いています。
このRidgepoleの実行環境はコンテナ化されており、AWS ECS上のサービスとして稼働しています。

従来のフロー

DBは開発環境(以下、dev環境)本番環境(以下、prd環境)に分かれており、dev環境での実行は開発者、prd環境での実行は権限のあるオペレーターに依頼して実行してもらう、というフローになっていました。

下記が詳細な手順です。

  1. local環境で開発(Schemafile)
  2. Github RepositoryにPR
  3. Githubでmaster merge(dev環境にECSデプロイが走る)
  4. 実行環境のインスタンスにSSHログイン
  5. dry-runでDDLの内容が合っているか確認(dev環境)
  6. dry-runの内容に問題がなければdev環境でridgepole(DDL)の実行
  7. 最新のコミットに対してタグをpush(prd環境にECSデプロイが走る)
  8. オペレーターに依頼し、prd環境でdry-runの実行
  9. dry-runの内容に問題がなければprd環境でridgepole(DDL)の実行

ごちゃごちゃ書きましたが、煩雑な雰囲気だけでも伝わって頂ければと思います。

今回は上記の 4, 5, 6, 8の部分を自動化しましたので、次章以降で詳細をお伝えできればと思います。

新・自動化フロー

自動化のアーキテクチャは下記のようになっています。 dry-run実行フローとDDL実行フローの2パターンに分けて説明します。

dry-run実行

f:id:taxa_program:20191003172131p:plain
dry-run実行時

図を見ていただくと分かりやすいと思うのですが、ECRのimage更新を検知して、Step Functionisが起動し、Fargate Taskでdry-runスクリプトを実行し、結果をslackに通知してくれます。

slackへ通知するメッセージは、下記のようにdry-runの内容DDL実行の可否を問うボタンで構成されています。

f:id:taxa_program:20191003173000p:plain
dev環境のdry-run slack通知メッセージ

上記でdry-runの結果を確認し、問題なければOKボタンを押下するだけで、dev環境へのDDLが実行できます!めっちゃ便利!

ちなみに、prd環境のDDL実行はオペレーターが手動で行う運用としているため、dry-runの結果通知のみにしています。(実行時間帯や権限の関係で)

f:id:taxa_program:20191003184156p:plain
prd環境のdry-run slack通知メッセージ

次に、上記のslackメッセージでOKボタンを押下した後のDDL実行フローについてご紹介します。

DDL実行

f:id:taxa_program:20191003173408p:plain
DDL実行

slackメッセージのボタン押下イベントをAWS API Gatewayで受け取り、そこからLambda経由でStep Functionsを起動しています。
そしてFargate TaskでDDL実行スクリプトを実行し、結果をslackに通知します。

f:id:taxa_program:20191003174457p:plain
DDL実行結果のslack通知

自動化して何が嬉しかったか

DDL実行の都度、実行環境にSSH接続して、dry-runを実行して、結果を確認して、、、という手順が省略されたことが大きいと思います。

また、prd環境で実行する際は

  • オペレーターがdry-runを実行して
  • 開発者に対して、dry-runの結果に誤りがないか確認して
  • 同意がとれたら実行する

という手順を踏まなければならなかったのが、dry-runの結果は自動的にslackに通知されるので、都度開発者にdry-runの整合性を確認する必要がなくなりました。
そして、dry-run結果のメッセージに返信する形で「prd環境で実行お願いします!」と一言伝えるだけでOKになったのも、作業負荷の軽減に繋がっていると思います。

実際に、煩雑だった従来の手順が

  1. local環境で開発(Schemafile)
  2. Github RepositoryにPR
  3. Githubでmaster merge(dev環境にECSデプロイが走る)
  4. 実行環境のインスタンスにSSHログイン
  5. dry-runでDDLの内容が合っているか確認(dev環境)
  6. dry-runの内容に問題がなければdev環境でridgepole(DDL)の実行
  7. 最新のコミットに対してタグをpush(prd環境にECSデプロイが走る)
  8. オペレーターに依頼し、prd環境でdry-runの実行
  9. dry-runの内容に問題がなければprd環境でridgepole(DDL)の実行

下記のように省略されました。

  1. local環境で開発(Schemafile)
  2. Github RepositoryにPR
  3. Githubでmaster merge(dev環境にECSデプロイが走る)
  4. slackでdry-runの実行結果を確認し、メッセージ内のボタン押下でDDL実行
  5. 最新のコミットに対してタグをpush(prd環境にECSデプロイが走る)
  6. slackでdry-runの実行結果を確認し、問題が無ければオペレーターにridgepole(DDL)の実行を依頼

また、従来はEC2バックエンドだったため、常にインスタンスが起動している状態でしたが、今回はFatgate Taskで実行しているため、コストの観点からも恩恵を受けることができそうです。

アーキテクチャ構築のポイント

上記フローを構築する際の注意点をいくつかあげてみます。

Step FunctionsでFatgate Taskを実行するときの注意点

セキュリティグループを正しく指定する必要がある

Step Functionsの定義でセキュリティグループを設定しないと、デフォルトのセキュリティーグループが自動で割り当てられる(?)ようなので、想定するセキュリティーグループを指定する必要があります。

下記に例を載せておきます。

...
"States": {"Fargate task": {"Comment": "Fargate taskの実行",
      "Type": "Task",
      "Resource": "arn:aws:states:::ecs:runTask.sync",
      "Parameters": {"LaunchType": "FARGATE",
        "Cluster": "arn:aws:ecs:ap-northeast-1:12345678:cluster/cluster-name",
        "TaskDefinition": "arn:aws:ecs:ap-northeast-1:12345678:task-definition/task-name:1",
        "NetworkConfiguration": {"AwsvpcConfiguration": {"SecurityGroups": ["sg-id"],
            "Subnets": ["subnet-id"],
            "AssignPublicIp": "ENABLED"
          }}}
...

EcsTask実行ポリシーに、「タスクを実行するRoleにアクセスする権限」を追加する必要がある

Step Functionsに付与するIAMロールに、EcsTask実行ポリシーを追加する必要があるのですが、自動的に追加されるポリシーには、EcsTaskを実行するRoleにアクセスする権限が付与されません。(これがないとFargate Taskが実行できません)

なので、下記のようにタスクロールタスク実行ロールの2つのロールに対するアクセス権限を、Step FunctionsのIAMロールに追加する必要があります。

...
{"Effect": "Allow",
    "Action": ["iam:GetRole",
        "iam:PassRole"
    ],
    "Resource": ["arn:aws:iam::12345678:role/EcsTaskRoleName",
        "arn:aws:iam::12345678:role/EcsTaskExecutionRoleName"
    ]}
...

slack apiとAWS API Gatewayの連携の注意点

ここが一番大変でした・・・
ネットにもあまり情報がないので、トライ&エラーを繰り返し、なんとか実装できました。

API Gatewayの設定

まず、AWS API GatewayでREST APIを作成します。
今回はLambdaを呼び出すので、下記のように指定します。

f:id:taxa_program:20191004172039p:plain
POSTメソッドの作成

すると下記のようなAPIが作成されます。

f:id:taxa_program:20191004172335p:plain
API作成例

次に、アクションボタンからAPIをデプロイします。

f:id:taxa_program:20191004172530p:plain:w400
APIのデプロイ

すると、下記のようなURL(エンドポイント)が取得できます。
このURLは、後述のslack APIを作成する時に必要になりますので、メモしておいてください。

f:id:taxa_program:20191004172803p:plain
API Gatewayのエンドポイント

slack APIの作成

slack APIは、slackメッセージ内のDDL実行ボタンを押下したタイミングでAPI Gatewayを呼ぶ時に必要となります。

下記に手順を示します。

1. こちらからslackAppを作成する

f:id:taxa_program:20191007103217p:plain:w400
slack Appの作成

2. Interactive Componentsの設定
AWS API Gatewayを作成したときに取得したエンドポイントを設定します

f:id:taxa_program:20191007103457p:plain:w400
Interactive Componentsの設定

3. OAuth & Permissionsの設定
まず初めに、Tokenを取得します。ここで取得したTokenは、AWS Lambda → slackに下記のようなボタン付きメッセージを投稿する際に使用します。

f:id:taxa_program:20191003173000p:plain
ボタン付きメッセージ

Tokenの取得

f:id:taxa_program:20191007104505p:plain:w500
Tokenの取得

ちなみにLambdaからは下記のようにTokenを設定してリクエストを投げます。

defsample(event, context):
    
    attachments = [{
        'text': 'dry-runの結果はいかがでしょうか? \n問題なければ「OK」ボタンを押下してDDL実行してください。',
        'callback_id': callback_id,
        'attachment_type': 'default',
        'actions': [{
            'name': 'done_yes',
            'text': 'OK',
            'type': 'button',
            "confirm": {
                "title": "Are you sure?",
                "text": "DDLを実行してもよろしいでしょうか?",
                "ok_text": "Yes",
                "dismiss_text": "No"
            }
        },
        {
            'name': 'done_no',
            'text': 'Cancel',
            'type': 'button',
            "style":"danger",
            "confirm": {
                "title": "Are you sure?",
                "text": "DDLの実行をキャンセルしてもよろしいでしょうか?",
                "ok_text": "Yes",
                "dismiss_text": "No"
            }
            
        }]
    }]

    payload = {
        'token':※ここにTokenを設定※,
        'channel': SLACK_CHANNEL,
        'username': username,
        'icon_emoji': icon_emoji,
        'attachments': json.dumps(attachments)
    }

    res = requests.post(post_url, data=payload)
    return res

最後に、然るべきPermissionを設定し、対象のチャンネルに今回作成したAppをintegrationとして追加すれば終了です!

まとめ

今回はDDL自動実行フローの構築ということで、DevOps的な取り組みについてご紹介しました。

このフローに関しては、構築に少し手間がかかりますが、一度構築してしまえばそれ以降半永久的に恩恵を受けることができます。
もし同じような悩み・課題を感じている方がいれば、一度試してみてはいかがでしょうか。

CakePHP3用のMaster/Replica接続管理プラグインをOSS化しました

$
0
0

こんにちは、サーバーサイドやっております 金城 (@o0h_)です。
なんとな〜〜〜くKindleのライブラリを見ていたら、スキエンティアがあって「とても美しくて良い話だなぁ。。。」と思った次第です。

スキエンティア (ビッグコミックススペシャル)

スキエンティア (ビッグコミックススペシャル)

たまに読み返したいな。

さて、掲題のとおりですが、以前にママリのマスプロモーションを実施した際の負荷対策として作成した機構をプラグインとして公開しました。
・・・という書き出しで以前に書いたのが、CakePHP2.x用のMaster/Replica接続管理プラグインです。

tech.connehito.com

それから暫く経ちましたが、この度CakePHP3用の同様のプラグインを公開しました。

packagist.org

この記事では、以下の3点について紹介したいと思います。

  1. プラグインの利用方法についての簡単な説明
  2. 設計について
  3. 「CakePHP3.xのプラグイン」を公開する際に工夫したこと

①利用方法について

CakePHP2用のプラグインと同様に、「複数の接続を管理する」「単一のモデルからそれらを任意に使い分けられる」ことを目的としています。例えば「参照しか走らないリクエストは参照用DBに接続して、更新系はマスターDBに接続する」といったような使い方を想定しています。

簡単に利用方法を紹介します。

  1. Pluginをcomposer installで導入する
  2. 「複数の接続先」をconfig(デフォルトではconfig/app.php)に書き込む
  3. (Controller等で)必要に応じて接続先情報を変更するメソッドを実行する

1. Pluginの配置

Composerによるinstallに対応しています。

composer require connehito/cakephp-master-replica

単体クラスの名前空間解決による読み込みだけで十分なので、Pluginのロード等の処理は必要としません。

2. 接続情報の設定

ConnectionManagerに喰わせる設定を変更します。
標準的な構成では、これは config/app.phpにある Datasourcesの内容となります。

通常のConnectionクラス用の設定と同様に記述したあとに、「接続先の差分」を rolesに書き込んでください。 共通する接続情報を記述した上で、それぞれの接続先ごとに異なる部分を記述するようになります。

例えば、以下のような3つの接続先を扱いたいという需要があったとします*1
(MySQLを使うものとします)

  1. master
    • Host: db-host
    • DB: app_db
    • Username: root
    • Password: password
  2. replica(1)
    • Host: db-host
    • DB: app_db
    • Username: read-only-user
    • Password: another-password
  3. replica(2)
    • Host: replica-host
    • DB: app_db
    • Username: read-only-user
    • Password: another-password

これに「master接続だけ利用する」場合は、CakePHP標準のConnectionを利用して以下のように記述できるかと思います。

<?php// config/app.phpuse Cake\Database\Connection;
use Cake\Database\Driver\Mysql;

return['Datasources'=>['default'=>['className'=> Connection::class,
                'driver'=> Mysql::class,
                'persistent'=>false,
                'host'=>'db-host',
                'username'=>'root',
                'password'=>'password',
                'database'=>'app_db',
                'timezone'=>'UTC',
                'flags'=>[],
                'cacheMetadata'=>true,
                'log'=>false,
                'quoteIdentifiers'=>false,
        ],
    ],
];

これを、master + replica + replica2という構成に対応できるように書き換えてみます

<?php// config/app.phpuse Cake\Database\Driver\Mysql;
use Connehito\CakephpMasterReplica\Database\Connection\MasterReplicaConnection;

return['Datasources'=>['default'=>['className'=> MasterReplicaConnection::class,
                'driver'=> Mysql::class,
                'persistent'=>false,
                'host'=>'db-host',
                'database'=>'app_db',
                'timezone'=>'UTC',
                'flags'=>[],
                'cacheMetadata'=>true,
                'log'=>false,
                'quoteIdentifiers'=>false,
                'roles'=>['master'=>['username'=>'root',
                        'password'=>'password',
                    ],
                    'replica'=>['username'=>'read-only-user',
                        'password'=>'another-password',
                    ],
                    'replica2'=>['host'=>'replica-host',
                        'username'=>'read-only-user',
                        'password'=>'another-password',
                    ],
                ],
        ],
    ],
];

このように、classNameをMasterReplicaConnectionに書き換えた上で「差分だけrolesに書く」ことによって全ての接続を扱えるようになります。

3. 接続の切り替え

デフォルトでは、 masterという名前の設定を用いて接続されます。
switchRole()というAPIを用いて簡単に接続先を切り替えることが可能です。

例えばTableインスタンスが手元にある場合は、Tableインスタンスを介してアクセスするのが手軽だと思います。

<?php$this->UsersTable->getConnection()->switchRole('replica');

それ以外の場合は、ConnectionManagerを利用することになるでしょうか。

<?phpuse \Cake\Datasource\ConnectionManager;

ConnectionManager::get('default')->switchRole('replica2');

比較的容易にアクセスができるので、リトライ機構の導入や局所的にアクセス先を変更したいというニーズにも対応が簡単です。

4. 発展的な利用例: CQRS的なもの

多くのWebアプリケーションで、「書き込み・更新を伴うページやエンドポイント」「参照しか用いないもの」というのが比較的はっきりと分かれるのではないでしょうか。
エンドポイント単位をいわゆるCommand/Query的に分類し、それぞれで接続先を設定できると実用できる場面が広がるように思います。

社内のプロダクトでは、Routingの設定と絡めて「どっちにつなげるか」を管理できるようにしました。 例えば「商品の個別ページ」として、 item/showというエンドポイントがあり、ここではDBの更新が走らないものとします。item/editでは情報の更新を行うため、DBの更新を行います。

まず、 routes.phpでエンドポイント個別の設定として独自のオプションであるreadOnlyフラグを立てます。

<?php//routes.php$routes->scope('item/', ['controller'=>'Items'], function(RouteBuilder $routes){$routes->connect('show',
        ['action'=>'show',
            '_method'=>'GET',
        ]);
    $routes->connect('edit',
        ['action'=>'edit',
            '_method'=>'POST',
            'readOnly'=>false,
        ]);
});

ルーティング情報と一緒に渡ってきた内容に対して、 AppController::beforeFilter()などの「最初の方に通る共通処理」で設定を反映させます。

<?php/** * routesからパラメータを読み取り、接続先のDBを切り替える * * @return void */privatefunction setDefaultDbRole(){$readOnly=$this->getRequest()->getParam('readOnly', true);
    $dbRole= readOnly ?'replica':'master';

    /** @var MasterReplicaConnection $connection */$connection= ConnectionManager::get('default');
    $connection->switchRole($dbRole);
}

これだけで、「明示的にreadOnlyフラグを折ったエンドポイント以外はレプリカを見る」機能が実現されました。

②設計について

ここからは、実装面の話を紹介させていただきます。

CakePHP3の「ORM」「DB接続」

CakePHP2と比べて3.xは「ORM周りの機能が大幅に強化・変更された」というのは主要なトピックの1つですが、ORM/Database周りに関する内部構造も複雑になっています。

主要な登場人物として「ORM」「Datasource」「Database」の3レイヤーが出てきます。
ざっとまとめると以下のようになります。

f:id:o0h:20191014023707p:plainf:id:o0h:20191014023609p:plain

通常のアプリケーション開発を進めている時に直接触るのはORM層のみで、ほぼ事足りると思います。
この記事では詳細については割愛します*2が、「Connection」と「Driver」に着目してください

Driverクラスは、ClassDocを見ると以下のようなサマリーがついています。

/**
 * Represents a database driver containing all specificities for
 * a database engine including its SQL dialect.
 */

(cakeの中では)これが最もDBに近い層で、PDOインスタンスを保持します。
Connectionクラスはこれらを使役するクラスで、Driverを生成・取得し保持します。
今回作成したかったのは「複数の接続先を管理する」機能なので、「接続を切り替える」場としてConnectionクラスを改変することにしました。

接続の生成と管理・切り替え

PHP上で実際に「DBに接続している」のは「PDOインスタンスを生成(保持)している」箇所になります。
当プラグイン = Connectionクラスでは、「クラスのインスタンス化時に、注入されている全ての設定に応じた PDOインスタンス(との仲介役であるDriverインスタンス)を生成し、保持する」という戦術を取りました。

CakePHP2用プラグインでは、Datasource\Databaseレイヤーへのハックを行いMysqlを前提としていました。そのためにPDOとも密結合になっています。
これに対してCakePHP3用のプラグインでは、接続管理が抽象化されたことで、過度な設計を必要とせずに接続用のDBドライバは付け替え可能です。その点に注目しました。

デメリットとしては、例えば「replica接続しか使わないのにmaster接続のインスタンスも生成されてしまう」といったオーバーヘッドがあります。
実際、当初は「呼び出された時点でPDOインスタンスを作り、不要になったら破棄する」という方法を実現できるか?と模索もしました。
生成自体は遅延読み込み的に生成すれば実現できそうな気はします。問題は「破棄する」タイミングです。トランザクション管理など、どうしても「DriverないしConnectionから向き合わなければいけない関心事が増える」ことで、実装上の複雑さが増しそうな懸念がありました。また、「呼び出されるたびに再接続する」ことで生じるDB接続確立は大きな負担になりそうです。

総合して、「接続するのは最初に済ませてしまう」「インスタンスを内部に保持し続ける」ことで得られる、システムリソース的にもアプリケーションコード的にも魅力を感じました。 また、これらの機能をたった70行ちょっとの単一クラスで実現できたというのは「簡潔な記述ができた」成果だとも言えるのではないでしょうか。

③「CakePHP3.xのプラグイン」を公開する際に工夫したこと

当プラグインは、元々プロダクトコードの一部として実装していたものをライブラリとして切り出したものです。
実際のプロダクトなら「コントローラーもデータベースもすべてが揃っている!」テスト環境があるのですが、スタンドアロンなライブラリでは、そうも行きません。環境構築を含む事前準備は本質的ではないストレスになると考えています。もしテストがすぐに実行できれば、どれだけ開発体験が良くなるか・・・・

この問題は個人的には毎回頭を抱える部分なので、自分なりに「こうしたら楽かな?」と思える工夫をいくつか施してみました。
なお、今もなお試行錯誤している部分なので、ぜひ皆さんのご意見も聞いてみたいです。

docker-composeの梱包

このプラグインは理屈上はDBを使わなくても実装したロジックの内容を検査が可能だと思います。しかしながら、DB周りの機能を提供するものでもあるため、テストの時点で「実際にDBに触ってみる」事ができると安心です。
そこで、docker-composeを用いてPHP+MySQLの開発土台を配布できるようにしました。
(sourceはコチラ)

これによって、例えばPhpStormユーザーであれば手軽にIDE上からのテスト実行を提供できることになります🎉

  • stormの設定
    f:id:o0h:20191014032552p:plain
    f:id:o0h:20191014032654p:plain
  • 実際にテストを実行している光景🗻
    f:id:o0h:20191014032937p:plain

(localの)テスト時にローカルからプラグインを読み込む

Composer配布前提のライブラリなので、最終的にはpackagist経由で喰わせることになります。しかし、開発中はわざわざpushするというのは面倒な話です。
そこで、 docker-composeのvolumesと(テスト実行側アプリの)composer.jsonを組み合わせることで、「テスト実行時にローカルからライブラリを読み込む」ようにしました。

ホストからDockerコンテナにライブラリのsrcを喰わせる

全体構成としては

  • src: ライブラリ本体
  • tests/test_app/composer.json: テスト実行アプリのcomposer.json
  • tests/test_app/docker-compose.yaml: テスト実行アプリのdocker-compose

となります。

docker-compose上では、次のようにして「PJ全体は /appに喰わせる」「それとは別に、 /dist上にライブラリの本体とパッケージ情報(composer.json)を喰わせる」ようにします。

services:test-app:volumes:- ../../src:/dist/src
      - ../../composer.json:/dist/composer.json
      - ../..:/app
ローカルのパスをレポジトリとして設定する

composer.json上には、ローカルのパッケージを参照させるようにレポジトリ情報を追加します

"repositories": [{"type": "path",
          "url": "/dist"
      }],

これで、 app/tests/test_app/vendor/connehito/cakephp-master-replica/distを指すシンボリックになります🎉

CakePHP4を見据えて・・

次期バージョンであるCakePHP4は、すでにβ4まで進んでおり、段々と全貌が見えてきています。
CakePHP3との互換性については意識されているということで、cakephp-master-replicaプラグインにおいても可能であれば低コストに移行したいと考えています。

そこで、4.xから入る規約等への対応を実施しました。
主だったところでは以下の4点です

  1. cakephp-codesnifferを利用してPSR-12対応
  2. 厳密な型チェック(strict_types=1)
  3. 引数・戻り値の型宣言
  4. PHPStanの対応レベル引き上げ(Lv5)

strict_typesの宣言漏れについてはcodesnifferでチェックできるので、そこまでナーバスになる必要もありません(CIでコケるため)。
PHPStanのレベルについては「コーディング規約」とは異なるようにも思いますが、本体の動向に追従しようというものです。
最終的にはstableのリリースを待って移行ガイド他ドキュメントをチェックし対応することになりますが、今の時点での「変更点」としてはこんなものだと思っています。

最後に

ごく僅かなコードで機能を実現できたのは、「フレームワークに乗っかった旨味だ」と感じています!
また、実現したいコードを如何にしてフィットさせるか?という観点でのコードリーディングは、モチベーションも湧きやすく良いものです。今回の機構の開発にあたり、自分なりにCakePHP3のORM,Databaseレイヤーについて理解が深まりました。

PDOインスタンスを複数持たせるというアイディア自体についても、結果的にIlluminateの接続管理でも同様の手法を取っているものです。・・・もっとも、これは実装してから気づいたので、「もっと早く見ればよかった」と項垂れもしました。が、個人的には「悪くないやり方と思っていいのかな〜〜」と、同時に自信も深められたと言えます✨Illuminateの方が高機能な実装をしているようにも思うので、こちらもまだ改善の余地がありそうです。

今回触れた「master replica切り替え機構」の設計や詳細については、もっと詳しい資料が社内にございます🌅
コネヒトではサーバーサイドエンジニアを募集していますので、是非お気軽に遊びに来てくださいね! www.wantedly.com

また、11月に開催されるCakeFestでは弊社CTOも登壇しますので、応援してください!!

*1:実際にこんな構成が使いたいか?は別として、あくまで「こういう事ができるよ」というのを説明するための内容です

*2:手前味噌ですが、以前にこの辺りを調べてみた記事があります。よろしければ御覧ください https://cake.nichiyoubi.land/posts/10-orm-database/


Atomic Designを実践して得た学びと失敗

$
0
0

🙋‍♂️エンジニアの@dachi_023です。約4ヶ月ぶりに記事を書きます、がんばります。

この記事について

コンポーネントやAtomic Designについて書いています。ここではUIデザインのフローに関するAtomic Designの実践ではなく、開発(実装)のフローにはめ込んだ場合にどうすべきなのか、というお話をしています。

コンポーネントとAtomic Design

ReactやVueをはじめとするライブラリのお陰でフロントエンド開発に「コンポーネント」という考え方が浸透した今日この頃ですが、そんなコンポーネントの設計についての話なんかをしているとよく現れるのが今回の主題に挙げている「Atomic Design」です。Atomic Designはデザインシステムを効率よく作成するための手段のひとつですが、その中に登場するコンポーネントを5階層(Atoms, Molecules, Organisms, Templates, Pages)に分類するという手法がコンポーネント指向なライブラリと相性が良く、設計や実装に取り込むことで今までよりもエンジニアとデザイナーの認識も揃えやすそうだよね〜、といった理由などにより次第にメジャーな手法となっていきました。*1

ちなみに、Atomic Designに関する解説などはしないのでよくわからないけど気になるなという方は下記のweb書籍、もしくは「Atomic Design」で検索して出てきた解説記事などを読んでいただくことをオススメします。 atomicdesign.bradfrost.com

直近1年くらいは特に「Atomic Designを参考に設計・実装しました」系の記事やスライドを見る機会も増えてきたのですが、結構ハマりどころとか似てるんじゃないかな?と思ったので溜まった学びを本記事にまとめることで誰かの役に立てれば良いなと思っています。

Atomic Designによって得られた恩恵

「共通言語」を手に入れたことによってコミュニケーションの質が上がりました。例えば、導入前は分類するための指標がないのでコンポーネントの粒度が個人の判断任せになっていて、レビューをする際もお互いに何が正解なのかが分からずで不健全な状態が続いていました。それに比べ導入後は「このUIをAtomsとして1つのコンポーネントに切り出してください」といったコミュニケーションができるようになったのでレビューがしやすくなりました。

実践し始めた当初はエンジニア1名(私だけ)のプロジェクトで開発してたこともあって、UIを型にはめて分類できるようになったことがめちゃくちゃ便利〜!と思っていたんですが、それよりもチームメンバーにエンジニア・デザイナーがいる環境でチーム内で同じ認識を持つための道具としての方が受ける恩恵が大きいなと感じました。コミュニケーション大事。

f:id:dachi023:20191019034838p:plainAtomic Design Methodology | Atomic Design by Brad Frost

ハマりどころと、それをどう解決したか

Atomic Designっていいところがいっぱい!やっていこ!と思えるんですが、開発手法におけるベストプラクティス、みたいな話でもないので開発にそのまま当てはめると痛い目を見ます。というわけで、ハマったポイントが複数あるのでそれぞれ書いていきます。

5階層であることは重要ではない

5階層あるんか!キッチリ分けたろ!となるかもしれませんが、別にいらなければ使わなければいいですし、逆にこういう層が欲しいなぁ〜っていうのがあれば足してもいいと思います。Atomic Designはあくまで「コンポーネントを分解する時にこういう考え方をするといいよ」というアドバイス的なもの(だと私は解釈しています)なので、どういう形で実装されるかは実践するエンジニア・デザイナー次第です。例えば実装時にうちのプロダクトではTemplatesは使わなさそうなので4階層でやります。とかそういう感じです。チームやプロダクトに合わせた構成でやっていくのが1番だと思います。

Atomsから作っていくのが難しい

例えばボタンとかラベルのようなUI的にも分かりやすく「これ以上分解できない」ものは簡単だと思うのですが、作っていかないと気づかないものやその時点では本当に必要なのかが分からないものもあるはずです。そういう時はOrganismsやPagesを作って、それを分解していくのが良いと思います。どこから作るか、にこだわり過ぎずチームとして効率的に開発・デザインできる順番で良さそうです。

MoleculesなのかOrganismsなのか

この問題はよく見かける気がするんですが「Moleculesに求めるもの」「Organismsに求めるもの」が定義できていればいいだけだと思っています。他の階層についても同じで、それぞれどんな条件を満たしている時にその階層になるのか、でやればいいと思います。チーム内で各階層のコンポーネントについての定義ができていて、ちゃんと共通認識となっていれば判断に迷わないはずです。

ちなみに、私は「Organismsはそれ単体で画面上に存在しうるもの」と考えていて、それに満たないもので複数のAtomsによって構成されていいれば全てMoleculesとしています。Atomic Design Methodologyに出てくるUIを例に挙げると、ヘッダーは単体で画面に表示されていても1つのUIとして機能するのでOrganisms、それに比べて検索フォームは検索する対象が(= 何を検索するのかがUIとして明示的で)なければ存在している意味がありません。かつ、フォーム(Atoms)とボタン(Atoms)のセットなのでMolecules、という風に考えています。

f:id:dachi023:20191019054422p:plainf:id:dachi023:20191019054443p:plain

Atomic Design Methodology | Atomic Design by Brad Frost

Organisms以降のコンポーネントがFatになりがち

(ここだけ考え方ではなくて具体的な実装の話をしています)

組み合わせるコンポーネントの数が増えるにしたがってコード量も増えがちです。ディレクトリ構造が下記のようになっているとOrganisms内のみで使用されるコードを逃がす場所がなく、1コンポーネント内に大量のコードが詰め込まれることになりやすいです。

# Before
src
├ atoms
│ ├ Button.jsx
│ └ Input.jsx
├ molecules
│ └ SearchForm.jsx
└ organisms
  └ Header.jsx

なので、下記のようにディレクトリを1つ掘り下げて、Organisms内で閉ざしておきたいが分割した方が可読性が上がりそうなコードを置く場所を作ってあげると実装しやすいです。実際の実装では「分割はできるが再利用性がないもの」を考慮しないと結構辛いシーンが多いのでその辺は柔軟に実装側でカバーできるようにしておくと良いと思います。

#After
src
├ atoms
│ ├ Button.jsx
│ └ Input.jsx
├ molecules
│ └ SearchForm.jsx
└ organisms
  └ Header
    ├ index.jsx
    ├ Logo.jsx
    └ Menu.jsx

エンジニアがAtomic Designから得たもの

「コンポーネントをどう分類するか」に尽きると思います。これをフロントエンドの設計に当てはめた時にめちゃくちゃ使いやすくなるよね、が多くのエンジニアにウケたし、実際やってみて良かったことが多かったからだと思います。ただ、そのままでは使いづらい部分も多いよね、という話でより具体的な実装の話を持ち出したAtomic Componentsというものも出ていたみたい*2です。

所感

設計に関わる部分なので、いろんな方がいろんな意見を持っていそうな気はしていますが、結局はチームとしての開発効率をどこまで上げられるか、だと思うのでAtomic Designの原則に縛られすぎずにチームにフィットするようなやり方を常に模索していきたいですね。

参考リンク

PR

コネヒトではエンジニアを募集しています。フロントエンド周りの設計や実装に興味がある方はもちろん、バックエンド/インフラ/Android/iOSのエンジニアの方も募集していますので興味があったらぜひ話を聞きにきてください、いつでもお待ちしています! www.wantedly.com

*1:日本だとAbamaTVさんのAtomic Design powered by React @ AbemaTVからAtomic Designが流行っていったのかな?と思っています

*2:もともとMediumに記事があったようですが削除されていたので参考リンクの欄に翻訳記事へのURLを貼らせてもらいました

iOSでWebThread関連のクラッシュが急増した件

$
0
0

こんにちは!エンジニアの柳村です。

9月末頃からママリのiOSアプリでWebThread関連のクラッシュが増加し、ときどき爆増するといった事が起こりました。

f:id:yanamura:20191025172637p:plain

クラッシュレポートを調べてみると以下の3つクラッシュが多数発生していました。

Crashed: WebThread
0  JavaScriptCore                 0x1a583e14c WTFCrashWithInfo(int, char const*, char const*, int) + 20
1  JavaScriptCore                 0x1a5d21b3c JSC::Interpreter::prepareForRepeatCall(JSC::FunctionExecutable*, JSC::ExecState*, JSC::ProtoCallFrame*, JSC::JSFunction*, int, JSC::JSScope*, JSC::ArgList const&) + 742
2  JavaScriptCore                 0x1a5f808c0 JSC::boundFunctionConstruct(JSC::ExecState*) + 588
Crashed: WebThread
0  JavaScriptCore                 0x19407aad4 <redacted> + 20
1  JavaScriptCore                 0x1948013dc <redacted> + 746
2  JavaScriptCore                 0x194a80b50 <redacted> + 608
Crashed: WebThread
0  libGPUSupportMercury.dylib     0x1d8145fe4 gpus_ReturnNotPermittedKillClient
1  AGXGLDriver                    0x1dc7f2ed8 (シンボルが不足しています)
2  libGPUSupportMercury.dylib     0x1d8146fac gpusSubmitDataBuffers
3  AGXGLDriver                    0x1dc7f4404 (シンボルが不足しています)
4  IOAccelerator                  0x1be209884 IOAccelContextFinishResourceSysMem + 64

見ての通り、どれもなるほどわからんというエラーですね・・・

しかも、アプリやiOSのアップデートとは関係なく急増するという不思議な状況でした。

原因の分析

ママリのiOSアプリではいくつかWebViewを使っている箇所があり、どこのWebViewが原因かをまず洗い出す必要がありました。

ママリのiOSアプリではFirebase Crashlyticsを利用しており、Firebase Crashlyticsだとクラッシュレポートとログが一緒に見れるのでどの画面でクラッシュしたか判断するのに役に立ちました。

調べた結果は起動直後にクラッシュしているユーザーがいたのでトップページのどこかであるということに絞れました。

しかし、トップページにWebViewは使った心当たりはなかったので、XcodeのDebug View Hierarchyを使ってどこでWebViewが使われているか調査しました。

その結果Google Mobile AdsのDFPBannerView内でUIWebViewが使われていることがわかりました。

f:id:yanamura:20191025175922p:plain

対策

まずはSDKが古かったので最新(7.50.0)にしてみましたが効果はありませんでした。。

そこでGoogle Mobile Adsのフォーラムを見たところUIWebViewをWKWebViewに変えることができるとの情報が得られたのでやってみました。

以下のようにinfo.plistにgad_preferred_webviewというKeyとwkwebviewというValueを設定するとWKWebViewに変えることができました。

<key>gad_preferred_webview</key>
<string>wkwebview</string>

これをリリースしたところ、多数発生していたクラッシュがWKWebViewにしたバージョンでは発生しなくなりました!

まとめ

急にWebThread関連のエラーが増えて、Google Mobile Adsを使っている場合はinfo.plistを変更してWKWebViewに変えるとよいです。

UIWebViewはiOS13ではdeprecatedになっていますし、iOS13でUIWebViewでwindow.confirmを使うと挙動がおかしかったりするということも弊社で確認しているので、UIWebViewは捨て去ったほうがよいかと思います。

Connehito Marché vol.6 〜機械学習・データ分析市〜 を開催しました!

$
0
0

f:id:taxa_program:20191108125429p:plain

こんにちは。MLエンジニアの野澤(@takapy0210)です!

11月に入っていよいよ寒くなってきましたね。
寒いといえば、毎朝洗濯物を干すのが辛くなる季節でもあります。
このような季節も影響し、我が家ではドラム式洗濯機のデプロイが検討されています。もしオススメのドラム式洗濯機があれば教えてください!!

さて今回は、先日無事に開催することができました、「Connehito Marché vol.6 〜機械学習・データ分析市〜」の様子や、LTの内容などを簡単にご紹介できればと 思います!
(嬉しいことにLT枠もオーディエンス枠も満席となり、大盛況で終えることができました!)

connehito.connpass.com

今回のテーマ

今回は第6回目ということで「機械学習・データ分析」をテーマとして開催しました。

抽象的なテーマだったため、LT内容含めてどのような方々が参加してくださるのか、非常に楽しみでした!

ちなみにコネヒトマルシェでは毎回テーマを変えており、過去には下記のようなテーマで開催しています。

今回は、下記3つのお願いお伝えした上で、スタートさせていただきました!

f:id:taxa_program:20191108130206p:plain

LT内容

データ分析コンペにおいて特徴量管理に疲弊している全人類に伝えたい想い

by @takapy0210

概要

  • データ分析コンペをnotebookだけで挑むといろんなツラミがある
  • 特徴量管理とパイプライン組むとちょっと良くなった
    (初っ端から時間オーバーしてすみませんでした。。。)

SageMakerで構築する価格推定システム

by @0xb5951さん

www.slideshare.net

概要

  • 機械学習でなんかやってみてよと言われてやった
  • 依頼がいくらで成約するかを推定
  • SageMakerを用いて手早く実装した
  • 今日リリース予定だったが、リリースできず

社内での円滑なデータ分析のために

by @yu__ya4さん

概要

  • データ分析業務のポジティブな社内政治のお話
  • 直接関係のないPJなどにも顔をだしたり、ランチに行ったりコミュニケーション取るのがめっちゃ大事(何やってるかわからない人にならないためにも)
  • 結果として社内外でのプレゼンスが向上しいろんなことが円滑に進んだ

初めて機械学習PJをやってみて得た知見

by @yaginuuunさん

概要

  • 自社サービスにレコメンドエンジンを入れた
  • 簡単でも良いので、まずは結果を見える形にする
  • Kaggleは役に立つ

SIGNATEの練習問題コンペで 57位までスコアを上げた話

by @shnagaiさん

概要

  • 機械学習勉強しはじめて初めて自分でモデルを作った
  • あとで分析結果などを見返したい時のために、メモを取るのが大切
  • ドメイン知識をフル活用して、スコアを向上させた(57位/1748)

BigQueryいいよね!って話をしようと思ったらBigQueryより早いAzure Synapseが出た

by @YASU11552288さん

概要

  • BigQuery良いよね、という話をしようと思っていたら、75倍速いAzure Synapseが出たので、急遽内容を変更
  • Azure Synapseはインスタンス単位の課金でインデックスのチューニングが必要
  • 導入を考えるとすこし運用コストが高そう

日本語学習済みモデルについて

by @TwYamatさん

docs.google.com

概要

  • 今はBERT が微笑む時代
  • 学習済み言語モデルのGood / Badポイント
  • 日本語学習済みモデルを使用すれば、様々なNLPタスクに取り組める一方で、日本語の学習済みモデルが少ないので、適応するには制限がありそう

Meta Kaggleを覗いてみた

by @IshizakiYukoさん

概要

  • Kaggleって本当に流行っているか、Meta Kaggleを覗いて調べてみた
  • 新規ユーザーは右肩上がりの一方、コンペにSubmitしているユーザーで分析してみると、鈍化している
  • 最近は画像コンペが増えている
  • kaggleの沼にはまろう

NGBoost論文読んでみた

by @ml_taroさん

概要

  • kagglerにも人気かつ、つよつよAndrewNg先生が共著者だったので、読んでみた
  • NGBoostは出力の不確実性を確率値として出力する
  • 自然勾配(勾配が大きく変化する場所は慎重に&勾配があまり変化しない場所は大胆に)を用いることで、最適化を効率的にしている

NLP Beginner BERTを試す

by @ktr_wtbさん

概要

  • BERTでkaggleの過去コンペを解いてみた
  • BERTなら特になにもしなくてもそこそこ良いスコアが出るかと思いきや、そんなことはなかった
  • Fine Tuningのやり方を工夫することが大事

SQLベースのMLパイプライン

by @hatuninaさん

概要

  • データセットの作成と特徴量エンジニアリングをSQLで
  • SQLをベースにすることで、使い回しやすい、共有しやすいなどのメリットがある
  • 一方で、DBが混んでいるとデータ作成がボトルネックになりがち

競艇の順位予想をしてみた

by @wakame1367さん

docs.google.com

概要

  • 競艇のデータセットは公開されているが、表データっぽいtxtデータで、データクレンジングに8割の労力を割いた
  • LightGBMでランク学習させた
  • ドメイン知識が少なく、特徴量エンジニアリングがあまりできなかった。
  • 実際に予測してみたところ、勝率は。。。

懇親会

今回はお寿司とお酒を手に乾杯しました。

参加者の方々で質問しあったり、LTの感想を伝えたりしていて、終始楽しそうな雰囲気で幕を閉じることができました!
個人的には、Twitter上で知っている人と顔を合わせてお話できたのがとても嬉しかったです!

f:id:taxa_program:20191108115122j:plainf:id:taxa_program:20191108114935p:plain

最後に

というわけで、当日の様子をお届けしました!

拙い司会・進行でしたが、最後までお付き合い頂きありがとうございました!
私自身もとても楽しく参加させていただきました!

次回開催時期・テーマなどはまだ決まっておりませんが、今回のマルシェの振り返りを社内で実施したときに「今回盛況だったから、次回も機械学習でいこうよ!(いこう)」という話がチラっとあがっていたので、もしかしたらもしかするかもしれません(笑)

参加していただいた皆さま、ありがとうございました!
また次回のマルシェでお会いできたら嬉しいです!

CakePHPの国際カンファレンス「CakeFest 2019」に協賛&参加しました!(資料まとめ)

$
0
0

こんにちは!サーバーサイドエンジニアの @fortkleです!
今回は、先週末に開催されたCakePHPの国際カンファレンスである「CakeFest 2019」に参加してきたのでレポートしたいと思います。

CakeFest 2019

CakeFestはPHPのフレームワークであるCakePHPの国際イベントで、セミナー2日、カンファレンス2日の計4日間に渡って開催されるイベントです。 開催地は事前に投票によって決められており、今回は初めての日本開催となりました。 私は後半のカンファレンスから参加しました。

cakefest.org

会の雰囲気

f:id:fortkle:20191110162031j:plain
後半2日間のカンファレンス会場はSmartNewsさんのオフィス

公式発表はないのであくまで推測になりますが、全体でいうと100名弱ほど、そのうち半分ほどが海外からの参加者という風に非常に国際色豊かなイベントでした。
このような形式のカンファレンスに参加したのは初めてだったのですが、堅苦しい感じとは真逆でアットホームな雰囲気の温かいカンファレンスでした。

f:id:fortkle:20191109172102j:plainf:id:fortkle:20191109171934j:plain
会場で提供されたドーナツと海外を感じるバナナの箱置き!

今回、微力ながら協賛もさせていただきました。
コネヒトのプロダクトはCakePHPに支えられているものばかりなので、少しでもCakePHPコミュニティの発展を支援できたのであれば幸いです!

当日のセッション

カンファレンス1日目の夜に行われたLTを除くセッションについて、すでに公開されている資料をまとめてみたので参考にしてみてください! ※ 敬称略

特に、José RodríguezさんによるCakePHPのまだあまり知られて機能の発表金澤さんによる滑らかなCakePHP3への移行についての発表などは知らないことも多くまさに"知見"という印象だったのでまた見返したいと思います。もちろん、弊社CTOの伊藤によるCakePHPではじめるOSSの発表もぜひご覧ください!(宣伝)

f:id:fortkle:20191110163556j:plain
弊社CTO伊藤も発表しました(タイトル: Let's start your first OSS with CakePHP )

Day1

※ 一部の資料はCakePHPのSlackチャンネルでのみ共有されていたため当該Slackチャンネルの投稿のリンクを記載します。*1

タイトル/スピーカー 資料公開先
A safer and more helpful CakePHP in 4.0 / Mark Story https://www.slideshare.net/markstory/safer-more-helpful-cakephp
The CakePHP features I wish you were using more / José Rodríguez https://cakesf.slack.com/archives/C172CS4TE/p1573272016104200
Consider a smooth upgrade to CakePHP 3 / Yuki Kanazawa https://speakerdeck.com/ykanazawa/consider-a-smooth-upgrade-to-cakephp-3
12 Factor CakePHP Applications - The Remix / Jose Gonzalez https://speakerdeck.com/josegonzalez/12-factor-php-applications-the-remix-1
Test-driven development to avoid painful of test code / KAZUKI HIGASHIGUCHI https://speakerdeck.com/hgsgtk/test-driven-development-to-avoid-test-painful
CakePHP & Spatial Big Data - Visualising Spatial Data & Metrics over 70 Billion+ rows / Daniel Voyce https://speakerdeck.com/voycey/cakephp-and-spatial-big-data-visualizing-70-billion-rows-of-data
Working with Database Replication / Tadahisa MOTOOKA https://speakerdeck.com/motooka/working-with-database-replications-in-cakephp

Day2

タイトル/スピーカー 資料公開先
Beyond unit testing: How to make your applications more reliable / José Rodríguez https://cakesf.slack.com/archives/C172CS4TE/p1573354079126300
GraphQL, CakePHP & JWT: A Fast & Secure Redemption from REST Hell / Prosper Otemuyiwa 諸事情により発表なし
Baking with Vue.js / David Yell https://docs.google.com/presentation/d/1bgilFVNRtvhp9KyCLVWMMKtlS-Q39rx07AXCOpf67G4/edit#slide=id.p
CakePHP with Habitat - Build once, deploy everywhere / Graham Weldon 諸事情により発表なし
Building interactivity with websockets / Wim Godden https://www.slideshare.net/wimg/building-interactivity-with-websockets-191944043
Life after CakePHP / Andrej Griniuk https://docs.google.com/presentation/d/1Hlrs_T-rZ_7LNy6iQh-S_Ff8Ca6O8HlThYfjg6D8GO4/edit
Let's start your first OSS with CakePHP / Sho Ito https://speakerdeck.com/itosho525/lets-start-your-first-oss-with-cakephp

最後に

今回のCakeFestは、Github上でしかやりとりをしたことがなかったCakePHPのコアコミッターの皆さんと直接コミュニケーションが取れたことがとても貴重な機会でした。
運営の皆様、会場提供してくださったDMM.com様・スマートニュース様、そして当日参加された皆様、本当にありがとうございました。そして、お疲れ様でした!

*1:CakePHPのSlackチャンネルは誰でも自由に入れますし、日本語話者向けのチャンネル#japaneseもあります! 参加方法はこちら https://twitter.com/fortkle/status/1193390451883040768

データ分析コンペで役に立つ特徴量管理方法と学習・推論パイプライン【コネヒトマルシェLT書き起こし】

$
0
0

こんにちは!MLエンジニアの野澤(@takapy0210)です!

気づけば2019年の営業日も残り20日強ですね。年始に立てた個人的な目標が1/5しか達成できていないことに先日気付いたので、残りの期間で1つくらいは達成できると良いですね、という他人行儀な振る舞いをしたくなっている今日この頃です。

さて今回は、11月5日に開催した(コネヒトマルシェ)でLTした内容の全文書き起こしです。参考資料とあわせてご紹介できればと思います。
全文書き起こしは初の試みなので「ふ〜ん。なるほど〜」ぐらいのお気持ちで見ていただければと思います。

発表資料はこちらです。


f:id:connehito:20191113190809p:plain:w500
f:id:connehito:20191113190823p:plain:w500
f:id:connehito:20191113190902p:plain:w500

Kaggleとは

Kaggleと書いて「カグル」と読みます。日本でも最近は定着してきましたが、Kaggleに参加している方を「カグラー(Kaggler)」とも呼びます。 「The Home of Data Science & Machine Learning」(データサイエンスと機械学習の家)と題されている通り、世界中の機械学習・データサイエンスに携わっている約40万人の方が集まるコミュニティです。最大の目玉とも言えるのは「Competetion(コンペ)」です。
https://www.kaggle.com/

SIGNATEとは

日本版Kaggleというのが一番わかりやすく、特徴としては、開催されるコンペティションのデータは日本の企業から提供されています。コンペティションで高順位をとると、後日表彰式および報告会という形で呼ばれることがあり、入賞すると、賞金が出ます。
https://signate.jp/

f:id:connehito:20191113191145p:plain:w500

(会場の9割強の人が挙手)

参加したことある方に聞いてみたいのですが、特徴量の管理ってどうされてますか?

f:id:connehito:20191113191208p:plain:w500

最初に、僕の実体験を添えてありがちなパターンを2つほどご紹介できればと思います。

例えば、[A, B, C, D]という特徴量があった時に、これらから[E, F, G, H, ...]という形で特徴量エンジニアリングする、というシチュエーションはよくあると思います。

f:id:connehito:20191114102914p:plain:w500

で、一通り特徴量を生成し終えた後に、実験したい特徴量を指定して学習データを作り、学習させます。

f:id:connehito:20191114102942p:plain:w500

一通り学習を終えたところで、ふとこんな事を思うタイミングがありました。

f:id:connehito:20191114103004p:plain:w500

どのような計算で生成した特徴量か探してみると、これが結構大変だったりします。

f:id:connehito:20191114103051p:plain:w500

特徴量生成場所が見つかっても、他の特徴量から段階的に生成されていたりする場合、この根源を探すのも大変です。(もちろん、特徴量の名前を一目で分かることにしておくことは前提としてとても大切だと思います。)

f:id:connehito:20191114103117p:plain:w500

続いて、2つ目のパターンをご紹介します。

f:id:connehito:20191114103206p:plain:w500

これは結構ありがちだと思います(笑)

f:id:connehito:20191114103231p:plain:w500

で、意気揚々とDuplicateしてnotebookの中身を見てみるとこんな感じになっているんですね。

f:id:connehito:20191114103247p:plain:w500

。。。

f:id:connehito:20191114103342p:plain:w500

お気付きの方もいると思うのですが、特徴量生成処理など、同じ計算を再度行う必要がでてきます。これは本当に無駄だと思っていて、どうにかしないとな〜と思っていました。

f:id:connehito:20191114103717p:plain:w500

また、Duplicateを繰り返していくと、気づいたらnotebookファイルだらけになっていた、なんてこともあるかと思います。

f:id:connehito:20191114103811p:plain:w500

最初は「めっちゃ良いモデルが作れた!」と歓喜していましたが、煩雑なnotebook、特徴量管理により、コンペのモチベーションも低下してしまう、なんてことにもなりかねません。

f:id:connehito:20191114104304p:plain:w500

今回は、上記で述べたような実体験から感じていた課題感を、玄人の事例を参考に少しづつ解消できてきたので、みなさんにも少しおすそ分けできたらと思っています。
題して
「データ分析コンペにおいて特徴量管理に疲弊している全人類に伝えたい想い〜学習・推論パイプラインを添えて〜」
という壮大なタイトル(笑)でお話できればと思っています。

f:id:connehito:20191114104414p:plain:w500

こちらがアジェンダです。

f:id:connehito:20191114104442p:plain:w500

いろいろ書いていますが、「玄人の知恵をお借りして、特徴量管理と学習・推論パイプライン構築に取り組んだ結果、めっちゃよかったよ」という話をします。 あくまで主観になりますので、「こんな方法で取り組んだらよかった!」などありましたら是非教えてください。

f:id:connehito:20191114104459p:plain:w500

まずは簡単に自己紹介させてください。

野澤哲照と言いまして、コネヒト株式会社で機械学習エンジニアとして働いています。
会社などでは「たかぱい」と呼ばれています。
Kaggleしたり、野球したり、ラーメン食べたりするのが好きです。

f:id:connehito:20191114104712p:plain:w500

次に特徴量管理方法についてお話します。

これから発表する特徴量管理については、下記記事を参考にさせていただきました。
参考記事:Kaggleで使えるFeather形式を利用した特徴量管理法 - 天色グラフィティ

f:id:connehito:20191114104737p:plain:w500

まずは「特徴量を列ごとに管理する」「メモファイルを作成する」という部分のイメージを共有できればと思います。

f:id:connehito:20191114104811p:plain:w500

「特徴量を列ごとに管理する」とは、下記のように1つの特徴量をtrainデータ, testデータそれぞれ1ファイルずつで管理することをイメージしてください。

f:id:connehito:20191114104922p:plain:w500

「メモファイルを作成する」とは、上記の特徴量を生成する際に自動的に「この特徴量はこうやって生成したもの」というメモファイルを生成することです。

f:id:connehito:20191114105030p:plain:w500

これだけ見ると結構大変そうに感じる方もいると思いますが、1つのスクリプトファイルを実行するだけで実現できます。

f:id:connehito:20191114105053p:plain:w500
f:id:connehito:20191114105112p:plain:w500

以下で具体的な方法についてお伝えできればと思います。

例えばhoge.pyという特徴量生成用のスクリプトを下記のように用意しておきます。
これを実行すると、「各特徴量」と「特徴量メモファイル」が生成されます。

f:id:connehito:20191114105222p:plain:w500
f:id:connehito:20191114105242p:plain:w500
f:id:connehito:20191114105300p:plain:w500

特徴量のメモファイルを作成する箇所に関しては、難しいことをやっている訳ではなく、生成した特徴量の記述がファイルになければ追記していく、ということをやっています。

f:id:connehito:20191114105505p:plain:w500

この特徴量メモはCSV形式で保存しておくとGithubから参照しやすかったりします。
ExcelやNumbersといったアプリケーションからでも綺麗に見えるので、今回はCSVファイルを採用しました。

f:id:connehito:20191114105555p:plain:w500

新しい特徴量を生成したい場合は、hoge.pyにその特徴量生成処理を新しく記述します。

f:id:connehito:20191114105642p:plain:w500

hoge.pyを実行すると新しい特徴量が生成されますが、この時すでに生成されている特徴量の計算はskipしてくれるので、余計な計算時間がかかることはありません。(もちろん、再計算することも可能です)

f:id:connehito:20191114111404p:plain:w500

特徴量をdataframeに読み込む場合は、読み込みたい特徴量名のリストを生成しておき、下記のように記述すれば指定した特徴量データのみを読み込むことが可能です。

f:id:connehito:20191114111439p:plain:w500

この特徴量管理方法を使って何が嬉しかったかと言うと

f:id:connehito:20191114111510p:plain:w500

特徴量管理をすることで下記のようなメリットを享受することができ、「時間的なコスト」を大幅に削減できたのが個人的にはとても嬉しかったです。
データ分析コンペでは特徴量生成だけではなく、学習、推論にも一定の時間がかかります。
そのような中で、特徴量を管理することで余計な計算時間が減るだけでなく、学習→推論のPDCAも回しやすくなったと感じています。

f:id:connehito:20191114111536p:plain:w500

次に、学習・推論パイプラインについてお伝えします。

こちらに関しては、昨今話題の下記書籍を参考にさせていただきました。
参考文献:Kaggleで勝つデータ分析の技術:書籍案内|技術評論社

f:id:connehito:20191114111610p:plain:w500

書籍で紹介されているパイプラインを土台に、下記run_nameをprefixとすることで、一貫性のあるファイルやモデル管理を、意識しなくてもできるように工夫しました。

f:id:connehito:20191114111758p:plain:w500
f:id:connehito:20191114111824p:plain:w500

生成されるファイルは下記のようなものになります。モデルや推論結果のファイルは皆さんの想像通りのものなので、それ以外のファイルについて少しご紹介します。

f:id:connehito:20191114112028p:plain:w500

features.txtは今回の学習に使用した特徴量が記載されたファイルです。

f:id:connehito:20191114112059p:plain:w500

また、param.txtは今回の学習に使用したハイパーパラメータが記載されたファイルです。

f:id:connehito:20191114112125p:plain:w500

shap.pngはshapで計算された可視化イメージを保存したものです。これを元に次の学習の勘所を見つけていきます。

f:id:connehito:20191114112306p:plain:w500

logファイルについては、処理過程を保存したもの(general.log)と、モデルのスコアだけを保存したもの(result.log)の2種類あります。

f:id:connehito:20191114112346p:plain:w500

この学習・推論パイプラインを構築して何が嬉しかったかというと

f:id:connehito:20191114112410p:plain:w500

「この特徴量」と「このパラメータ」を使って学習させたモデルに関して、「各タスクに要した時間」と「各foldと最終的なスコア」を意識しなくても管理できるようになったことです。
これにより、モデルの再現性はもちろん、どの特徴量を使うとスコアが上がった or 下がったということも自然と管理できるようになります。
また、shapの計算結果などを出力しておくことで、次の学習時の勘所も掴むことができます。

f:id:connehito:20191114112431p:plain:w500

最後にまとめです。

特徴量管理とパイプラインを構築することで、様々な「いいぞ!」を感じることができました。一定のイニシャルコストはかかりますが、一度構築してしまえば流用できるので、興味のある方は試してみてください!

f:id:connehito:20191114112521p:plain:w500

また、「他にこんな良い方法もあるよ!」といった知見・意見あれば、是非教えていただけると嬉しいです!

ご清聴ありがとうございました!

f:id:connehito:20191114112552p:plain:w500

発表資料全体をご覧になりたい方はこちらをご覧ください。

以上、当日の書き起こしでした。

今後、コネヒトのMLチームとしては推薦システムに取り組んでいく予定です。
取り組みから得た知見など、積極的に発信していきたいと思っていますので、楽しみにしていてください!


よろしければ、今までのキャリア、コネヒトでの業務などをまとめておりますのでこちらもご覧いただけたら嬉しいです。 www.wantedly.com

Viewing all 377 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>