ぺい

渋谷系アドテクエンジニアの落書き

テストコードが生む生産性について

テストコード書いてますか?!!!

最近になって、テストコードをちゃんと書くようになったぺいです。
今まで、どうしても以下のような理由でなかなか書けていませんでした。

  • どうやって書けばいいのか分からん。
  • 正直めんどい。

何故そんな私が、テストコード書くようになったのかについて、実経験を元にまとめてみました。

テスト書かないと起きる問題

自分はまだ学生ですが、アルバイトなどでプロダクトコードを書く機会があります。そこでテストを書いていないことによって起きた問題がありました。
それは、大きな変更が怖くて出来ない事と、動作チェックを手作業で行うので、チェック漏れはもちろん時間もかかるという2点です。

大きな変更が怖くて出来ない

例えば、様々な箇所から参照されているプロパティやメソッドの変更があったとします。その変更をすることは容易ですが、その変更から生じる影響範囲が広ければ広いほど、絶対に大丈夫という担保が難しくなります。そのため、問題っちゃ問題だけど、思い切って直すまでもないなというものは放置してしまう。
しかし、これが絶対に必要な変更だった場合が最悪です。かなり危険な上に、精神衛生上よろしくない開発がスタートします。もし、テストをちゃんと書いていれば、変更によって生じる問題が分かるので、変更に対して億劫にならずに済み、改善もスムーズに出来るので、最終的にサービスの寿命も生産性も上がると感じました。

手作業チェックはテストコード作成より時間がかかる

テストコードを書かない場合、コードの安全性は手作業によって行われます。なので、当然のように抜けもあります。しかも、チェックがその時の一回しか行われないので、後からの仕様変更でバグが発生しても、気がつかないことがほとんどです。結局そのバグのせいで余計な業務が発生して、テスト書く時間以上の時間がかかっていたりして、かなり悲惨な状態になっていたりする。
また、出来上がったコードのチェックがそもそも面倒なので、コードチェックは後回しになることが多く永遠チェックされないまま放置されたりしていることもある。

結論

テストは書かなくてもプロダクトは作れますが、長く運用する程、問題が発生します。結局テストコードを余裕で書ける時間以上の時間を食われるので、無理してでも書いた方が良いと結論づき、書くようになりましたとさ。

github.com 余談になりますが、BDDというテスト手法がわりと自分の中で良いなと感じいます。

Goのgoaを使ったAPIデザインまででイケてるところ

goa goa goaでAPIデザインまでしてみた

GoのWAFであるgoaの最大の特徴であるAPIデザインまでやって、イケてるなと感じた点を挙げてみました。

結論

goaにはDSLというものがあって、それを覚えるのが「最初はだるい」ですが、覚えてしまえば、すぐに追加や修正が出来るのと、最初の時点で設計をしないと開発に入れないので、今まで以上にどういう設計にすべきかを考えれるので、僕のようなすぐに手を動かしちゃう人には良い。

現時点でイケてるなと感じた点

現在、goaは趣味で開発しているアプリのバックグラウンドに使う目的で開発をしています。

セキュリティ周りのロジックが簡単に適用できる

端末の識別に使うtokenなどをHTTPのヘッダーに含めて、それが正しいものをかを毎回チェックしたりするのですが、そのチェックのロジックを全てのエンドポイントで行うわけではなく、必要な時とそうじゃない時の2パターンがありました。
goaでは、そのチェックロジックgあ以下のように簡単に記述することが出来ました。

セキュリティ · goa :: Design-first API Generation
examples/security at master · goadesign/examples · GitHub

ヘッダーチェックロジックの定義(標準で用意されている)

var UserAuth = APIKeySecurity("key", func() {
    Description("ユーザートークン")
    Header("X-Authorization")
})

適用する

var _ = Resource("users", func() {
    BasePath("/users")
    Security(UserAuth) <-----/user以下のエンドポイントでヘッダーのチェックをする

        // このエンドポイントは特殊でチェックしたくない。
    Action("tmp account create", func() {
        Routing(
            POST("/tmp"),
        )
        ~~~~~~
        NoSecurity()  <------/user/tmpではチェックしない
        Response(OK, Token)
        Response(BadRequest, ErrorMedia)
    })

        // ▼チェックされる▼
    Action("account create", func() {
        Routing(
            POST("/new"),
        )
        ~~~~~~
        Response(OK, Token)
        Response(Unauthorized)
        Response(BadRequest, ErrorMedia)
    })
    Action("list", func() {
        Routing(
            POST("/"),
        )
        ~~~~~~
        Response(OK, User)
        Response(Unauthorized)
        Response(BadRequest, ErrorMedia)
    })
        // ▲チェックされる▲
})

