30億のデバイスで走るHonMarkHunt

JavaとJavaScriptと恐竜の絶滅について書いていきます。

Scala implicit デザインパターン

Scala implicit デザインパターン

implicit。書いてあるコードは読めるけど自分で実装する時に使いどころがワカン。」 みたいのがあって職場の人に聞いたらいい感じのリンクを教えて頂いたので翻訳しつつ勉強がてらメモ。

目次

  1. 最初に
  2. Implicit Contexts
  3. Type-class Implicits
  4. Derived Implicits
  5. Type-driving Implicits
  6. まとめ

最初に

しばしば貧弱Scalaエンジニア(俺)達から畏敬の念とともに語られるimplicit。実はそれ自体の機能はそんなに強力じゃないみたい。

  • implicit parameter : 明示的に引数のを渡す必要なく、その型とスコープ内の値に基づいて自動的に推論
  • implicit conversion function : 要求に応じて明示的に関数を呼び出す。

ただ単純に使用するのではなく、長い歴史と数多くのエンジニアたちの知恵を礎に最適な使用方法(=デザインパターン)が生まれた。4つほど紹介してみる。

Implicit Contexts

DB Connectionとか 何回も何回も使用する引数を、渡す度に定義してたらめんどくさいよね。値があるなら空気読んでそれ使ってよ。 という話。

ex) 非同期処理する全てのメソッドにExecutionContextを引き渡す

// 全てのメソッドが`ExecutionContext`を必要としている
def getEmployee(id: Int)(implicit e: ExecutionContext): Future[Employee] = ???
def getRole(employee :Employee)(implicit e: ExecutionContext): Future[Role] = ???

getEmployee(), getRole()の両方でカリー化された引数でExecutionContextを要求している。

// implicitではない雑魚
val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global

val bigEmployee: Future[EmployeeWithRole] =
  getEmployee(100)(ec).flatMap { e =>
    getRole(e)(ec).map { r =>
      EmployeeWithRole(e.id, e.name,r) 
    }(ec)
  }(ec)

implicitを使わない場合全ての呼び出し箇所で明示的に(ec)を引き渡す必要がある。すごくめんどくさい。 4つならまだいいけどもっと数が増えてきたり、複数箇所で使用されていたらもうめんどくさすぎる。

// implicit parameterを使うぞ!
implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global

val bigEmployee: Future[EmployeeWithRole] =
  getEmployee(100).flatMap { e =>
    getRole(e).map { r =>
      EmployeeWithRole(e.id, e.name,r) 
    }
  }

implicitキーワードとともにExecutionContextを定義することで、全ての使用箇所で明示的な引数の引き渡し(ec)を全て削除できる。やったねたえちゃん!という話。

Implicit Contextsの定義

  1. 通常ジェネリックス型ではなく、型パラメーターも持たない。
  2. 異なるシグネチャを持つあらゆる関数に同じimplicitが渡される。
  3. implicitは呼び出すタイミングによってその都度値が評価される。
    • play frameworkではHTTPリクエストの度にRequestオブジェクトの値が変わる
  4. AkkaのActerSystemのような場合にはimplicitの値は変更可能な場合がある。(これは意味不明誰か教えてくれ)

Implicit Contextsパターンは "ボイラープレートな引数を渡す便利な方法" であり、implicit parameter の唯一使用方法である。それ以外は一般的な引数と違いはない。

Type-class Implicits

いろんな型からJsonに変換したい場合とかにパターンマッチやオーバーロードで実装もできるけど、全ての型を記述するのは不可能でなによりコンパイル時にわからないし、同名関数を定義しまくるのは汎用性がない。変換可能な型が事前にわかればコンパイルエラーで防げるし、呼ばれるべき関数が型でわかれば汎用性もあるよねという話。

ex) Jsonシリアライズ

// Jsonオブジェクトの定義
sealed trait Json
object Json{
  case class Str(s: String) extends Json
  case class Num(value: Double) extends Json
  // ... もっとたくさん
}

def convertToJson(x) // このメソッドで x をJsonオブジェクトに変換したい

このような場合 def convertToJson(x): Json の実装はどのようなパターンが考えられるだろうか。

パターンマッチ

まずは前述のパターンマッチで実装してみる。

// パターンマッチで convertToJson を実装してみる
def convertToJson(x: Any): Json = {
  x match{
    case s: String => Json.Str(s)
    case d: Double => Json.Num(d)
    case i: Int => Json.Num(i.toDouble)
    // float, short などさらに多くのケースが考えられる
  }
}

さあ。このconvertToJson()はどうだろうか...?勿論パターンマッチで実装済みの型からの変換は正しく動くだろう。しかし、未実装の型(Booleanjava.io.File)の場合はどうだろ? 実行時エラーで失敗してしまう。 現実的に考えて全ての型のパターンマッチを実装するのは不可能であるし、何より変換できない型が渡された場合はコンパイラーに止めてほしい。この問題を解決するのがType-class Implicitsパターンだ。

オーバーロード

オーバーロードを使用することで複数パターンのconvertToJson()を実装することができるかもしれない。

def convertToJson(t: String) = Json.Str(t)
def convertToJson(t: Double) = Json.Num(t)
def convertToJson(t: Int) = Json.Num(t.toDouble)

このように同名で引数の型が異なる関数を複数用意しておけば、前述の未実装の場合にコンパイルエラーが発生しない問題は回避できる。 しかし、convertToJson()のような関数を複数作成しようとした場合どうなるだろうか?

def convertToJson(t: String) = Json.Str(t)
def convertToJson(t: Double) = Json.Num(t)
def convertToJson(t: Int) = Json.Num(t.toDouble)

