Legacy code
from day one

Jakub Kozłowski - Scala Developer, Scalac



Scala Matsuri | March 17, 2018 | Tokyo, Japan

1日目からレガシー化

Disclaimers

  • Only a handful of ideas for messing up a project
  • No offence meant for anybody
  • Some over-exaggerated examples
  • I have nothing against MongoDB

プロジェクトをダメにする方法を紹介します
特定データベースへの悪気はありません

Schedule

  • Code review
  • Development
  • Maintenance

コードレビュー、開発、メンテ

Code review

What is it?

Code review

Don't do it

  • Your code is perfect
  • Your formatting is consistent
  • So is your naming convention and coding style
  • Less time wasted on the process
  • More time == more code == more features
  • Nobody feels judged

...unless you're the one judging

then invite everyone from the company to take part

コードレビューはしなくてもいい

Code review rule 1

Focus on the least important things

ルール1: 重要性が最低のことに集中する

Code review

"Flexible formatting"

「柔軟性のあるフォーマット」

Code review

"Technology agnostic" spaghetti

「技術非依存」スパゲッティー

Code review

"Optimize early" and prematurely

「早すぎる最適化」

Code review

"Optimize for reusability" and never practice it

「再利用のための最適化」

Code review

"Simplicity first" boilerplate next

「シンプルさファースト」というボイラープレート擁護

Development

Development

  • Scaling teams
  • Reinventing the wheel
  • Ignoring the type system
  • Ignoring warnings

開発におけるコツ

Development

Known fact: teams scale linearly

Grow the team until its velocity satisfies you

...or you're out of money

チームは線形にスケールする

Development

Roll your own


Case study: External APIs

独自実装する

Use existing library?

  • you may not like the creator
  • it might break compatibility
  • might stop being maintained
  • the code could be deleted
  • potential security vulnerability
  • we don't understand the code anyways

外部ライブラリは作者のことが好きじゃないかもしれない
互換性が壊れるかもしれない

Roll your own!

  • you can write more code
  • fully control the release cycle & artifacts
  • unlimited support
  • you can add any functionality you like
  • no compatibility issues - just release and update all its dependants

独自実装ならばリリースを制御できる

Development

Ignore the type system


Case study: MongoDB (de)serialization

型システムを無視する

MongoDB (de)serialization

sealed trait MongoValue

case class Booking(id: BookingId, name: String, time: ZonedDateTime)

//use some typeclass?
def toBooking(mongo: MongoValue): Option[Booking] = ???

MongoDB のシリアライゼーション

PoC: typeclasses

Sample AST (assume BSON == JSON for simplicity)

sealed trait MongoValue extends Product with Serializable
case class MongoObject(value: Map[String, MongoValue]) extends MongoValue
case class MongoArray(value: List[MongoValue])         extends MongoValue
case class MongoString(value: String)                  extends MongoValue
case class MongoNumber(value: Double)                  extends MongoValue

型クラスを実装してみる

PoC: typeclasses (continued)

trait MongoReader[T]{
  def read(from: MongoValue): Option[T]
}

implicit val readLong: MongoReader[Long] = from => from match {
  case MongoNumber(num) => Some(num.toLong)
  case _                => None
}
//assume instances for field types exist
implicit val readBooking: MongoReader[Booking] = from => for {
  id   <- from.readAs[BookingId]("id")
  name <- from.readAs[String]("name")
  time <- from.readAs[ZonedDateTime]("time")
} yield Booking(id, name, time)
def toBooking(mongo: MongoValue): Option[Booking] =
  implicitly[MongoReader[Booking]].read(mongo)

Too complicated!

複雑すぎる!

Typeclass drawbacks

  • Complex pattern based on rocket science
  • And even harder - implicits
  • Instances must be written by hand - boilerplate
  • Or generated! 😱


so let's write simple boilerplate instead

型クラスは高度に複雑なパターン
インスタンスは手書きもしくは生成される必要がある

MongoDB (de)serialization

//companion omitted for brevity
class DBObject(internal: Map[String, AnyRef])

//definitely better than implicits
class Booking(obj: DBObject) {
  private def idOpt = obj.getAsOpt[String]("id")

  def id = idOpt.get //surely safer than nulls

  def name = obj.getAs[String]("name")

  def name_=(value: String) = obj.set("name", value)

  def time = ZonedDateTime.parse(
    obj.getAs[String]("timestamp")
  )

  def time_=(value: ZonedDateTime) =
    obj.set("timestamp", value.toString)
}

MongoDB (de)serialization

var booking = new Booking(DBObject.empty)
//lenses for free!
booking.name = "hello world"
booking.time = ZonedDateTime.now()

repository.save(booking)

//pretty sure it won't throw, but if it does, it's fine
//"let it crash" behavior
println(booking.id)

Development

Ignore warnings

type Click = String
def matches(c: Click, str: String) = c == str

Later...

- type Click = String
+ case class Click(value: String) extends AnyVal

<console>:2: warning: comparing values of types
Click and String using `==' will always yield false
       def matches(c: Click, str: String) = c == str
                                              ^

警告は無視するべき

Ignore warnings

Compilation finished with 0 errors and 94 warnings.

大丈夫だから

Maintenance

Maintenance

  • Document nothing
  • Release rarely
  • Customize by default
  • Couple tight
  • Refactor in batch
  • Never change

メンテナンスのコツ

Maintenance

Document nothing

  • Requirements never change
  • Team members never change
  • Tools never change

Nobody will ever need to modify your code

要求仕様は変わらないので文書は書かない
チーム構成も変わらないし

Maintenance

Release rarely


  • Delay bugs until later
  • Invest less in deployment needed
  • Don't stress out about a release every day

リリースはたまにでいい

Maintenance

Customize by default

  • Weird branching model (who needs GitFlow)
  • Custom deployment stack
  • Customer-specific logic in code
  • Custom tools/wrappers for everything

デフォルトで魔改造
カスタムブランチ、カスタム開発スタック

Maintenance

Couple tight


  • Mix layers
  • Avoid abstraction over utilities

密結合
レイヤーは混ぜる

Maintenance

Refactor in batch


  • "We'll fix it later"
  • Refactor everything at once

リファクターは後でまとめて
同時に色々リファクタリングする

Maintenance

Never change

変わらなくてもいいよ

Maintenance


"It is not necessary to change. Survival is not mandatory."
- W. Edwards Deming

変化は必須ではない。生存は義務ではないのだから。

Summary

  • Lots of ways to mess up
  • Make sure to research and try all of them
  • Find more!

/s

ダメになる方法は色々あります

Thank you!

Questions?

Slides: kubukoz.github.io/legacy-day1


Contact me:

@kubukoz

| kubukoz@gmail.com

| kubukoz.com

ありがとうございました