GraphQL(Kotlin)とStackdriver Traceの美しさに惚れました。

GraphQL(Kotlin)とStackdriver Traceがすごくいい感じだったから見て欲しい。

実装

ここで公開したアプリに修正を加えてみました。

shiraji.hatenablog.com

github.com

ライブラリの追加

spring-cloud-gcp-starter-traceを使い、Stackdriver Trace*1利用するため、gradleに変更を加えます。

repositories {
    mavenCentral()
    jcenter()
+    maven(url = "http://repo.spring.io/libs-milestone")
}

dependencies {

+    val springCloudGcpVersion = "1.1.0.RC2"
+    compile("org.springframework.cloud:spring-cloud-gcp-starter-logging:$springCloudGcpVersion")
+    compile("org.springframework.cloud:spring-cloud-gcp-starter-trace:$springCloudGcpVersion") {
+        // remove this after 1.1.0.RELEASE
+        exclude(group = "io.grpc")
+    }
+    compile("app.ubie:brave-kt:1.0.0")

https://github.com/ubie-inc/kotlin-graphql-sample/commit/b0aee867adf13e3e2ef41e8c3e8a5979619995f3

exclude(group = "io.grpc")しているのは、1.0.0.RC1よりバージョンが上のspring-cloud-gcp-starter-traceを使うと、io.grpcが複数のバージョンに依存してしまう問題があるためです。以下で問題提起しています。1.1.0.RELEASEで修正が完了する模様です。

github.com

環境変数などを変更

次に、Stackdriver Traceを利用できるように環境変数などを変更しています。 GraphQLのクエリをログ出力するために、graphql.GraphQLのログレベルをDEBUGに変えています。

  cloud:
    gcp:
      trace:
-        enabled: false
+        enabled: true
      logging:
-        enabled: false
+        enabled: true
  sleuth:
    sampler:
      probability: 1.0
@@ -24,3 +24,4 @@ logging:
  level:
    root: INFO
    org.springframework.jdbc.core.JdbcTemplate: DEBUG
+   graphql.GraphQL: DEBUG

https://github.com/ubie-inc/kotlin-graphql-sample/commit/beb23dc3b858c1cd8cbead49d2f7c1a0abd59b21

Scope Spanを使い、さらに見やすく

最後に、brave-ktを利用して、各クエリがどれくらい時間がかかっているのかを確認するTraceを入れます。

-        if (icd.isBlank()) return emptyList()
-        return jdbcTemplate.query(
+        return Tracing.currentTracer().scopedSpan("findKinkiDrugsByIcd") {
+            if (icd.isBlank()) return emptyList()
+            jdbcTemplate.query(
                 //language=SQL
                 """
                 SELECT
                   yj_code,
                   icd
                 FROM
                   disease_kinki_drug
                 WHERE
                   icd = :icd
                 """.trimIndent(), mapOf("icd" to icd), rowMapper
-        )
+            )
+        }

https://github.com/ubie-inc/kotlin-graphql-sample/commit/25e8e477b365f037daa4948dc94b24ff5789709a

brave-ktに関しては以下の記事に書いてあります。

shiraji.hatenablog.com

結果

ライブラリ追加して、ちょこっと見やすい工夫を入れるだけなのですが、以下のような結果が表示されるようになります。

f:id:shiraji:20190116030939p:plain
Scope Spanを使い、各DBアクセスの時間を見えるように

f:id:shiraji:20190116031047p:plain
1クエリ内で発行されたログも表示できる

f:id:shiraji:20190116031125p:plain
リクエストクエリなども見える

特定のクエリに対して、どのSQLがどのタイミングで発行され、どれくらいの時間がかかっているのか?などが一目でわかるようになりました。

感想

めっちゃ便利。この結果が美しくて惚れました 😍

宣伝

さて、Ubieでは、こんな感じで、便利なものを積極的に採用して開発を進めています。もし、Ubieに興味出てきたなーって方はぜひ、@shiraj_iにDM下さい!

サーバサイドエンジニア以外にもいろんな職種募集していますので、こちらもぜひ確認してみてください。

speakerdeck.com

*1:Stackdriver Traceを利用するには https://cloud.google.com/trace/docs/quickstart?hl=ja を参照

Twitterエゴサーチで見る2018年まとめ

今年本当に自分のエンジニア生活が一変した年だったので、書き留めておく。(2年連続同じ文言)

shiraji.hatenablog.com

技術的な話は皆無です。ツイートと共に自分のための振り返りです。ので、自分のことを知りたい人以外が読んでもらっても多分「あ、そう。」という感想だけしか得られないはずので、戻るボタンクリックお願いします。

1月

2018年はこんなところからスタート

実はですが、初詣に行って、嫁ちゃんに今年で転職しそうだね。しっかりお祈りしときな。と言われて、(ははーん!この人何も俺のことわかってないね!結婚して何年経ったんだよ?)と思ったけど、今日の時点では(何で自分より自分のことを知ってる人がこの世に存在するんだ・・・?)と言う畏怖の念を抱いております。

2月

DroidKaigiがありました。How to Kontribute v4をやりました。このネタは結構な回数こなしてたんですが、あんまりうまくやれずに悔しい思いをしたのを覚えています。

きっと心の底から喜んでるでしょうね!

3月

r.kt #3でライブコントリビュートしました。ライブでコーディングしたの初めてだったので、かなり練習したのを覚えてます。

recruit-lifestyle.connpass.com

ライブストリーミングでshibuya.apkにも参加しました。子供がいるのでこう言うのがどんどん流行ってほしい!と思ってます。準備が大変ですけど・・・。

4月

4月になったので、キャリアについて真剣に考え始めたタイミングでした。早めのジョイン狙いだよなーってなってる時です。

年収ツイートするといいねいっぱい貰えると学んだツイート

フラグが立った日

そして、その日のうちに、スタートアップの早めのジョインなんて今の自分じゃ無理だと判断したツイート。

やっぱり4月は転職周りのツイート多いな。。。

5月

Google IOに行かせてもらいました。初めての参加でした。

Codelabで問題一切やらずに、ChromeBookの検証してた。

これ忘れられなくて、どうしてもやりたくて、現職でやらせてもらいました。もうすぐ詳細を弊社の誰かが書くはず!

6月

サーバサイドKotlinやりたくて仕方なくなってる頃です。

なるほど

GitHub Satellite Tokyoに参加して、色々話聞きました。で、この機能を知らなかったことに正直衝撃を受けてて、毎日GitHub触ってないことに対して危機感を覚えました。このままこのキャリアでもいいかな?と考えてもいたのですが、この危機感は今でも鮮明に覚えていて、この日を境にキャリアを変えると決断しました。で、6月終わりには現職に転職を決めました。

7月

コネヒトさんのイベントに参加して、Kotlin Festのネタ集めを始めました。本当にこのイベントのおかげで色々助かったので、コネヒトさんには感謝しかない!

にも関わらず恩を仇で返す

当然ごめん。

8月

そして最終出社日

最後の最後まで、店舗の方に顔だして仕事してたりしてました。自分がこの会社で長く働けなかったことは本当に悔しいし、またどこかの大企業に再挑戦できる機会がどこかであればいいなと思ってます。

そして、テンションおかしくなって、なぜかその日のうちにコミケに初参加を決めました。

参加者がみんな熱いな!ってちょっと感動してました。

Kotlin Festのスケジュールが公開されて、ふてくされました。

入社日前日に現職のBBQイベントがあったので、家族同伴で参加しました。

入社して一番やりたかったことを2日目にしてやらせてもらって、本当に嬉しかった。

その一週間後のKotlin Festで登壇。自分と同じ時間のLTの人たちがみんな時間通り進めたおかげで、最後の辺り全員が自分の登壇を観ると言う美味しい状況でした。

あと、ぼっち飯回避でTwitterで募集したら始めてお会いする人たちとご飯いけました。本当に楽しかった。

shiraji.hatenablog.com

この8月は今年の中で一番濃かったなー。

9月

現役引退予定でしたが、いまだにバリバリコーディングしてます。35歳定年なんてのはないですね。

10月

Pixelを合法的に日本で触れるなんて思ってもいなかったですね。これが4月までに発生してたら転職とかなかったかも?なので、何が起こるかわかりませんね。

GraphQL始めました。この辺りからツイートがGraphQL一色になってる。かなりお気に入りになった模様。

これ公開したら良くね?って気づいてすぐにOSSとしてコーディングスタイル公開しました。なかなか良い反応もらえて嬉しかった。

その勢いのままbrave-ktも公開しました。

shiraji.hatenablog.com

github.com

11月

2年連続でDroidKaigi登壇が決まりました。今回は日本語でやります。ライブコーディングの予定です。スライドあるとしても自己紹介だけになるので聴きに来てね!

キャリア変えたことを報告しました。Android開発者に罵られるかも?とビクビクしてました。でも当然そんなこと無く、本当に暖かい良い開発者コミュニティだなーと思います。DroidKaigiで少しでも恩を返せたらと!

shiraji.hatenablog.com

12月

今年はアドベントカレンダーに参加しない方針だったので、こっちの参加表明。

でもKotlin枠が空いてて、書くのを抑えられなかった人です。これ結構プロダクションに乗っかってるコードに近いので、興味ある人はぜひ触ってみてください!10月から月1ペースでOSS公開してます。そんな頻度とかは特に意識はしてないけど、出せるものは出して、サーバサイドKotlinやる人増えてくれたら嬉しいなー。

shiraji.hatenablog.com

github.com

2018年の目標達成度

2018年の目標

一回登壇を経験したので、流れも掴めたし、登壇の方の負荷も減ると思うので、コードのアウトプットと両立させたい。

負荷減ってない気が・・・。OSSのコードアウトプット量は前半かなり減っていたので、焦りが出た。それを機に転職して、2018年後半結構アウトプット出来たと思う。 ただ、まだまだやれると思うので、来年はもっと増やしていきたい。

2019年の目標

OSSもそうだけど、仕事でのアウトプットも増やしていきたい。まだまだ手探り感がある。 プライベートはようやく来年から落ち着きそうなので、家族みんな元気に過ごす!が目標。

最後に

8月に転職してまだ4ヶ月しか経ってないのかーと驚きです。今年は仕事もプライベートも色々あって、ドタバタだった感があります。

今年も初めましてな方といっぱい会うことが出来ました。来年もきっと楽しいことが起こるだろうなとワクワクしています。

今年一年、ありがとうございました。来年もよろしくお願いいたします。

Kotlin大好きな人向けなCollectionのstdlibメソッドを掘り出してみた

これはQiita Kotlin Advent Calendar 10日目の記事です。

qiita.com

これマニアックなんじゃね?Kotlin大好きな人向けなんじゃね?ってstdlibにあるIterable/Collection/List/Set/Mapのメソッドを独断と偏見で挙げてみようと思います!

Collection - Kotlin Programming Language

想定読者

  • KotlinのCollectionのstdlibメソッドもっと知りたい人
  • 一旦はさすがです!って褒めてくれる人

環境

Kotlin 1.3.11で使えるメソッドにしています。全メソッドPlaygroundで動作確認済みです。

distinct

説明: 重複削除したListを作る

distinct - Kotlin Programming Language

サンプルコード:

https://pl.kotl.in/HJnYskugV

fun main() {
    val list = listOf(1,1,1,3,5)
    println("List: $list") // List: [1, 1, 1, 3, 5]
    println("Result: ${list.distinct()}") // Result: [1, 3, 5]
}

感想: かー毎回Setに詰め込んでたね。それでListが欲しい場合は、toListしてたね。さらに順番が必要なら色々頑張ってたね。便利😊

intersect

説明: 両方のCollectionに入っている要素をSetにして返す

intersect - Kotlin Programming Language

サンプルコード:

https://pl.kotl.in/SkQkak_eE

fun main() {
    val list1 = listOf(1,1,1,3,5)
    val list2 = listOf(3,4,1)
    println(list1.intersect(list2)) // [1, 3]
}

感想: 知らなかったら、filterとかcontainsメソッドなどで泣きながら書いてたと思う。

union

説明: Collectionをがっちゃんこする。戻り値はSet

union - Kotlin Programming Language

サンプルコード:

https://pl.kotl.in/rykBje_lV

fun main() {
    val list1 = listOf(1,1,1,3,5)
    val list2 = listOf(3,4,1)
    println(list1.union(list2)) // [1, 3, 5, 4]
}

感想: intersectと同じ感想

onEach

説明: 渡した引数のactionを全ての要素に実行し、レシーバーを返す

onEach - Kotlin Programming Language

サンプルコード:

https://pl.kotl.in/HyrGyxugE

fun main() {
    val list = listOf(1,1,1,3,5)
    list.onEach {
        println("Log: $it")
    }.filter {
        it > 3
    }.forEach {
        println("Result: $it")
    }
}

結果

Log: 1
Log: 1
Log: 1
Log: 3
Log: 5
Result: 5

感想: forEachして、そのまま何かしたい!って時にまたレシーバーを書いてたけど、これ使えば全部繋げられるじゃん。便利じゃん。onEachWithIndexとか欲しくなりそう。

partition

説明: Collectionの要素が引数の条件にマッチした要素をPairのfirstのListに。マッチしない要素をsecondのListに分割する

partition - Kotlin Programming Language

サンプルコード:

https://pl.kotl.in/HJgvexugN

fun main() {
    val list = listOf(1,1,1,3,5)
    println(list.partition { it >= 3 }) // ([3, 5], [1, 1, 1])
}

感想: 分割が必要な時、頑張らなくてもいいね!

requireNoNulls

説明: 要素にnullがないことを担保する。もし、nullがあればIllegalArgumentException

requireNoNulls - Kotlin Programming Language

サンプルコード:

https://pl.kotl.in/SJ3VMlul4

fun main() {
    val list: List<Int?> = listOf(1,2,4)
    val listNoNulls: List<Int> = list.requireNoNulls()
}

感想: requireNotNullのCollection版って感じ。nullがないことが前提条件のメソッドとかで利用できそう。(特に気にもせずに無心でfilterNotNullするかもだが。。。)

unzip

説明: PairのArrayに対し、firstの値だけのListとsecondの値だけのListをPairとして返す。

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/unzip.html

サンプルコード:

https://pl.kotl.in/ryYFNedx4

fun main() {
    val array = arrayOf(1 to "foo", 2 to "bar", 3 to 5)
    println(array.unzip()) ([1, 2, 3], [foo, bar, 5])
}

感想: ちょっと限定的すぎる?🤔いや、こういうのいいですよねきっと!

windowed

説明: Collectionをsizeの要素数のListを作る。次のListはstep分ずれたものからスタートする。

ごめんなさい。どう英訳すれば一番わかりやすいのかすらわからない。以下が原文。

Returns a list of snapshots of the window of the given size sliding along this collection with the given step, where each snapshot is a list.

windowed - Kotlin Programming Language

サンプルコード:

https://pl.kotl.in/r13a_gdxN

fun main() {
    val list = listOf(0,1,2,3,4,5,6,7,8,9)
    println(list.windowed(size = 5, step = 3)) // [[0, 1, 2, 3, 4], [3, 4, 5, 6, 7]]
    println(list.windowed(size = 5, step = 3, partialWindows = true)) // [[0, 1, 2, 3, 4], [3, 4, 5, 6, 7], [6, 7, 8, 9], [9]]
}

partialWindowsがtrueだった場合、sizeに足りてないものも含む。

感想: ちょっと置いていかれたメソッド。これが便利なんだ!というくらいKotlinを使いこなしたい・・・

zipWithNext

説明: Listの隣り合った要素をListのPairとして返す

zipWithNext - Kotlin Programming Language

サンプルコード:

https://pl.kotl.in/r1cjdxOlE

fun main() {
    val list = listOf(0,1,2,3,4,5,6,7,8,9)
    println(list.zipWithNext()) // [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8), (8, 9)]
    println(list.zipWithNext { a, b -> a + b }) // [1, 3, 5, 7, 9, 11, 13, 15, 17]
}

感想: transformを渡せば、数列とかで便利かも?あとは次の値を見て何かするような処理とかでもいいかも。

サーバサイドKotlin+GraphQLのアプリケーションをOSS化しました

これはQiita Kotlin Advent Calendar 2018 1日目の記事です。

qiita.com

ツイートしたら良い反応もらえたので、サーバサイドKotlinのアプリを以下で公開する事に決めました。

github.com

cloneして、docker-compose upしたら、GraphiQLが http://localhost:8090/graphiql 立ち上がって、サーバの動作確認が出来る。簡単!!!

f:id:shiraji:20181214165629p:plain
GraphiQL画面

Ubie内で絶賛開発中のシステムに少し手を加えていますが、基本的に実際に開発している環境と同じになっています。

公開目的

公開に至った理由はいくつかあります。

サーバサイドKotlinやりたいって人のハードルを下げたかった

やりたい!という声はいろんなところで聞くけど、「環境ないし・・・。」「経験無いし・・・。」という方も多いと思ってます。実際Ubie入社前の自分がそうでした。

このプロジェクトは、clone後すぐにローカルで動かす事が可能です。

そのため、このお手軽さを見てもらい、もっと多くの方にサーバサイドKotlinやってもらいたいなーと思いました。

Kotlin+SpringBoot+GraphQLの開発者にコードレビューしてもらいたかった。または仲良くなりたかった

今回、色々な事情があり、KotlinでGraphQLの採用をしています。自分にKotlin+GraphQLの実績がないため、コードを公開することにより、いろんな人に見てもらい、より良いものになったら良いなと思いました。さらに同じような環境の方ともっと仲良くなりたいです!

特に後述しますが、多対多のGraphQLResolverの処理が本当にこんな感じのコードで良いのかが不明です。Spring+GraphQLの情報が少なすぎる。。。もっと効率良く出来そうなんですが・・・

同じ構成のものをOSS化することにより、Ubieの入社即パフォーマンス発揮!をしてもらいたかった

今のところ、Ubieのサーバサイドに関わる人には入社前に1日インターンをしてもらい、チームに溶け込めるか?を見てもらう時間を取っています。しかし、時間が1日しかなく、環境構築に時間を取られてしまうのはもったいないです。そのため、同じ環境をOSS化することにより、先に環境構築出来る状況にしたいと考えました。

さらに将来的には1日インターン宗教上無理!って方にこのOSSプロダクト開発に携わってもらい、雰囲気をそこで味わってもらうのもいいかなと考えています。

技術スタック

このプロジェクトでは以下の技術スタックを利用しています。

今後テスト書くので、以下が追加予定

  • JUnit5
  • Mockk
  • WebTestClient

パッケージ構成

app.ubie.kotlingraphqlsample
├── domain
├── infrastructure
│   └── jdbc
├── presentation
│   ├── queryresolver
│   └── resolver
└── service

(Query)Resolver → Service → JDBCという呼び出し階層になってます。

DB

f:id:shiraji:20181214205606p:plain
E-R図

薬と傷病の関係性のサンプルテーブルになっています。医療業界で利用されているデータに合わせ、IDがなかったり、外部キーがUniqueじゃなかったり、全て多対多の仕様になっています。

DBアクセスはspringがデフォルトで提供しているJDBCを使い、SQLをコードに書いてDBアクセスしています。これは賛否両論あると思いますが、以下の理由から採用しています。

  • SQLのチューニングがしやすい
  • ラーニングコストが低い
  • 一目でどこでどのSQLを発行しているのかがわかる

Flywayを利用して自動マイグレーションが走る状態にしてあります。

GraphQL

GraphQLのスキーマ定義ファイルには薬情報と病気情報が定義されています。

type Query {
    # 薬を取得します
    drugs(yjCode: String!) : [Drug!]
    # 病気を取得します
    diseases(icd: String!) : [Disease!]
}

# 薬情報
type Drug {
    # 薬品名
    name: String!
    # YJ Code。だいたい薬を表すために使われるCode。一意ではないので注意
    yjCode: String!
    # 併用禁忌薬
    kinkiDrugs: [Drug!]
    # 処方してはいけない特定の病気
    kinkiDiseases: [Disease!]
} 

# 病気
type Disease {
    # 傷病に対してつくコード。一意ではないので注意
    icd: String!
    # 病名
    name: String!
    # 処方されてはいけない薬
    kinkiDrugs: [Drug!]
}

Queryブロックには公開APIが定義されています。graphql-javaではこの定義をGraphQLQueryResolverを実装したクラスで記述する必要があります。このプロジェクトではqueryresolverパッケージ内で定義されています。

@Component
class DiseaseQueryResolver(private val service: DiseaseService) : GraphQLQueryResolver {
    fun diseases(icd: String): List<Disease> = service.getDiseases(icd)
}
@Component
class DrugQueryResolver(val drugService: DrugService) : GraphQLQueryResolver {
    fun drugs(yjCode: String): List<Drug> = drugService.getDrugs(yjCode)
}

Drug/Diseaseブロック内にあるkinkiDrugsなどの外部キーを使った結合はgraphql-javaではGraphQLResolver<T>を実装する必要があります。resolverパッケージ内で定義されています。typeがDrugkinkiDrugsの取得時にはfun getKinkiDrugs(drug: Drug): List<Drug>が呼び出される。

@Component
class DiseaseResolver(private val service: DiseaseKinkiDrugService, private val drugService: DrugService) : GraphQLResolver<Disease> {

    fun getKinkiDrugs(disease: Disease): List<Drug> {
        val kinkiDrugs = service.findKinkiDrugsByIcd(disease.icd)
        return drugService.getDrugs(kinkiDrugs.map { it.yjCode })
    }
}
@Component
class DrugResolver(
    private val kinkiDrugService: KinkiDrugService,
    private val diseseKinkiDrugService: DiseaseKinkiDrugService,
    private val drugService: DrugService,
    private val diseaseService: DiseaseService
) : GraphQLResolver<Drug> {

    fun getKinkiDrugs(drug: Drug): List<Drug> {
        val kinkiDrugs = kinkiDrugService.findKinkiDrugsByYjCode(drug.yjCode)
        return drugService.getDrugs(kinkiDrugs.map { it.kinkiYjCode })
    }

    fun getKinkiDiseases(drug: Drug): List<Disease> {
        val kinkiDrugs = diseseKinkiDrugService.findKinkiDrugsByYjCode(drug.yjCode)
        return diseaseService.getDiseases(kinkiDrugs.map { it.icd })
    }
}

多対多な関係が多く、SQLの発行回数が若干多いなーという印象があります。この辺りどうしたらいいのか誰かと語りたい!!!

終わりに

このコード読んで、UbieでサーバサイドKotlinやってみたいと思った方は @shiraj_i に直接DMか以下のWantedlyさんのサイトから話を聞きにきて下さい!

www.wantedly.com

もっと気軽にUbieに遊びに来たい!という方は以下のBosyuさんからメッセージを送って下さい!

bosyu.me

Ubie, incに入社しました。

2018/08/20からUbieに入社しています。3ヶ月の試用期間も無事明けたので、色々書いておこうと思います。

キャリアを変えようとした動機

ちょくちょく話題になる35歳に今年なりました。

2018年4月の時点で自分の今後のキャリアについて悩んでいました。主な悩みはこのままAndroidアプリ開発者を続けるか?です。 Twitterさんや日経さんなど今や多くのWebサイトがブラウザ上での体験を圧倒的に良くするPWAを採用し始めています。PWAと比較してネイティブアプリを作る優位性が、低いレイヤーの処理を必要とするゲームくらいしかないのでは?という不安に陥っていました。このままこのキャリアを続けて5年後、Androidアプリ開発者の需要が万が一無くなってしまった場合、40代で(ゲームではない)Androidアプリ開発が得意です!としか言えなかったら、エンジニアとしてのニーズが無くなりそうだと。Androidアプリ開発者の需要がある今のうちに、新しいモノを試して、駄目だったり、さらにAndroid開発者の需要が増えたら、またカムバックすることが最善策ではないかと考えるようになりました。

Ubieを知る

さぁ次のキャリアどうするかー?といろんな会社にお話させてもらおうと考えていた時にたろうさんの転職のエントリーが出ました。

taro.hatenablog.jp

このエントリーで初めてUbieという会社があるんだなと知りました。当時このブログエントリーの感想は面白そうなことやってる会社だなー。RailsをKotlinに置き換えるとかスタートアップがそんな余裕なの!?でした。

Ubieと話す

たろうさんの記事の後1ヶ月くらい経ってから、Ubieがサーバサイドエンジニアを募集しているということを知りました。せっかくなのでとお話を聞かせてもらいました。

共同代表の久保と話すことが出来て、ざっくりまとめると

  • RailsからKotlinに切り替えるのはガチ
  • 論文を読み込んで、真剣に機械学習に取り組んでいる
  • 精度に関しては圧倒的な自信あり。他社はまず追いつけない
  • 創業して1年目(当時)だけど、日本だけではなく、世界をもう視野に入れてる

のような話をしてもらいました。思った以上にすごすぎて、ちょっとポカーンとしちゃいました。

Ubieに入る

その後、冷静に自分がUbieで働く場合を整理してみると

  • サーバサイドKotlinをやるチャンスがある
  • あのたろうさんと一緒に働ける
  • Data Scienceチームのサポート役の経験を積めそう
  • 組織がまだこれから整備される状況であり、自分の知見が活かせる余地がありそう
  • 事業的には伸び代かなりありそう
  • 創業1年目のベンチャーで、面白そうなフェーズ

自分の次のキャリアとしてこれ以上ないなと思い、入社をさせてもらうことにしました。今では本当にタイミングと運が良かったなと思っています。

入社の儀 f:id:shiraji:20181122235605p:plain

撮影してもらいました f:id:shiraji:20181107121402j:plain

Ubieの業務

入社前からサーバサイドKotlinをしたいと言い続けていたので、希望通り、主なタスクはサーバサイドKotlinをやらせてもらっています。Kotlin化の初期だったということもあり、たろうさんと1on1での開発をさせてもらいました。そのおかげもあり、サーバサイド開発のキャッチアップは結構すんなりいけました。

ライフサイクルが非常にシンプルということもあり、Kotlinのコーディングに集中出来、コード書く楽しさを実感してます。 テストもしっかり書く文化になっているのもお気に入りです。

Kotlinのタスク以外にもGitLabからGitHubへの移行、CircleCIの導入、OSSとしてライブラリの公開などなど色々やらせてもらっています。 毎日のようにテレレ テッテッテーとBGMが頭の中で流れています。

今後はこれらに加えGraphQL+Kotlinや機械学習との連携、SRE業務やPythonでの開発など様々なことに手を出していきます。

リモートRepository移行させたということもあり、正確なPR/issueの数とかわからない+自分の働き方上、多くのRepositoryに顔出しているので、分散しちゃっているのですが、一番コミット数が多いRepositoryはこんな感じでした。

f:id:shiraji:20181121012248p:plain

Ubieの制度 - リモートワークと有給休暇

Ubieは「週2のリモートワークが可能」です。オフィスの環境が良いこともあり、多くの人がオフィスに出社しています。今の事業フェーズではあまりうまく事が進むように整備し切れていないため、フルリモートは出来ないようになっています。今後可能になるように絶賛整備中です。

自分は奥さんと子供がいます。台風が来た週、子供が熱を出してしまったため、台風で一日、子供の看病で一日リモートしました。さらに子供の熱が下がらない可能性があり、有給休暇を取るかリモートするか悩んだため、Slackで投げかけてみました。

f:id:shiraji:20181121005053p:plain

リアクション見てもらえばわかる通り、看病であれば、週3日のリモートもOKでした。

ただ、さらに良くないことが続きました。自分の奥さんが入院することになってしまいました。自分たち家族は親族が新幹線乗らないと来れない距離にいるため、今までであれば、この状況に陥った場合は有給を使い看病することになります。入退院を繰り返すことになってしまったので、合計すると3ヶ月のうち、約1ヶ月はリモートしていました。最大2週間連続でした。でも同僚は自分がうまく働けるように工夫してくれました。

この件からUbieのリモートワークのルールは「週2のリモートワークが可能。ただし何かしら理由があり、恒久的でなければフルリモート可」が実態です。

ということで、この3ヶ月会社にいないことも多かったのですが、最終的に有給休暇の消化0です。子供が産まれてから子供の看病のため有給休暇を取得し、毎年有給消化率80%は越えていました。あんまり自分のための有給を取った覚えがなく、どういうタイミングで取ればいいのか悩んでいます。ちなみに有給は休みます!と宣言したら取れますし、週5のフルタイムであれば入社日に14日付与となります。

Ubieの制度 - 勤務時間

Ubieは裁量労働制です。

日本で初めて入った会社が裁量労働制で、朝9:30出社(1分でも遅刻NG)で帰りは残業などして夜10:00がザラでした(見込み残業が30時間だったので40-50時間くらいサービス残業してました)。そのため、裁量労働制に対して非常に嫌なイメージを持っていて、それ以降裁量労働制の会社を避けていました。

Ubieも裁量労働制だと聞いたので、若干の残業は覚悟の上で、プライベートは絶対死守しようと考えていました。しかし、実態はUbieはしっかり裁量労働をさせてくれています。遅刻という概念がほぼなく、業務中にお昼寝や映画を観にいく人もいます。自分は現在奥さんが病気になってしまったこともあり、朝息子を保育園に送り、夜迎えに行くことが必須となっています。そのため、出社が10:00頃になり、退社は17:30です。30分業務時間が足りてません!しかし自分の労働時間ではなくアウトプットを見てくれてますし、足りていない分、家で仕事して補填しています。トータルでは残業はほぼなしです。

自分があまりの早さで帰るため、Ubieの他の社員さんたちがどれだけ残業しているのかあまり把握していないですが、自分より早く来て早く帰る人もいますし、自分も今後も残業0時間を目安に働いていこうと考えています!

Ubieの制度 - KPT

Ubieでは週次でチーム内KPTをしています。リモートワークを始めたタイミングではたろうさんや自分が今何をやっているのか把握できない事態に陥ってしまったため、この課題がKPTのPとして挙がりました。解決策としては「Slackで始業時に今日のTODO共有する」としたところ、お互い何をやっているのかが把握でき、リモートでも非常にスムーズにタスクが進められるようになりました。

こんな感じで毎朝やってます。この日はリモートで9時から開始

f:id:shiraji:20181122215342p:plain

残業ほぼなし、リモートもしていて、パフォーマンスが下がらないか不安だったのですが、問題があれば、都度全員で解決策を考え、改善していく文化があり、色々手助けをしてもらい、特にパフォーマンスを落とさずにタスクがこなせています。

他にも良いと思ったものを取り入れたり、逆にこれは減らして良いだろうと判断したものは削減したりと試行錯誤していて、ガンガン変わっていく姿が見れて楽しいなーと感じています。

Ubieの社員構成

Ubieの中でかなり気に入っているのがこの社員構成です。

f:id:shiraji:20181121013528p:plain

特徴的なのがSaaSのスタートアップにデザイナーが2人揃っていること。しかも強い。この2人がUXのためなら公民館だろうが診察室だろうがガンガン入っていってより観察しまくります。動き方はUIデザイナーではなく、サービスのデザイナーです。医師が2人いるのも特徴的です。Ubieに入って初めて医師って全員が全員病院に勤務するわけでもないのだなという気づきがありました。弊社の医師だけなのかもしれませんが。全体を観て、バランスが非常に取れている組織だなーという印象を受けています。(業務委託の人がカウントされていないので、その人たちを含めるともう少しエンジニアの数が増えます。)

ちなみにこの社員構成ですが来月には数字がいくつか増えます。どんどん大きくなっていっていて、ワクワクします。

終わりに

ということで、3ヶ月しか働いていないこともあり、良いことにしか目がいっていない恋は盲目状態のブログエントリーでした。

半年くらい働けば、きっと悪いところももっと見えてくるはずなので、来年落ち着いたらまた続編を書いていきたいと思います。

これを見て、興味あるなーという方は @shiraj_i にDMしてもらうか、以下で応募して下さい。

https://www.careers.dr-ubie.com/www.careers.dr-ubie.com

一覧にはないのですが、SREやデータサイエンティストも募集してますのでぜひ!

直近ではこんなイベントもあるよ!

bosyu.me

bosyu.me

brave-ktをリリースしました

Ubieという会社に入社して、brave-ktというライブラリを公開しました。 Ubie入社ブログエントリーは試用期間明けたら書くとして、このライブラリについて書いていこうと思います。

想定読者

  • Ubieのシステムに興味ある人
  • サーバサイドKotlin興味ある人

前提

今自分が関わってるプロジェクトではSpring Boot2.1+Kotlin1.3+GCP(GKE, CloudSQL)を利用して、RESTfulのAPIを開発しています。 ざっくり図にするとこんな感じです。

f:id:shiraji:20181103114707p:plain

よくある構成だと思います。 ユーザがAPIを叩くと、最初にロードバランサーに到達し、アプリサーバを経由して、DBアクセスします。

ログ出力

この構成で問題になるのはログです。一つのリクエストを処理するのに複数のコンテナが関わっており、あるユーザのリクエストを追跡する場合、ログを一つ一つ確認していかなければなりません。

f:id:shiraji:20181103114819p:plain

この問題を解決するために出てきたのであろうものがStackDriver Traceです。 導入すると以下の公式ドキュメントのquickstartの画像のようにTimelineのところで複数のコンテナのログを一つのリクエストごとにまとめてくれます。

f:id:shiraji:20181103113249p:plain Quickstart  |  Stackdriver Trace Documentation  |  Google Cloud

StackDriver Traceの導入

StackDriver Traceの導入には、Google Cloud SDKが必要です。

Installing Google Cloud SDK  |  Cloud SDK Documentation  |  Google Cloud

gcloudにログインして、project名を設定します。

gcloud auth application-default login
gcloud config set project [PROJECT名]

Spring Bootでの導入には設定をいくつか追加します。Ubieではlogbackを利用しています。 ちなみに、spring-cloud-gcp-starter-traceの1.0.0.RELEASEは動かないので注意して下さい。

build.gradle

repositories {
    // for spring-cloud-gcp-starter
    // There is a bug on 1.0.0.RELEASE
    maven { url "http://repo.spring.io/libs-milestone" }
}

dependencies {
    compile 'io.sentry:sentry-logback:1.7.5'
    def springCloudGcpVersion = '1.0.0.RC1'
    compile("org.springframework.cloud:spring-cloud-gcp-starter-trace:$springCloudGcpVersion")
    compile("org.springframework.cloud:spring-cloud-gcp-starter-logging:$springCloudGcpVersion")
}

logback-spring.xml

<configuration>
    <springProfile name="production">
        <include resource="org/springframework/cloud/gcp/autoconfigure/logging/logback-appender.xml" />

        <root level="INFO">
            <appender-ref ref="STACKDRIVER" />
        </root>
    </springProfile>
</configuration>

application.yml

spring:
  cloud:
    gcp:
      trace:
        enabled: true
      logging:
        enabled: true
  sleuth:
    sampler:
      probability: 1.0

問題発生

さて、ここまでやってログを出してみたのですが、以下のようになってしまいました。

f:id:shiraji:20181103115315p:plain

queryのログなどがまとまっています。しかし以下の赤線の部分がquery実行中になるはずなのですが、queryの実行時間などを見ることが出来ません。

f:id:shiraji:20181103115520p:plain

これはCloudSQLがTraceなどに対応していないように見えます*1

つまり以下のような感じになっているようです。 f:id:shiraji:20181103115826p:plain

これを解決するためには、GCE上のSpring+Kotlin側で明示的にログを出力する以外手は無さそうです。

ようやく本題

この問題を解決するため、TraceのライブラリにあるScopedSpanを使います。

val span = Tracing.currentTracer().startScopedSpan("db access")
try {
    jdbcTemplate.query ( ... )
} finally {
    span.finish()
}

こんな感じに表示されるようになります。良さそう。 f:id:shiraji:20181103121824p:plain

それでこのコード見てて、Kotlinだしもうちょっと書きやすくできないかなーって思い、ライブラリを作りました。それがbrave-ktです。 これを導入すると以下のように書けるようになります。

val result = tracer.scopedSpan("db access") {
    jdbcTemplate.query ( ... )
}

以上!(二行しか紹介してない。。。)

宣伝

さて、Ubieでは、エンジニアを募集してます!

https://www.careers.dr-ubie.com/www.careers.dr-ubie.com

結構GCPにはヘビーに今後も依存していくので、その辺り関わりたいSREの方や、Spring+Kotlin書きたいサーバサイドKotlinやりたい人はぜひ応募して下さい! 雰囲気先に知りたいって方は以下のただ社員が遊ぶイベントがありますので、どんな感じか確認しにきてください。

bosyu.me

ちょっと怖いから何からすればいいかわからない。という方はTwitterでDMして下さい。

Kotlin Fest 2018に参加してきました

Kotlin Festの感想ブログを読んでいて、やっぱり自分も書きたくなっちゃった。だから書く。

登壇依頼

DroidKaigiの発表の時、CfPを出すものだと思っていたので、何を書こうかなー?って思ってたのですが、一向にCfP出せる場所がなく、本当に大丈夫なのかな?と心配していたら、@satorufujiwaraさんから依頼を直接受けました。日時が土曜日だったこともあり、家族に確認を取り、なんとか了承してもらって参加することに決めました。登壇内容はHow to Kontributeを日本語でやってほしいという話でした。

もともとはHow to KontirbuteはOSSの話であるし、日本人だけにやるものではないと思っていて、英語でやるものだというイメージがあったのですが、藤原さんがそういうならと日本語でやってみようと思い、快諾しました。

登壇準備

準備は英語のプレゼンを日本語にしていくだけなので、簡単だと思ってました。が、初めてリハをやったときは52分(セルフィーなし)という記録を出してしまいました。これはプレゼン最中にふと思ったことを口にしてしまえる母国語だからで、これ重要だ!とかここで笑いを取りにいけそう!と思ったら口に出してしまい、グダグダに伸びるという状態でした。さすがに5分以上超過はまずいと、スライドを何枚か削り、メモを英語で書いていたので、あまり考える余地を作らせないために、日本語に書き直すなどの対処をしました。

スケジュール確定

スケジュールを知ったのはconnpassでスケジュールが公開された時です。公開された+LTと同じというのを同僚の某主催者から聞かされました。 カンファレンスでは基本早めにトークさせて下さいって毎回お伝えしているのですが、Kotlin Festでは時間の指定しなかったです。ただまさか最後になると思ってなかった+LTとセッションを同時刻にやると思ってなかったので、決まった時の本音は「それはないわー!」でした。

前日

完全に見落としてたのですが、ランチがないことにconnpassを見て気づきました。 で、せっかくだし、色んな人と話したいと思ったので、こんなツイートしました。

これツイートする前に結構文章推敲してて、それを嫁ちゃんが横で見ていて、ツイートしたらぼそっと「これで誰も来なかったらウケるw」って言われてまじでこれで来なかったらどうしようと泣きそうになりましたが、結果4人も集まってくれました!

ちなみに夜にリハする予定だったのですが、気づいたら、寝てて、3時くらいに「あ、この登壇終わったわ・・・」と愕然としてました。

当日(午前中)

時間通りに到着しました。ビルの入り口がわからなくて迷子になったけど。

登壇者の控室がものすごく広くて快適でした。快適だったんですが、これなら3つの会場にしてもらって、もっと色々発表あったら!と思ったけど、あまりに快適だったからやっぱりそのままがいいです。控室にもセッションの音とスライドが見えていて、登壇準備や練習をしつつ、聴けるという最高の環境でした。

オープニングは会場の雰囲気を確認したく、2列目で聴いてました。そしたら、LTと時間がかぶったためなのか、二回も紹介してもらっちゃって、もうテンション上がりまくりでした。

「Kotlinを愛でる」をプッシュしまくっていたので、じゃあ、ということで、タイトルにEmbracing Kotlinを追記しました。

ランチ

前日に募集したのですが、何話すとか、何も決めずに向かいました。 お店は朝迷ったときにビルの1階に肉のお店を見つけて、暑かったし、そこでどうでしょう!って即決しました。 初めて会った人ばかりだったのですが、一時間Kotlinの濃い話が出来ました。もっと時間あったら良かったのですが、本当に楽しかったです。 また登壇とかする機会があれば、スピーカーということネタにランチ募集しようかなと思いました。

当日(午後)

参加者と交流することが目的だったので、セッション一つも聴いてないです。(そのためPSIViewerがこの日二回目の登場だったのを知らなかった)

Yahooさんのブースでモブプロやるって言っていたので、参加しました。 モブプロはあと一人か二人自分のチームに入ったら、たろうさん、自分含めやってみたいなーと思ってたので、どういう感じになるのかな?ということで参加しました。 RESTのAPIを作るプロジェクトでした。Springの知らない機能を使っての実装だったので、あんまり口が出せなかったのが残念だったのですが、気になっていた、意見が衝突する場合、どう解消するのか?を知りたく、それまでの話からドライバの方が想定していたであろうControllerのメソッド名(findById)ではないもの(show)にしませんか?と叫んでみました。 まぁRESTのControllerのメソッド名はあんまり影響ないためか、参加者から挙がったものだからか、特に議論もなく採用されてしまいました。 あー!やっちまった!ということで

さすがに時間が短かったなーという印象です。登壇前の30分でしたし、参加者は席を取りたいし。 個人的には実際に意見がぱっかり割れた時にどのように合意が形成されるのかが知りたかったです。 RESTのAPIを作ることが目的になってしまっていた感じで、うーん、じゃあSpringのライブコーディングという形にして、モブプロではなくても良いんでは?となってしまいました。 でもモブプロを疑似体験出来て良かったです。設計するタイミングとかでの採用もいいなーという感じでした。

他にもmixiさんやCAさんのブースのPuzzler参加しました。mixiさんの方では、何問か間違えましたが、なんとか良い結果だったと思います。CAさんの方はさっぱり過ぎて、手元にIDEAないと無理でしょこんなん!という感じでした。楽しかった! ただ、CAさんの方、これせっかくやるならエンジニア一人は常時ブースにおいてほしかったです。ブースにいる方がふたりともエンジニアじゃなくて、このコードに関してーとか話そうとしたのですが、エンジニアじゃないので、またあとで来て下さい!と言われてしまって、うおー聴きたい!と消化不良でした。もっと話したかった!

ブースを一通り回ってから、控室に戻り、スライドの手直しをはじめました。控室で他のスピーカーの話が聞こえるの非常に良いのですが、あまりに面白そうな内容で気になってしまって、控室前の椅子に座って準備してました。トイレ前だったこともあり、結構色んな人に話しかけてもらえて、これはこれで楽しかったです。登壇前の緊張がほぐれたし、色々また勉強することが出来ました。

登壇2時間前くらいに自分のスライドの特にInspectionの実装コードのスライドがちょっとこのカンファレンスの空気と合ってないなーと感じました。 そこで、登壇時間が懇親会前ってこともあり、急遽いらすとやさんの飲み物、食べ物の画像をペタペタ貼り、Twitterで盛り上げようぜ!というスライドを追加しました。

残り時間少しということもあり、最後のリハをしました。控室で外向きながら小声でブツブツ話していたので、傍目から見たら相当やばい感じだったと思います。

登壇

登壇前15分に会場に行きました。 アダプターが合わず、かなり焦ったのですが、スタッフ、会場の皆さんに探してもらい、ちょうど良いものを10分くらい前にそろえてもらいました。本当に感謝です。 さらに想像通り、結構な方がLTの会場に向かっていました。そこで登壇者の控室で近くにいた、m3の方たちに控室で聴けるにも関わらず、空いてるから会場に来て下さい!とお願いしたら本当に来てくれてしかもかなり前の席を確保してくれるという神対応をしてもらいました。

登壇前めっちゃ緊張してたのですが、@mhidakaさん、@shanonimさん、@chiiia12さんが色々話しかけてくれて、結構リラックスしてやることが出来ました。

登壇中、いくつかのスライドで頭真っ白になってしまい、あれ?何話すんだっけ?とめっちゃ頭がフル回転したのは覚えています。 会場に聴きにきてくれた人たちの反応が暖かく、自分が思った通りのところでしっかり反応してくれていて、やっぱりKotlin界隈暖かくていいなーと。 懇親会のための気分盛り上げのいやすとやさんも反応が良くて、してやったり!でした。

photos.app.goo.gl

終わった後気づいたのですが、LT会場でも結構自分のことを紹介してもらっていて、さらにスタッフの機転でLT後に自分の会場もつないでもらったようで、本当にごちそうさまでした。

懇親会

懇親会ではパックマンルールが採用されてました。本当にこのルールいいですね。 懇親会だと仲良い人と話すことになりがちなんですが、空いているので、入ってきやすいし、輪を作ってる人も周りを意識している感じでした。 でももっとはじめましてな方と話したかった!

最後に

ものすごく楽しかったです。Kotlin Fest 2019も登壇したい!!! Kotlinをもっともっと愛でたい!!!!!!