def convertToJsonAndPrint(t: String) = println(convertToJson(t))
def convertToJsonAndPrint(t: Double) = println(convertToJson(t))
def convertToJsonAndPrint(t: Int) = println(convertToJson(t))

def convertMultipleItemsToJson(t: Array[String]) = t.map(convertToJson)
def convertMultipleItemsToJson(t: Array[Double]) = t.map(convertToJson)
def convertMultipleItemsToJson(t: Array[Int]) = t.map(convertToJson)

こうなる。

オーバーロードは同名関数という規約があるので、少し役割を変えた関数を定義しようとした場合。その新しい関数でも全ての型を網羅するオーバーライドを記述する必要がある。これはもうさいあくである。

Type-class Implicits パターン

  • パターンマッチは全パターンを網羅することができない
  • オーバーロードは汎用性がない

ここでType-class Implicitsパターンを使用してみよう!

trait Jsonable[T]{
  def serialize(t: T): Json
}
object Jsonable{
  implicit object StringJsonable extends Jsonable[String]{
    def serialize(t: String) = Json.Str(t)
  }
  implicit object DoubleJsonable extends Jsonable[Double]{
    def serialize(t: Double) = Json.Num(t)
  }
  implicit object IntJsonable extends Jsonable[Int]{
    def serialize(t: Int) = Json.Num(t.toDouble)
  }
}

こん感じで書いておけば...

def convertToJson[T](x: T)(implicit converter: Jsonable[T]): Json = {
  converter.serialize(x)
}

このようにconvertToJson()を実装できる!!!! convertToJson()のカリー化された引数にimplicitJsonable[T]型を受け取っている。この[T]convertToJson[T]で束縛されている。

例えばxStringであった場合、このような挙動になる(っぽい)。

  1. xStringなのでJsonable[T]Jsonable[String]に束縛される
  2. Jsonable[String]implicitなのでscalaが使えるimplicitがないか捜索の度に出る
  3. まずスコープ内を探す、この場合は定義していないので見つからない
  4. 次はJsonableのコンパニオンオブジェクトを探しに行く、今回はStringJsonableを定義済みなのでここで見つける
  5. scala「お!定義済みだね!通れ!」となる

もし、Jsonable[File]のような未定義のクラスの場合。scala「だめだ!お前は!implicitが見つからないぞ!コンパイルエラーだ」となる。

さらに、前述の def convertToJsonAndPrint(t: String) = println(convertToJson(t)) のような関数を定義した場合も「お!StringからJsonに変換するimplicitがほしいんだね!StringJsonableを使いな!」となる。やったね!

Type-class Implicitsと型クラスimplicitの違い

  • Type-class Implicitsは評価する度に使用するimplicitを探しにいく、ライブラリで実装済のものを使う場合もあれば、自前で実装した物を使用する場合もある。
  • Type-class Implicitsは通常​​、データを保有しており、しばしば変更可能型クラスimplicitは純粋な関数である場合が多い。(まったく理解していない)

Derived Implicits

Type-class Implicitsでは深い階層の呼び出しも型安全に行えるよ!イエ〜イ! という話。

前述のJsonクラスでStrNumの他にJson Listも実装したいとしたらどうすれば良いだろうか?

sealed trait Json
object Json {
  case class Str(s: String) extends Json
  case class Num(value: Double) extends Json
  // ... and more
}

StrNum実装済みであるのでconvertToJson()は成功させたい。しかし、File型などの未実装の場合はconvertToJson()コンパイルエラーにして防ぎたい。

// これは実行できる
convertToJson(Seq(1, 2, 3))
convertToJson(Seq("baz", "bar", "foo"))
// これはコンパイルエラーにしたい
convertToJson(Seq(new java.io.File("."), new java.io.File("/")))

どうすればこの問題を解決できるだろうか? 答えは先ほど追加したJsonableにSeqをserializeするimplicit SeqJsonableを作成。このSeqJsonable内のSeqを[T]型に束縛してしまえばいいのだ。 きっと日本語でおkなのでコードを見てみよう!

trait Jsonable[T]{
  def serialize(t: T): Json
}
object Jsonable{
  implicit object StringJsonable extends Jsonable[String]{
    def serialize(t: String) = Json.Str(t)
  }
  implicit object DoubleJsonable extends Jsonable[Double]{
    def serialize(t: Double) = Json.Num(t)
  }
  implicit object IntJsonable extends Jsonable[Int]{
    def serialize(t: Int) = Json.Num(t.toDouble)
  }
  // このSeqJsonableを新しく追加
  implicit def SeqJsonable[T: Jsonable]: Jsonable[Seq[T]] = new Jsonable[Seq[T]]{
    def serialize(t: Seq[T]) = {
      Json.List(t.map(implicitly[Jsonable[T]].serialize):_*)
    }
  }
}

このdef serialize(t: Seq[T])[T]型の束縛のおかげで、implicit実装済みの型は呼び出せますが、未実装のjava.io.File型などは呼び出し時にコンパイルエラーになります!めでたし、めでたし。

実はDerived Implicitsパターンはこれだけではありません。どれだけネストした型でも型安全に呼び出すことができるのです! これも日本語でおkなのでコードを見ていきましょう!

// コンパイル可能
convertToJson(Seq(Seq(Seq(1, 2, 3))))
// コンパイルエラー
convertToJson(Seq(Seq(Seq(new java.io.File(".")))))

どれだけネストが深くても関係ありません。コンパイラ再帰的に型を判定し[T]型にマッチしなければコンパイルエラーに変換します。 この制約は一見面倒に感じるかもしれません。仮にJsonableが外部ライブラリだった場合、前述のようにjava.io.File型はコンパイルエラーなってしまうため実行できないからです。しかし、これはメリットでもあります。 存在しなければjava.io.File型のFileJsonableを実装すれば、Seq[File]も同様に扱うことができるのです。