まあ、便利!wという感じでしたね。他にもデフォルトでチェックのロジックはgoaで用意されており、ごく普通なアプリを開発するには困らなさそうでした。

パラメーターのバリデーションが行える

地味にかったるいバリデーション。ですが、やらない訳にもいかない。しかし、だるい。
goaはとても簡単にこの問題を解決してくれます。

Param("email", String, "メールアドレス", func() {
    // メールのフォーマットになっているかチェック
    Format(goa.FormatEmail)
})
Param("client_version", String, "アプリのバージョン")
Param("platform", String, "OSとバージョン")
Param("identifier", String, "識別子(android:Android_ID, ios:IDFV)", func() {
    // 正規表現
    Pattern("(^[a-z0-9]{16}$|^[a-z0-9\\-]{32}$)")
})
Required("email", "client_version", "platform", "identifier")

他にも文字数チェックやEnum形式で特定の文字列だけを許可したりなど、いい感じにバリデーションしてくれます。 · goa :: Design-first API Generation

Swagger食わせるだけでAPIドキュメントが出来上がる

実際に作成したDSLをSwaggerのフォーマットに合ったjsonyamlファイルを作成してくれます。これに関してはすごいの一言でしたね。。。
見たほうが早いので、以下に自分の作成したものの実例を貼っておきます。
Build, Collaborate & Integrate APIs | SwaggerHub
作らないといけないけど、正直面倒なドキュメント。goaは強制的にドキュメント作成されるので、共同開発する上で最高です。

ビジネスロジックに集中が出来る

goagenコマンドを実行して、フォルダ移動を多少必要としますが、ビジネスロジックが全てcontrollerパッケージにまとまります。
※controllerとserverフォルダはデフォルトではありませんが、私の好みで作成してます。

.
├── LICENSE
├── Makefile
├── README.md
├── app
├── client
├── controller <----------ビジネスロジックを書く(雛形は自動生成される)
│   ├── events.go
│   ├── genres.go
│   ├── prefs.go
│   └── users.go
├── design <----------APIでざいんを書く
│   ├── api_definition.go
│   ├── media_types.go
│   ├── resources.go
│   └── security.go
├── glide.lock
├── glide.yaml
├── server
│   ├── app.yaml <-------GAEの設定ファイル
│   └── main.go <-------ミドルウェアの定義など(雛形は自動生成される)
├── swagger
│   ├── swagger.json
│   └── swagger.yaml
├── tool
└── vendor

DSLで定義したセキュリティ周りやバリデーションなどは、自動的に生成されているので、組む必要がありません。そのため、コントローラがビジネスロジックだけを組めばいい状態になり、シンプルで目的がハッキリとしたコードにすることが出来ます。これはいつもごちゃごちゃになりがちな自分にとっては嬉しかったです。

goaは面倒なところもあるけど、結構良さそう

他にもイケてる点はありますが、とりあえず、印象的だった点を挙げてみました。これから実装に入るところなので、最終的な生産性に関して未知数ですが、微妙にだるい点などもいくつかあるので、うまく解消する方法などが見つかったら紹介していきたいと思います。

Realm World Tour 17 Mobile DatabaseとRealm Mobile Platformについて

そもそもRealmってどういうもの?からRealm Mobile Platformはどういった経緯で、開発がスタートしたのかが分かりやすかったので、記事にしました。
※聞き起こしに近いので、誤字脱字やニュアンスが違う可能性があります。

realm.io

The Realm Mobile Platform Experience

Realmは様々なプラットフォームに対応しています。
正式版がリリースされてから、多くの企業に使われています。恐らく世にあるアプリの一箇所以上は使われているのではないかと思います。

f:id:tikasan0804:20170326144848j:plain データベースのエンジンはモバイルに最適化するために、フルスクラッチで作成されています。そして、様々な便利な機能が盛り込まれています。
ライブオブジェクトであったり、リアクティブであることが特徴ですが、その中でもデータベースの特徴であるライブオブジェクトを使って、リアルタイム性のあるの良いものを作ろうと考えています。 例えばRealmのライブオブジェクトを使うと、自動的に要素が更新されますので、毎回フェッチする必要がなくなります。それをうまく使うことでスマートにリアルタイム性のあるアプリケーションを作ることが出来ます。