// これを作ってあげる
implicit object FileJsonable extends Jsonable[java.io.File]{
    def serialize(t: java.io.File) = Json.Str(t.getAbsolutePath)
  }

// これも実行可能になる
convertToJson(Seq(Seq(Seq(new java.io.File(".")))))

このように型の束縛は実装を制限するのではなく、ユーザー側に拡張性という大きなメリットをもたらします。

Type-driving Implicits

より大きな型に変換しようとすると予想外の動きをすることがあるけど、一つの関数が引数の型に応じて戻りの型を自動で変換してくれた楽チンだよね! という話

ex) Byte -> Short -> Longに変換

// Byteで定義
val x: Byte = 123
x: Byte = 123

// Byte -> Shortに変換
val y: Short = x
y: Short = 123

// Short -> Longに変換
val z: Long = y
z: Long = 123L

これは結構便利。しかし、予想外の動きをする場合があります。

// Longの最大値
val bigLong = Long.MaxValue - 1
bigLong: Long = 9223372036854775806L

// Long -> Floatに変換
val bigFloat: Float = bigLong
bigFloat: Float = 9.223372E18F

// Float -> Longに戻す
val bigLong2: Long = bigFloat.toLong
bigLong2: Long = 9223372036854775807L

// 最初のLongと一度Floatを通したLongを比較
bigLong == bigLong2
> res40: Boolean = false

false!!!!!!!

このような事態を防ぐにはどうすれば良いでしょう?型の拡張を自動で行う関数を定義できます!

{
  def widen[T, V](x: T)(implicit widener: Widener[T, V]): V = widener.widen(x)
  class Widener[T, V](val widen: T => V)
  object Widener{
    implicit object FloatWiden extends Widener[Float, Double](_.toDouble)
    implicit object ByteWiden extends Widener[Byte, Short](_.toShort)
    implicit object ShortWiden extends Widener[Short, Int](_.toInt)
    implicit object IntWiden extends Widener[Int, Long](_.toLong)
  }
}

こんな感じで、与えられた型を自動で拡張するwiden()関数を作成します!さぁ呼び出してみましょう!

// Floatを渡すとDoubleが返ってくる
widen(1.23f: Float)
> res12: Double = 1.2300000190734863

val smallValue: Byte = 123

// Byteを渡すとShortが返ってくる
val shortValue = widen(smallValue)
> shortValue: Short = 123

// Shortを渡すとIntが返ってくる
val intValue = widen(shortValue)
> intValue: Int = 123

// Intを渡すとLongが返ってくる
val longValue = widen(intValue)
> longValue: Long = 123L

戻りの型は最初のようにscalaコンパイラが自動で判断するのではなく、Widenerに定義されているimplicitの実装で決定されます。それだけではなく、widen(longValue: Long)このように未実装な型を渡した場合はコンパイルエラーで防ぐことができます。

このDerived Implicitsは、一般的にTupleを大きなTupleに拡張する時にも用いられます。

{
  def extend[T, V, R](tuple: T, value: V)(implicit extender: Extender[T, V, R]): R = {
    extender.extend(tuple, value)
  }
}
case class Extender[T, V, R](val extend: (T, V) => R)
object Extender {
  implicit def tuple2[T1, T2, V, R] = Extender[(T1, T2), V, (T1, T2, V)] {
    case ((t1, t2), v) => (t1, t2, v)
  }
  implicit def tuple3[T1, T2, T3, V, R] = Extender[(T1, T2, T3), V, (T1, T2, T3, V)]{
    case ((t1, t2, t3), v) => (t1, t2, t3, v)
  }
  implicit def tuple4[T1, T2, T3, T4, V, R] = Extender[(T1, T2, T3, T4), V, (T1, T2, T3, T4, V)]{
    case ((t1, t2, t3, t4), v) => (t1, t2, t3, t4, v)
  }
  // ... tuple21 まで続くよ ...
}

widen()の時と同じで、戻り値の型はExtenderで定義したimplicitに依存します。結果、Tuple2Tuple3はクラス階層では互いに直接関係がありませんので、Tuple2extend()を使用することができ、scalaコンパイラは結果をTuple3であると自動的に判断します。

// Tuple2を宣言
val t = (1, "lol")
t: (Int, String) = (1, "lol")

// Tuple3に拡張
val bigger = extend(t, true)
bigger: (Int, String, Boolean) = (1, "lol", true)

// Tuple4に拡張
al evenBigger = extend(bigger, List())
evenBigger: (Int, String, Boolean, List[Nothing]) = (1, "lol", true, List())

Type-driving Implicitsパターンを使用することで、implicitパラメータのどのインスタンスが有効範囲にあるかに応じて、関数の戻り値の型をscalaコンパイラが推測する方法を制御できる。これは少しクレーバーなテクニックです。戻りの方を制御するのは気づかない可能性もありますし何でもかんでもType-driving Implicitsパターンを使用すれば良いというわけではありません。しかし、効果的に使用すれば非常に有効なパターンになります。

ex) fastparse ではこのExtenderに似た形で実装されています。

最後に

これでimplicitを使用したいくつかのデザインパターンの紹介は終わりになります。しかし、これで全てを説明しきったわけではありません。ここで説明しなかった使用方法を最後に少し言及します。

  • ShapelessAux PatternShapelessではimplicitを使用してできるほぼ全てのことが網羅されています。しかし、ここで全てに言及するのは不可能なので興味があれば除いて見てみてください。
  • implicit parametersの代わりにimplicit conversionsを使用するもの。 ex) implicit constructorsやその拡張クラスを実装するもの。(C#のものに似ている)
  • 前述のCollectionのようなものだけではなく、case classで動作するDerived Implicitsパターンも存在します。Shapelessでも実装されています。
  • Implicit Contextsはよりパワフルに、行番号やファイル名などコンパイラからさらなる情報を取得することもできます。ClassTagsourcecodeで実装されています。

以上です!!マサカリ待っています!

参考

http://www.lihaoyi.com/post/ImplicitDesignPatternsinScala.html

Rakus Meetup Tokyo #1 終わらないスクラム 行って来た

こちら行って来た

rakus.connpass.com

テックブログもあるよとのこと

tech-blog.rakus.co.jp

アジェンダ

  1. 終わらないスクラム ~楽楽精算のサービス拡大を支えるスクラム開発の取り組み
  2. テックリード(自称)としてのやっていき! ~iOSアプリ開発チームを率いて
  3. 流行の開発手法ChatOpsとは ~楽楽明細チーム/ChatOpsでルーティン作業をまるごと自動化~

終わらないスクラム

概要

ラクスでは、まだ多くの開発チームがウオーターフォール型の開発プロセスを採用していますが、 一部のチームでスクラムによるアジャイル開発に取り組んでいます。 今回は楽楽精算チームでの取り組みを紹介します。 実際にやってみると様々な問題が発生しました。問題解決に向けたアクションや取り組みを通じて得た知見、今後の課題等を事例を交えてお話しします。

登壇者

twitter.com

資料

speakerdeck.com

背景

  • 過去プロジェクトや勉強会を通してスクラムを学んだ
  • 今回のプロジェクトからスクラムを導入

フォーターフォールからスクラム

Problem

  • バーンダウンしない
    • めっちゃわかる
  • とにかく会議隊が終わらない

Try

  • タスクをなるべく細分化
  • リファインメントの徹底
  • デイリースクラムの見直し
  • カンバン
    • 物理カンバン
    • キャパシティーを表す写真を貼って、4枚以上はキャパオーバーとか測る
  • インペディメントの除去

知見

  • 与えられた役割を全うする
  • スクラムチームの役割
    1. プロダクトオーナー
    2. 開発チーム
    3. スクラムマスター
  • 役割を全うするのはどのような開発手法でも同じ
  • 短期的なスプリントを何度も繰り返すので従来の開発より振り返り・導入の機会が多い

これからのスプリントでの挑戦

感想

  • スクラムに向かって行ってる感じがしていいなって思った
  • チーム多いなって思った(10人)
  • 1年後くらいに振り返ったら全然違うチームになってそう
  • 物理カンバンのだんだん汚くなるのと付箋の粘着力問題と立ち向かっててすごいなって思った
  • バーンダウンしないのめっちゃわかる。
    • 見積もりにくそ時間かけないと正しく測れない、正しく測れないから後からタスクが湧いて来てバーンダウンがなんなら右肩上がりになる
    • 正しく見積もろうとするとそれだけで数日経つのでコードが書けない。みたいなトレードオフになる気がする

テックリード(自称)としてのやっていき!

概要

若手エースエンジニアが初めてのiOSアプリ開発でテックリードとして奮闘したお話しです。 iOSアプリ開発は、自身も初、メンバーも初、しかも短納期(3ヵ月...)。 このデスマーチを予感させる不利な条件下で、テックリードとしてどのようにチームをリーディングしたのか。 様々な事例を交えてご紹介します。

登壇者

twitter.com

背景

スクラムチーム

  • バックエンド開発チーム 3人
  • iOSチーム 3人 ← ここ
  • スクラムマスター

やっていった

  • 仕組み化
  • コード品質
  • バランスをとる

仕組み化

  • DDD
  • 詳しい人がいないのでそこまでがんばんない

コード品質

  • あえて属人化させて開発速度をあげる
  • スタイルガイドなど導入
  • CIとか外部のやつ
    • なんか聞いたことないやつだった
  • コードレビュー
    • 細かいところまでは見れないので要点を絞ってレビュー

バランスをとる

  • hubになる
  • このへん仕事のSlackきてあんま聞けなかッタ...

振り返り

  • 技術的な課題感
    • 未着手の言語だとさすがに大変そう
  • エンジニアリング頑張りたい

感想

  • チャレンジする感じいいなって思った
  • 「3ヶ月くらいで一気に作ったけどtoilで死んだ」みたいな話をいつか聞きたい
  • php/java -> swiftってどうなんだろ
    • 大変そう(小並感)
  • 挑戦して成果出してアウトプットまで繋げるの偉いなって思った(こなみ)

流行の開発手法ChatOpsとは

概要

Jenkinsの導入で自動化が進んだけど、「Jenkinsを毎回開くのは面倒」、「アカウントの作成も面倒」、「非エンジニアに使ってもらうにはちょっとハードルが高い」。 そこで導入したのがChatOps! Slack互換のチャットツール「Mattermost」でルーティン作業を丸ごと自動化しました。 利用したbotツール、システム構成、Hubotスクリプトの実例など、ノウハウを余すことなくご紹介します。

登壇者

twitter.com

資料

speakerdeck.com

背景

  • 少人数で開発~リリースまでを支える自動化の紹介

環境

  • 開発者がすきなScript書いたり
    • やばそう
  • CI/CDはJenkinsに

問題点

  • Jenkinsだるい
    • だよね

ChatOps

  1. IncomingWebHooks
  2. SlackCommands
  3. OutgoingWebhooks

hubotを使う

  • Slackコマンドのリクエストを受け取るAppサーバー立てるの怠い
  • coffeeじゃなくていい
  • npmのパッケージも作って公開した
    • すごい

gyoza.beer

  • 活用事例は若干コンプラっぽそうだったので割愛

感想

  • サービス外のコーディングは楽しい
    • わかる
  • hubotどこで動いてるんだろ
  • digdag入れたい
  • 今はhubto以外も結構あるぽい
  • Q.意識高い人が作ったものってその人がいなくなるとわからんちん問題はどうしてるのだろう
  • A.まだ引き継いだりとかっていう段階じゃない。でも最高のコードを書いてるから大丈夫!

最後に

  • 次回は来年(2020年)2月ごろだそうです。

Bug Bashを1時間やって他チームに細かいIssueを洗い出して貰いました

株式会社サイバーエージェント AdTechStudio オペレーションテクノロジー事業部 iXamDrive(イグザムドライブ) でサーバーサイド・フロントエンドの開発をしている @HonMarkHunt です。

先日、 @konifar さんの記事 『B1グランプリ(Bug Bash)』を2時間やって細かいIssueを洗い出しました を拝見し素晴らしいこころみだと思ったのでiXamDriveでも挑戦して見ました!

blog.kyash.co

* iXam Driveとは?

インフィード広告の運用自動化ツール

www.cyberagent.co.jp

TL;DR

  • チーム内で触ってバグを見つけるのでは無く他チームのエンジニアにプロダクトを触ってもらいバグや分かりづらい点(UI指摘)を見つけてもらった。
  • 1時間でバグ・UI改善点が27個見つかった

Bug Bash!!

このような流れで行いました!

  1. 事前説明
  2. バグ報告
  3. 採点
  4. 景品授与

それぞれ解説していきます!

事前説明

f:id:HonMarkHunt:20180323192532j:plain:w450

まず、当日のレギュレーションを発表しました。大きく分けるとレギュレーションは以下の3つです!

  1. フロント(画面)サーバーサイドのコード(GitHubリポジトリ)フロントエンドのコード(GitHubリポジトリ)から自由にbag(と思わしきもの)UI/UX的な指摘その他なんでものを見つけて欲しい。
  2. フロント(画面) はStaging環境を使う。
    • 本番環境だと実データに影響を及ぼす危険操作ができてしまうため
  3. フロント(画面) は全機能を自由に触わるのではなく、こちらで指定した機能のみ触ってもらう。
    • 一部昨日はStaging環境でも実データに影響を及ぼす危険操作ができてしまうため

どの機能を触って貰うかミーティングで 危険操作だらけだった ことが判明したのも収穫でした。

バグ報告

  • バグと思わしきものを見つけた方は前に出てプロジェクターに繋いでバグ内容を発表してもらいました。

f:id:HonMarkHunt:20180323230637j:plain:w450

  • 想定の10倍くらいバグだらけだったのでバグ報告の行列もできてしまいました。

f:id:HonMarkHunt:20180323192728j:plain:w450

採点

  • 報告されたバグはその都度iXamDriveのメンバーで採点しました。
  • 4人のジャッジがそれぞれ1~5点までの札を持っていて、ジャッジの合計点数でもらえる景品が変わるという評価基準にしました。
  • 0点の場合は「仕様」という札をあげる。

f:id:HonMarkHunt:20180323193750p:plain:w450

(札は前日にiXamDriveメンバーで割り箸と段ボールで手作り)

f:id:HonMarkHunt:20180323192535j:plain:w350

景品授与

f:id:HonMarkHunt:20180323195056p:plain:w300

  • 報告のたびに採点してその都度点数に応じた景品を持って行って貰いました!
  • 景品用に大量の駄菓子と 机にあったいらない本 技術書を用意しました!
  • ちなみに フォームにHTMLタグをそのまま入力できる という痛恨の初歩的バグ を発見していただいた方が満点20点を叩き出しシークレット商品の iXamDriveの飲み会1回無料参加券 を獲得しました。

f:id:HonMarkHunt:20180323194847j:plain:w450

f:id:HonMarkHunt:20180323195043p:plain:w450

景品交換の様子

終了後

  • Bugは随時記録。
  • 27個ものバグが出ました!!!

f:id:HonMarkHunt:20180326121639p:plain:w400

振り返り

Bug Bashをやってみて良かったこと改善点などを書いていきます。

良かったこと

  • @konifar さんの記事を拝見してから、チームのKPT meetingでBugBash 1週間後の開催が決まりました。開催までの期間が短く、2度のmeetingを経て、他チームの方々への周知用チラシ作成、採点用札作成、景品準備など... ドタバタしながら進めていくのが文化祭みたいで楽しかったです。心なしかチーム力も向上した気がします!!
  • 参加していただいた方々にも楽しんでいただけた。参加者にはアンケートをお願いしたのですが結果がとてもよかったです!!「他チームでも開催したい。」という声があったのは嬉しかったです! f:id:HonMarkHunt:20180323201808p:plain:w400
  • issueがいっぱいできた。 ユーザーテストや単体テストと違い、自身のプロダクトを他チームのエンジニアに触ってもらう機会は中々ないと思います。エンジニアは景品欲しさに能動的攻撃を仕掛けてくるので普段は気づかないバグを発見することができました。

改善点

  • できたissueいつ消化する?問題。 issueはたくさんできたものの開発スケジュールにそんなに空きがあるはずもなく... とりあえず取り組めそうなものはバックログに追加しておいたので、一瞬手が空いた時にサッと着手できるといいかもしれないです。BugBashででたバグは当月中に解消するなどのルールを事前に決めておいてもよかったかもしれないです。
  • Bugの捜索対象をフロント(画面)サーバーサイドのコード(GitHubリポジトリ)フロントエンドのコード(GitHubリポジトリ)と3つ選出しましたが、ほとんどがフロント(画面)の指摘でした。アンケートでも「コードの指摘はそもそも仕様がわからないからハードルが高かった」とありました。機能を絞ったりしてコードも見やすいように手配すれば良かったです。

Tips

  • 発言しやすい空気を作るように心がけました。どんな報告が来てもとりあえず大声を出しました
  • 得点のフダ余ってるんで使いたい人がいたら言ってください。
  • お祭り感を出すためにわざわざチラシを作って他チームに配りました。

f:id:HonMarkHunt:20180323165458p:plain:w450

Shinjuku.LT 第18回 レポ

ShinjukuLT

www.shinjukult.tk

目次

  1. DI with Reader Monad
  2. Search Suiteのシステム構成
  3. 引っ越しを支えるかもしれない技術
  4. ツリー構造のデータをRDBで扱う
  5. 第一回 デザインクイズ
  6. Bug Bashを1時間やって他チームに細かいIssueを洗い出して貰いました
  7. カイゼンジャーニー読んだよ

DI with Reader Monad

f:id:HonMarkHunt:20180324142220j:plain

登壇者

twitter.com

TL;DR

  • 関数単位の細かなDIができる
  • 評価されるまで、DIを遅延させることができる

DI

メリット

  • 疎結合

    • テストしやすい
  • 種類

    • 動的DI
    • 静的DI

SwiftでDI

  • Swinject
  • DIKit

Reader Monad

  • コードがたくさんです! f:id:HonMarkHunt:20180324142217j:plain

感想

  • 心の片隅に覚えておこう。

Search Suiteのシステム構成

f:id:HonMarkHunt:20180324143217j:plain * 全面コンプラ

http://qqqmeisi.com/wp-content/uploads/2018/02/que-1343525833.jpeg

引っ越しを支えるかもしれない技術

f:id:HonMarkHunt:20180324164826j:plain

登壇者

twitter.com

TL;DL

  • 引っ越しで支えるかもしれないいろんな情報

引っ越しにかかるコスト

  • とにかく捨てるものが多い
    • 紙の本をたくさん持ってる
  • ダンボールを送りつけると買い取ってくれるサービス

発送買取によるメリデメ

メリット * 重いものを運ばなくていい

デメリット * 引っ越しで住所が変わるとだるい

家電や粗大ゴミの破棄

  • 家電は捨てれない -> 高い
  • 粗大ゴミは低コストで捨てれるが、予約が必要
  • 地元の引っ越し屋さんの有料回収した
  • ネットのリサイクルショップは反応が遅くて微妙
  • ミニクラ 等で預かってもらっても良かった

物件探し

  • レインズ見れない(見たい)

感想

  • レインズ見たい

ツリー構造のデータをRDBで扱う

f:id:HonMarkHunt:20180324164008j:plain

登壇者

twitter.com

ツリー構造のデータで考慮すること

  • 全体参照
  • 一部参照
  • 挿入
  • 更新
  • 削除

ツリー構造をRDBでどう表現するか

  • 隣接リスト

問題点

  1. 階層が深くなると一度に引くのが大変になる
  2. 分けてクエリを発行してもクエリの発行回数が増える
  3. 挿入・更新は容易だが、削除は(末端以外だと)大変
  4. 隣接リストを用いたツリー構造は ナイーブツリー と呼ばれるアンチパターン

解決策

代わりのツリーモデルを使用する

  • ただしやや複雑になるので注意

1 . 経路列挙

経路列挙

閉包テーブル

  • メモが追いつかなかった

まとめ

  • ツリー構造の隣接リストはアンチパターンになることがある
  • 経路列挙は参照整合性が維持できない
  • 万能っぽいが、構造の難しさとデータが増える

感想

  • 全然知らなかった

第一回 デザインクイズ

f:id:HonMarkHunt:20180324164830j:plain

登壇者

twitter.com

デザインクイズ

クイズで学ぶデザイン・レイアウトの基本

クイズで学ぶデザイン・レイアウトの基本

こちらの本から抜粋してAとBのどちらのデザインが直感的に優れているのかのクイズ大会

大事なこと

  • 誰のためのデザインなのか
  • 何のためのデザインなのか

感想

  • 楽しかった!

Bug Bashを1時間やって他チームに細かいIssueを洗い出して貰いました

登壇者

twitter.com

  • 僕です

資料

honmarkhunt.hatenablog.com

カイゼンジャーニー読んだよ

f:id:HonMarkHunt:20180324173756j:plain

登壇者

twitter.com

  • 小説 + 解説という面白い構成
  • リアルな状況に即したベストプラクティスに即している

感想

  • 「あなたは何をしている人なんですか?」

Shinjuku.LT 第17回 レポ

ShinjukuLT

www.shinjukult.tk

目次

  1. Shinjuku.fitness
  2. SQL Format1
  3. ブランチとCLRを組み合わせて、論理の繋がりを見える化してみる
  4. Lensとは?
  5. NGINX Blogから考えるマイクロサービスのProxy設定
  6. 危機的状況から脱出した話

Shinjuku.fitness

登壇者

twitter.com twitter.com

TL;DR

  • 正しい肉体改造方法

資料

docs.google.com

感想

  • 1時間40分も喋ってしまった。LTとは?

SQL Format1

登壇者

twitter.com

TL;DR

  • SQLフォーマッター作ってみた

様々なSQL文のフォーマット

  • フォーマットが人それぞれ
select
  a
  , b
  , c 
from
  hoge
select
  a,
  b,
  c
from
  hoge
select
  a,b,
  c
from
  hoge
  • 公式フォーマットは存在しない
  • サイモンホリウェルのフォーマットなど

www.sqlstyle.guide

合意をとる

  • 意外と色々めんどくさい...

自動でフォーマットしてくれたら楽!!

  • 作ってみた
1. ファイルパスを引数に指定された`.go`ファイルを書き換える
2. `sql`という変数に格納されている文字列を修正する
  • 手順
  1. astで抽象構文木を抽出
  2. 変数取得
  3. パーサーで解析、フォーマット
  4. 抽象構文木に入れる

抽象構文木 - Wikipedia

デモ

  • ターミナルで見せてくれた

感想

  • ファイル保存時にフォーマット直接書き換えて欲しいナ

ブランチとCLRを組み合わせて、論理の繋がりを見える化してみる

f:id:HonMarkHunt:20180218174722p:plain

登壇者

twitter.com

TL;DR

  • CLRの順番に質問してブランチで図に起こすと発言の意図をぶれずに伝えることができる

宣伝

  • 電車のお悩みを解決するオンラインハッカソンに挑戦して豪華賞品をゲットしよう!

hackathon.val.jp

前置き

  • 複数人での会話/会議はファシリテーションが大変(話が噛み合わない, 突拍子も無いことを言う, etc...)
  • ブランチ」と「CLR」が役に立つのでは

ブランチ

https://www.slideshare.net/NoriyukiMizuno/vol1-66046068/30

  • 原因と結果をはっきりさせる
  • AだからB
  • AでBだからC

CLR

  • 論理性を検証する4つの視点
  • 4つの視点を正しい順番で質問する
1. 明瞭性の懸念
2. 存在の懸念
3. 因果関係の懸念
4. 十分性の懸念

資料

hiiiiiiihikaru.hatenadiary.com

感想

  • 身につけたら一生モノのスキルになりそう

Lens

f:id:HonMarkHunt:20180218181020p:plain

登壇者

twitter.com

TL;DR

  • Lensとは何か?

宣伝

2018.tokyo.builderscon.io

Lensとは?

  • なんかすごいGetter/Setter

普通のGetter/Setterとなにが違うの?

  • ネストしたオブジェクトのある一部の値だけ操作したい

関連Link

資料

speakerdeck.com

感想

  • composeの実装、全然わからなかった

NGINX Blogから考えるマイクロサービスのProxy設定

f:id:HonMarkHunt:20180218174538p:plain

登壇者

twitter.com

TL;DR

  • マイクロサービス化 => プロキシのやること増えた => アプリ/ミドルウェアで解決するの辛い =>

よくある構成

  • リバースプロキシ

    • セキュリティ
    • SSL
    • ロードバランシング
    • キャッシュ
    • etc...
  • マイクロサービスのプロキシはどうあるべき?

  • 従来のアプローチ

  • 3 Models in the NGINX

1. Proxy Model
2. Router Mesh Model
3. Fabric Model

Proxy Model

  • 従来のリバースプロキシの設定 + マイクロサービス特有の機能

Router Mesh Model

  • 1台目のNIGNXで従来のリバースプロキシのような動き
  • 2台目のNGINX(ルーターメッシュハブ)
    • エンドポイントの統合
    • 内部キャッシュ
    • サーキットブレイカー
    • etc...

Fabric Model

  • マイクロサービスコンテナごとにプロキシを配置
  • 全ての通信IOがプロキシ経由

で、具体的にどうすればいいの?

SidecarとService Mesh

  • Sidecar

    • 各サービスとセットでデプロイ
    • 全ての通信がプロキシを経由
  • Service Mesh

    • ≠Fabric Model
  • 新しめのツールがたくさんあるぞ!

f:id:HonMarkHunt:20180218173826p:plain

資料

speakerdeck.com

感想

  • Proxy ModelからService Meshへ移行するぞ!!

危機的状況から脱出した話

f:id:HonMarkHunt:20180218175444p:plain

登壇者

twitter.com

TL;DR

Shinjuku.LT 第16回 レポ

ShinjukuLT

www.shinjukult.tk

目次

  1. キャラクターマーケテティングを考えてみる
  2. Physical Computing2 現実(リアル)を支配ハックする
  3. Elastic Cloudを1年ばかし使ってみたレビュー的な
  4. 入社までにやっておきたい事, 入社してすぐにしたい事
  5. ~歴史から学ぶ~ スニーカー基礎講座Ⅰ

キャラクターマーケテティングを考えてみる

f:id:HonMarkHunt:20180120145035j:plain

登壇者

twitter.com

TL;DR

  • キャラクターマーケティングには多くのメリットがあるが、浸透させてメリットを享受するにはそれなりの準備が必要。キャラクターをどう育てていくかが大事。

動機

  • 業務でキャラクターを作成したのでキャラクターマーケテティングについて考察してみた

キャラクターを作るということ

  • 差別化や認知度UP
  • たくさんのメリット
  • 定着させていくためには...
    • 一定の予算と時間が必要
    • PR
    • 作り込み
    • が必要

成功例

くまモン

http://www.kumamotoiccard.jp/wp-content/themes/kumamonic/images/kumamon.png

  • 新幹線開通にあわせて登場
  • 熊本外の人に向けてSNSなどで拡散
  • ターゲットと時期が明確

人気キャラを作るため

  1. 世界観
  2. 背景
  3. 差別化
  4. 使いやすい

実際に作ったキャラクター紹介

  • コンプラのためお見せできない
  • とても可愛かった

https://iwiz-chie.c.yimg.jp/im_sigghSFFFkARLdX2nNahutOZHw---x320-y320-exp5m-n1/d/iwiz-chie/que-1343525833

所感

デザインできるのっていいナー

Physical Computing2 現実(リアル)を支配ハックする

f:id:HonMarkHunt:20180120145314j:plain

登壇者

twitter.com

TL;DR

  • ブラーバを作る

方向転換する

  • モータードライバ
  • 前進と後進ができるようになった

f:id:HonMarkHunt:20180120150454g:plain

壁に近づいたら後進する

f:id:HonMarkHunt:20180120152056g:plain

所感

自分で掃除した方が早そう

Elastic Cloudを1年ばかし使ってみたレビュー的な

f:id:HonMarkHunt:20180120145040j:plain

登壇者

twitter.com

宣伝

hackathon.val.jp

TL;DR

経緯

  • 障害対応、顧客サポートでELBログを可視化したい
  • ElastiSearchとKibanaよさそう
  • ElastiSearchは管理が大変...
  • Amazon ESとElastic Cloudとの比較

状況

  • 要求: 管理に時間がかからないこと
  • ほぼAWSの構成

比較

f:id:HonMarkHunt:20180120145531j:plain

Amazon ESのメリット

  • 他のAWS製品との親和性が高い
  • IAMでのアクセス制限
  • VPCの対応
  • Kinesisとの連携

Elastic Cloudのメリット

  • ESと同社が作成しているので対応がきめ細やか
  • X-Pack標準搭載
  • クラスタのバージョンアップが容易 違いはそんなになかったがバージョンアップやESの使い勝手からElastic Cloudを採用

結果

  • 安定している
  • version up対応もスムーズ
  • 不満な点は特になし

所感

  • うちはlogentriesです

入社までにやっておきたい事, 入社してすぐにしたい事

f:id:HonMarkHunt:20180120152031j:plain

TL;DR

  • 入社初日から活躍するためにやっておきたいこと

準備しておきたいこと

  1. ビジョン・行動指針の把握
  2. 技術面のキャッチアップ
    • 公開されている記事や動画には目を通す
    • 使用しているライブラリ使ってみる 
  3. ビジネス視点でサービスさわってみる
  4. 中の人とご飯に行く 
    • 事前に親睦を深めておく
  5. お引越し
    • オフィス側に引っ越せるなら是非
  6. ビットコイン買っておく
    • みんなやってるから

最初は...

  • 最初の一ヶ月は「なんでも聞いていい魔法の時間」

入ったあとやりたいこと

  1. ひたすらストーキング
    • Slackの過去ログ
    • ドキュメント, PR
    • 闇雲でなく脳内にインデックスをつくっていく感覚
  2. 内容じゃなくて背景を知る
    • How Why
    • なぜこうなっているのかを意識する
  3. ビジネスドメインの理解

まとめ

  1. ストーキングしよう
  2. Whyを理解しよう
  3. ドメインを理解しよう

~歴史から学ぶ~ スニーカー基礎講座Ⅰ

登壇者

twitter.com

僕です

スライド

docs.google.com

【読書メモ】Scala関数型デザイン&プログラミング 1章 関数型プログラムの基礎

概要

Scala関数型プログラミングを学ぶ本。 関数型と聞くと「ウ"ッ」っとなるプログラマーだが、会社で輪読会のお誘いがあったのと、対象読者が「Javaばっかしやってた人」だったのでなんかできそうだなと思い読んでみことにした。継続的にkotlinをやっているのでScalaで関数型を学べるならこれ幸い。という気持ちもあった。

今回は、1章:関数型プログラムの基礎 のまとめ。

隔週で輪読会をやっているので(可能であれば)毎回ブログにまとめていく

TL;DR

式eがあり、すべてのプログラムpにおいて、pの意味に影響を与えることなく、p内のすべてのeをeの評価結果と置き換えることができるとしたら、eは参照透過です。関数fがあり、式f(x)が参照透過なすべてのxに対して参照透過であるとしたら、fは純粋関数です。

1. 副作用とは

  • 関数型プログラミング -> 純粋関数だけを使ってプログラムを構築すること
  • 純粋関数 -> 副作用のない関数のこと
  • 副作用 -> 単に結果を返すこと意外に何かする関数

副作用の例

  • 変数を変更する
  • データ構造を直接変更する
  • オブジェクトのフィールドを設定する
  • 例外をスローする、またはエラーで停止する
  • コンソールに出力する、またはユーザー入力を読み取る
  • ファイルを読み取る、またはファイルに書き込む
  • 画面上ここに脚注を書きます))描画する