Realmの動作フロー

f:id:tikasan0804:20170326144904j:plain この図は、Realmの重要なフローを表しています。

f:id:tikasan0804:20170326144910j:plain 最初はオブジェクトとのAPIです。
スライドにあるように、まずはRealmのオブジェクトを作成します。使い方は簡単でクラスを作成するだけで使うことが出来ます。特にデータモデルを定義するための中間ファイルなどは必要ありません。またRealmのためのミドルウェアも必要がなく、シンプルに使えることがRealmの最大の特徴です。
このRealmのモデルには、自由にプロパティを設定する。メソッドを定義することが出来ます。またリレーションもプロパティを使うことで関連付けの表現をすることも出来ます。SQLのように高度な記述は必要なく、単純にポインタでの関連付けなので、シンプルにデータを取得することが出来ます。
作成したオブジェクトを使うには、クエリを使って取得する必要があります。クエリを使ってオブジェクトを取得すると、Resultを使って結果を取ってくることが出来ますし、関連しているデータも取得することが出来ます。
関連には逆方向関連も使うことが出来、それはプロパティを使うことで実現することが出来ます。Realmのクエリで自由なことは、クエリのために使ったメモリをコピーせずに使うことが出来るので、メモリ消費がほとんど発生しません。

f:id:tikasan0804:20170326144917j:plain これが実際に構築されたクエリです。見たらわかると思いますが、すごく簡単なことがわかります。この例ではageが2以下のものを検索していますが、もし、このageの内容が変わった場合は、自動的に更新されているので、検索結果を再読込することを気にする必要がありません。また、変更があったことは通知もできるので、アプリ側はそれに従って動けばいいだけなので、簡単に機能を実現することが出来ます。
さらに、Realmはできるだけメモリのコピーをしないようにしているので、例えばこのクエリの場合、全てのデータを取ってるように見えますが、アクセスされるまでメモリを確保されないので、全てのデータを取っているわけではなありません。そのため特別な処置を必要としません。

f:id:tikasan0804:20170326144930j:plain Realmのクエリの結果、または関連のオブジェクトに変更があった場合、通知を用いて画面の再描画をすれば良いです。
その通知の例がこちらのスライドになります。この通知では、何が変更された追加された削除されたという情報が細かく通知されます。これを使うことで部分更新やきれいなアニメーションを使った再描画が可能となっています。例えば、アプリケーションで犬専用のインスタグラムがあったとします。その場合、このスライドのようなクエリを書くことになります。そして、更新があったら通知を使って、データを更新するのですが、追加の通知を乱暴にすることなく、何が通知されたかを適切にユーザーに見せることが出来ます。

f:id:tikasan0804:20170326144937j:plain ネットワークからデータを取ってくるだけではなく、ユーザーによる並べ替えや更新などによって変更がされることもあります。それにはトランザクションを使います。
Realmはトランザクションによってまとめた変更や削除などをひとつの処理として扱うことが出来るので、データの整合性を確保することが出来るので、処理が完了していないデータなどが見えてしまうことがありません。
ここまでの流れがRealmの基本動作になります。Realmは高速でモバイルデータベースを提供しています。

Realm Mobile Platform

f:id:tikasan0804:20170326144943j:plain 今まではモバイルデータベースを提供してきましたが、もうひとつモバイル開発者が考えていることがあると思います。それはRDBMSなどのデータベースとの連携だと思います。その解決のために考案されたのが、Realm Mobile Platformになります。

f:id:tikasan0804:20170326144956j:plain 誰もがアプリ作成のためにデータの同期をサーバとやることがあると思います。その作業をシンプルにしたくても、思わぬトラブルや難しい問題に直面することがあります。
その問題を表現する時に我々はこの氷山を使った説明をします。

この図の上の部分は、データを更新して、UIへ反映してという単純な部分を表していますが、この処理だけでも色々な問題が介在していて、簡単ではありません。
サーバーからデータを取得して、保存が出来たとしても、別のユーザーの変更も考慮しなければいけなく、一筋縄ではいきません。

f:id:tikasan0804:20170326145002j:plain Realmを使ったデータ管理の場合とRestAPIの比較図になります。
従来のやり方では、JSONのパースなどの処理が必要でしたが、Realmの新しいプラットフォームでは、一切そういった操作は必要がありません。サーバーとクライアントで双方向の通信をしていて、それは全て自動で行われています。例えば、データの更新の衝突した場合でも、こちらで用意したルールに従って処理が行われますので、ケアをする必要はありませんし、CPUやメモリの消費も最小限に抑えられています。