副作用を持つコードの例

副作用

2. 副作用の排除

  • buyCoffeeの際にトランザクションを発生させたくない
    • テスタビリティー
    • 再利用性
  • Chargeを返すように修正
  • n杯のCoffeeの購入に対応したbuyCoffeesを作成
  • ワンライナーでまとめられるよ!

副作用排除

3. 参照透過性

  • どのようなプログラムにおいても、プログラムの意味を書き換えることなく式をその結果に置き換えることができる
  • intToString(5) の結果を呼び出し元において"5"と置き換えても正しく動作する。てきな

  • これは参照透過なのだろうか?

  • chargeはUnit

    • buyCoffeeの戻りはnew Coffee()と同等
    • つまり、buyCoffeeの呼び出し元を全てnew Coffee()と置き換えても、正しく動作(影響を与えない)しなければならない
    • この条件が満たされないので上記buyCoffeeには参照透過性がない。といえる

置換モデル

  • 参照透過性では、関数が実行する全てのことが戻り値によって表される <= 普遍条件!
  • 関数を全て結果に置き換えても同じように計算が進められる。
    • 等価による置換
    • プログラムの等式推論がなりたつ(意味不明)

純粋性

  • 参照透過性が担保されるとめっちゃモジュール性高い
    • 合成可能

4. まとめ

  • 関数型プログラミングとは副作用のない純粋関数だけを用いてプログラミングすること
  • メリット
    • モジュール性向上
    • 推論の容易可