f:id:tikasan0804:20170326145009j:plain なので、Realmを使う場合は、赤い枠の部分だけを気にすれば良いと言うことになります。先程までに詳解した自動同期などは全てRealmがカバーしてくれますので、今まで通りローカルのデータベースを使うようにやれば良いだけです。唯一違うことは、バックグラウンドスレッドで更新がされる場合と同じように、他のデバイスの変更の通知を知る術を用意する必要はあります。
本当に簡単になります。データの変換も必要ありませんし、今までは一回の処理のために複数回APIを叩くこともあったかもしれませんが、それが全て任せれるようになります。
ネットワークが繋がっていれば、いつでも使うことが出来ますし、データも差分だけなので最小限に済ませるようにしています。Push Notificationを使ったBackgorund Fetchも少ない時間で終わらせることが出来ます。

f:id:tikasan0804:20170326145017j:plain よくある通信のあるモバイルアプリはサーバーとの通信、JSONとのやり取りがあると思います。それはスパゲッティのように複雑になりがちです。普通にやるとメンテナンスコストがかかると思います。

f:id:tikasan0804:20170326145024j:plain これが実際にRealmで行った時の図になります。このようにものすごくシンプルになっていることが分かると思います。また、一部分だけRealmを使うなども出来るので、シンプルに導入することが出来ます。
サーバー側のコードもRealmを使うときと同じように使うことが出来ます。

f:id:tikasan0804:20170326145031j:plain これはサーバー側でRealmを使った時のコード例になります。いまのところはNodejsを使って構築することになっています。サーバーサイドのRealmでの変更はトランザクションを用いて更新します。

f:id:tikasan0804:20170326145038j:plain このスライドにあるように、同期に加えて他にも多くの機能を提供しています。ユーザーの認証だったり、エンタープライズのみになりますが、スケーリングなどもあります。これについては後でDemoを致します。
Realmはサーバーサイドとクライアントで同期はもちろんですが、双方向のやり取りや継続的なバックアップ機能も提供しています。

f:id:tikasan0804:20170326145050j:plain f:id:tikasan0804:20170326145057j:plain 岸川さんがマリウスさんに、漢字の書き方を教えるDemoの様子。
リアルタイム同期なので、書き方はもちろん書き順も教えることが出来ます。
以下に動画があるので、そちらで参照出来ます。
Realm: リアクティブなモバイルアプリを短期間に

Realm Mobile Platformを使ったソースも公式から公開されています。
Realm Demos · GitHub

f:id:tikasan0804:20170326145106j:plain Realmのウェブサイトから、demoを取得することが出来ますので、ほとんどの機能を試せるようになっています。

DeveloperエディションとPlatformエディションの2つがあり、platformエディションは月々$1,500がかかりますが、2ヶ月のトライアル期間があります。違いとしては、サーバーサイドから変更の通知や同期などが使えるかどうかです。クライアント側での同期だけなら、無料で使えます。
Realm Pricing

f:id:tikasan0804:20170326145114j:plain Realmは人材採用をしてます!
多くのエンジニアがリモートで働いています。もし、リモートワークに興味がある方がいましたら、是非チェックしてみてください。

当日のゲスト枠の発表

Realm Mobile Platformについての情報

Go Con 2017スライドまとめと感想

GoCon2017参加してきました。

Go Conference 2017 Spring - connpass

Twitter上で、全国のgopherがやたらと合コンと言って、#goconをつけて呟いていたイベントです。

いつも後で見返そうと思って、放置してURLわからなくなるので、今回はまとめることにしました。リンクが抜けているスライドは発見が出来なかったものです。
もし、あれば教えていただきたいです。

発表枠

LT

関連リンク

参加者としての感想

これで無料でいいのか!?というぐらい良い話を聞けました。gopherなら参加しないと勿体無いと思えるくらいの満足度でした。
カンファレンスとは直接関係ないですが、趣味で作っているITイベント検索アプリのeventory(iOS/Android)のバックエンドが、GAE/Goで動かしていまして、今回のカンファレンスでは、GAE/Goの話題が多かったので、この勢いでそろそろGoのバージョン上げてほしいなーと思っていたら・・・

カンファレンスに参加していたGoogle開発者様から希望に満ちたリプを頂きました。今後もGoの成長に期待です!

最後に、このカンファレンスを主催された運営様、登壇者の皆様ありがとうございました!