読者です 読者をやめる 読者になる 読者になる

sandbox

Scala, Android, Architecture, Management, Service Design あたりを主戦場としております

技術的負債を抱えた状態で技術者がすべきこと

この Qiita のエントリに触発され、元文章の目的はさておき、技術的負債についての自分の考えを書いてみようと思う。
技術的負債の定義や、問題は下記エントリを参照して頂くとして、なかでも最後の「技術者がすべきこと」について、「自分だったらこの様にアプローチするか」ということを書く。

技術者がすべきこと

大前提

開発前にステークホルダー(ここではプロジェクトの責任者とする)に、プロジェクトの性質として何を重視するかを認識、選択してもらう。

  1. 短期的な価値実現を最優先とし、初速を重視ソフトウェアの健全さ、プロダクトの成長速度を犠牲にする
  2. 長期的な価値実現を最優先とし、ソフトウェアの健全さ、プロダクトの成長速度を重視し、初速を犠牲にする

1 を選択するという事について、健全でない状態や、成長速度が犠牲になった状態がどの様なものかを、プロジェクトが走り出す前に十分に認識してもらう必要がある。

…とはいえ、人生は 0 か 1 で語れる様な綺麗な世界ではない。
仮に 2 の認識の元、動きだしたプロジェクトであっても、予測できない現象が発生したり、不完全な人によってコードが書かれる限り、負債というものはどうしても発生する。

負債が目の前にある。
ただステークホルダーはその事実を認識していないか、または理解してもらえない様な場合にどうすればいいのか。

負債を明らかにする為に負債の総量を見積る

元記事でも言及されている様に負債には様々な種類があり、モノにより定量化が困難な負債も存在する。

不確実性の高い新規開発において時間ベースの見積りが意味を無さないのと同様に、大量の不可解なコード、存在しないドキュメント等の負債を抱えたプロダクトがチームに与える影響を絶対的な単位で定量化しようともまず無理だろう。

必要なのは、まさに作業をしている感覚を信頼できるチームによる相対的な見積りを行い、感覚ベースでスクラム等で採用されている 1,3,5,8… 等のポイントで負債の大きさを見積る。 そして、今目の前で曖昧になっている「技術的負債」というものを数値で明確化する。

まずは自分達が負債をどの程度抱えているのか、その負債が与えるインパクトがどれ程か、という感覚を定量化し、チームとして負債を認識する事が重要だと思う。

数値の規模感を共有する為にストーリーにする

負債を相対的に数値化したところで、既に共通認識が形成されているか、余程理解あるステークホルダーでない限り、「ひー、80ポイントもあるんだ。今すぐ返済しよう」とは言ってくれないだろう。

最初に必要なのは、やはり技術的負債がどの様なものであり、それがステークホルダーにどの様な影響を与えるか、これを理解してもらいやすい単位でストーリーとして話せる様に整理する必要がある
※ ストーリーの粒度に関しては、開発内部の負債の整理は細かくやってもいいと思うが、対ステークホルダーの説明としてはかなりざっくり、スクラムでいうエピックレベルでいいのかなとは思う

サービス開発におけるストーリーとは違い、すべてのストーリーに対して詳細に説明する必要はおそらくなく(おそらく負債について全部理解したいという動機がそもそもない)、幾つか内容として理解してもらいやすく、影響が大きいものをピックアップし、それらのストーリーに与えられたポイントの規模感さえ掴んでもらえれば、あとは負債のポイントの総量が明確であれば、「同様の問題がこれだけあるのか…」という風に想像がつきやすい。

もしかすると、理解あるステークホルダーであれば、ここで語られたストーリーの時点で、技術的負債の返済について具体的な計画を話し合える状態になる事もあるかもしれない。

それでもダメならコスト換算する

結局、総量を見積り、ストーリーによって負債が抱える現状と、それが与える影響を共有できたところで、会社として負債を返済することの Go がでないと意味がない。

ありそうなパターンとしてはプロジェクトチーム内ではその問題を共有できても、チーム内だけでは負債を返却するだけの期間をリソースを確保する権限がなく、上層部を説得する様な必要がある場合も容易に考えられる。

その場合は、特定のインパクトある負債を抱えた状態のストーリーをプレゼンテーションするのは同様だが、自分達が見積ったポイントあたりのコストというのも算出し、提示することで、負債がどの様にコスト等のビジネスインパクトを与えるのかを、上層部も納得しやすい理由を作ることができればよいと思う。

コストの算出に関しては、例えば現状のチームのおよそ人件費をベースに、いくつかの負債のストーリーを負債を抱えた状態と、返済した場合の人件費を比較してみて、ポイントあたりのコストを算出し、あとは負債の総量とかけあわせて、負債を抱えた状態で開発を続ける事がいかに経済的な合理性がない事を共有(多少でっちあげる事が)できればいい

共通認識にする

上記に書いた様な事が共通認識として形成されれば、健全な体制を作るのは比較的容易で、負債のポイントあたりのイメージが形成できている為、開発過程で負債が発生しようが、定期的に技術的負債を見積る、共有するというマイルストーンを設けておけば、あとはサービスに要求されるスピードに応じた返済プランをプロジェクトチーム内で検討できればよいし、「負債のポイントが50ポイントに達したら新しい要求は受けつけられない。返却フェーズとする」という様な決まりでもいいだろう。

もし、それでもまた負債が無視され、スピードだけが求められる様なことがあったら、負債が及ぼす影響のストーリーと、数値化されたその時点での負債の総量を示し、再び負債を返却すう必要がある時期だという事を主張するプレゼンテーションの場が必要かもしれない。

人と人が関わる開発においては、そもそも技術的負債という概念や、負債のポイントあたりの規模感などの共通認識の形成こそが重要で、一旦共通認識が形成されれば、コミュニケーションコストが減り、開発効率は上がり、健全なソフトウェア開発ができるのではないかなと思う。

まとめ

以上、技術者がすべきこと、として考えていることをまとめると、

  • プロジェクトを開始する前に、何を優先し、何を犠牲にするか、ステークホルダーに十分に認識を共有する
  • 負債を定期的に観測し、ステークホルダーと共有できる単位で数値化し、ストーリーで語り、理解を得る
  • 技術的負債における影響と、ポイントあたりの規模感を共通認識とし、閾値などを設けて仕組み化する

というあたりかなと考えている。

捕捉

こんな面倒なことせずとも、話して終わればそれでよい。
ただし、チームで相対的なポイントにより負債の総量を常に把握するのはよいプラクティスだと思う。

Scala で IO コンテキストの共有を implicit 以外で解決する方法 (0)

※注: 解のないチラシの裏です。

Scalaレイヤードアーキテクチャを採用し実装する場合、データベース接続等の IO のセッションをどう引き回すか、というのが問題になる。
単純に引数として引き回せば、全レイヤー貫通して DbSession 等の特定の永続化層への依存が生まれるし、かといって永続化層に隠蔽するとアプリケーション層からの柔軟なトランザクション管理が出来なくなる。 以前、実現の方法に多少の違いはあれど、よくありそうな implicit conversion で解決するパターンの記事を書いた。

この話題は少しだけ ScalaMatsuri 2日目の最後の Scaling Scala というアンカファレンスでも話しにでた。

「議論がある」。 確かに。
implicit で引き回すのは、宣言の冗長性と、レイヤーをまたがる依存性の観点などで美しくないと思いつつ、基本は implicit でやっている。
何故なら今の知識レベルではその他の方法は知らない & 思いつかないから。

id:xuwei_k さん曰く、それ Reader Monad でできるよ、ただし、Monad Transformer なども定義したりしないといけなかったり、そこまでチームでやるかといったらやらない、という様なニュアンスのことをそのセッション内で言っていた気がする。
※ 全然違ったらごめんなさい

で、キーワードに反応してなんとなく記憶に残っていた ScalaDays 2014 でやっていた Reader Monad に関するセッションについて言及してみたものの…

ちゃんと見たら全然関係なく静的な DI を Reader Monad でやる方法を解説していただけで、セッション中のコード例でも Context は Implicit で引き回す、という現実を間のあたりにした。
※ 下記の Jason さんもセッション中に「なんか良い方法知ってる?」という様なことを言っていた

僕が求めているのは Reader Monad ではないのか、と思いかけたが、以下のリンクを見て、やはり Reader Monad の考え方でいける気がすると思い直した。

https://groups.google.com/d/msg/scalaz/W6jiZUu5jaU/UBUGZjgnW7MJ

Strictly speaking, you should be using an IO-like Monad to do your database code. But that's OK because you can use ReaderT[IO, Config]

Typically I would define something like (if I understand you correctly):

case class Config(controllers: Controllers, services: Services, repos: Repositories)

Then run your program thru Reader[Config, _]. If you do use ReaderT with IO, then it's useful to do something like this:

type Program[+A] = ReaderT[IO, Config, A]

You can then define type constructors in a module:

object Program { def pointA = .... }

上記の URL に記載されているが、次の新しい道を差し示してくれるヒントが下記の動画かもしれない。

Teaching an old dog new tricks: wrapping an imperative API in a functional one https://skillsmatter.com/skillscasts/4943-teaching-an-old-dog-new-tricks-wrapping-an-imperative-api-in-a-functional-one#showModal?modal-signup-complete

説明では、この人が勝手にだろうけど Trampolining IO と呼んでいて、結局 Applicative functor のことらしいが、まだ見てないので実際のとこなんなのか良く分かってない。

何か進捗あれば次の記事 (1) を書く。

ScalaMatsuri 2014 で「国技と Scala」というタイトルで発表しました

国技と Scala (Japan's national sport and Scala) // Speaker Deck

ドワンゴグループにおける、ニコニコ以外の Scala 開発事例として、日本相撲協会公式アプリのバックエンドに Scala/Play Framework を利用した事例について発表してきた。

言いたかったのは主に2点

  • 長期運用の可能性があり、1万行越えだす様な規模であれば、静的型付けな環境は高い保守性と心の平安をもたらす
  • Scala のプラクティス、ノウハウなどで、既にやっている人にとっては常識となっている様な暗黙知も積極的に公開していき、Scala のエコシステム、コミュニティをもっと盛り上げていきましょうということ(自分もふくめ)

相撲という限られたマーケットであり、スケールさせる為にはとか、分散処理とかの Scala っぽい Reactive Programming 方面な話しではなく、単純に Scala/Play をスマートフォンアプリ開発のバックエンドとして利用した事例について発表した。 その開発する中で考えた設計判断、プラクティス、ライブラリの所感について触れ、基本的には、運用が必要なシステムで1万、2万行越えだすと、静的型付けであることによる保守性の高さは、結果的にコンパイル時間等を払拭できるほどの安全性、生産性、心の平安を生むと思っていて、要は静的型付けである事は非常に価値があって、特にテストが難しいテンプレート(twirl)の静的型付けは素晴しいという事を言いたかった。

また、昔から Scala コミュニティで情報共有している方にとっては当たり前になっているであろうけど、新参者は調べても良く分からない…といった、Scala での例外処理、無停止デプロイや静的コード解析などについても触れ、自身の現時点の考えとして、この様にやればよいのではないか、と考えているプラクティスを紹介した。 そして、話した内容というのはやはりあまり表に出ていない情報が多いので、Scala 新参者は得たノウハウや書いたコードを積極的に書いて、Scala コミュニティやエコシステムの更に発展させる為にもっと盛りあげていきましょうということを偉そうに言った。

ScalaMatsuri スタッフの皆さん、本当にお疲れ様でした & ありがとうございました!

Play JSON Tips

この記事は Play framework 2.x Scala Advent Calendar 2013 の19日目の記事です。

昨日登録状況を見たらまだ空いてる & 下記のツイートを見て某せらさん無双もそろそろキツそうだったので衝動的にポチりました。後悔はしていません。

そして人生において割と飲み会の優先度が高いのでこの記事も既に24時を回っております。すいません。
そんな状況でネタがないので Play JSON まわりの自分が困った小ネタの紹介です。

日付を ISO8601 形式で出力する

日付型で一般的に使われている JodaTime の DateTime を JSON 出力すると、デフォルトで Unix Timestamp なのですが、ISO8601 形式で出力したい事もあります。 Play JSON の Writes.scala に jodaDateFormat という関数が定義されており、それを使えば簡単にできます

import play.api.libs.json._
import org.joda.time._

print(Json.toJson(new DateTime(2013, 12, 19, 0, 0))) // -> 1387378800000

implicit val w = Writes.jodaDateWrites("yyyyMMdd'T'HHmmss.SSSZ")

print(Json.toJson(new DateTime(2013, 12, 19, 0, 0))) // -> "20131219T000000.000+0900"

Writes には他にも jodaLocalDateWrites なども定義されているので、他の日付系の型も同様の方法で対応できます。

値クラスをオブジェクトでなく単一の値として出力する

論理的に単一の値しか持たないものであっても、Scala では型安全性の為や他の理由から値クラスとして定義する事があります。 その場合、普通に JSON コンビネータを使うと JSObject が生成されてしまいますが、以下の様にすれば単一の JsValue として出力できます。

import play.api.libs.json._

case class UserId(id: Int) extends AnyVal
case class User(id: UserId)

implicit val userIdJsonWriter = Writes{ (userId: UserId) => JsNumber(userId.id) }
implicit val userJsonWriter = Json.writes[User]

Json.toJson(User(UserId(1))) // -> res0: play.api.libs.json.JsValue = {"id":1}}

え、何が変わってるか分からないという方向けに普通にやるとこうなるよという例が以下。

import play.api.libs.json._

case class UserId(id: Int) extends AnyVal
case class User(id: UserId)

implicit val userIdJsonWriter = Json.writes[UserId]
implicit val userJsonWriter = Json.writes[User]

Json.toJson(User(UserId(1))) // -> res0: play.api.libs.json.JsValue = {"id":{"id":1}}

尚、例では値クラスで書いていますが、シングルフィールドなのが良く値クラスであるだけであり、仕組み的には値クラスでなくても、あるクラスインスタンスを単一の JsValue として出力したい時に使えます。

いずれの例ももっとおいしい書き方があるかもしれません。
教えてください。

明日の Advent Calendar は今のところ /dev/null さんです。 (誰もいない)

Scala における Repository の実装パターンを考える -模索篇-

この記事は Scala Advent Calendar 2013 12/11 の記事です。
昨日は @chiral さんの Sprayの簡単な紹介 - アドファイブ日記 でした。
ちゃんと見ましたか? 見てね!

で、この記事は Scala で DDD の Repository の実装を書く場合どう書けばいいのか、その一例を考えてみたというお話しです。
(主軸が Scala より DDD な気がするがキニシナイ)

ただし、書いてる人の関数型成分含有率は 1% 位な為、関数型ガチ勢から見た時いろいろ思うところがある可能性は高いです。 マサカリお待ちしております。

DDD 関連の概念については特に説明しないので適当にググってください。

実現したい要件

Repository の実装パターンを考えるにあたり、達成したい要件としては以下の2点。

  1. ドメイン層(Repository)を特定の永続化技術から独立させる
  2. トランザクション管理はアプリケーション層から行う

1. ドメイン層を特定の永続化技術から独立させる

Repository というのは I/O が絡む為、特に何も気にせず設計すると、どうしても I/O 関連の技術詳細への依存が生まれます。
DDD 本で Eric 神は理想の Repository 実装として以下の様に言及しています。

理想は、クライアントから(クライアントの開発者からではないが)内部の働きをすべて隠蔽し、それによってクライアントコードが、データの格納箇所に関わらず同じになることだ。 … 略 … 格納と取り出し、および問い合わせの仕組みをカプセル化することが、リポジトリの実装における基本的な機能である。

この辺りをどう実現するかは Implementing Domain Driven Design や ddd-sample 等でも言及、紹介されているのでさらっと流しますが、依存性逆転の原則を適用して、ドメイン層はインタフェースのみ依存する様にするのが良く見るパターンです。

ScalikeJdbc を利用した Repository を例にするとこんな感じ。

一見すると上手く分割できた様に見えますが、これにはまだ明らかな問題があります。
セッション管理が Repository 内で閉じている為、複数のリポジトリをまたがるトランザクションを管理する事ができません。

1 を実現したが故に、純粋無垢な Repository のインターフェースを介して、どの様にトランザクション管理をすればよいか、という新たな課題が生まれた訳です。

2. トランザクション管理をアプリケーション層から行う

DDD 本で Eric 神はトランザクション管理についても以下の様に言及しています。

リポジトリはデータベースに対する挿入と削除を行うが通常は何もコミットしない。例えば、保存した後にはコミットしたくなるが、おそらくクライアントには、作業ユニット(unit of work)を正しく開始し、コミットするためのコンテキストがある。トランザクション管理は、リポジトリを手を出さないでいる方が単純になる。

この記事は模索編ですし、Eric 神も Repository の実装はインフラストラクチャ層により多岐に渡ると言及している事もあり、正直なところ Scala と、今 Scala 界隈に存在するライブラリでどの様にするのが実現するのが良いかはまだ見えてません。
ただ、ひとつのやり方として Implicit Parameter を使えば、Boilerplate なコードによる冗長性を可能な限り排除しつつも、ドメイン層のインターフェースは永続化の詳細を抽象化したままアプリケーション層からトランザクション管理をする事ができます。

コードにするとこんな感じです。

なんかこんなんでいいんだろうか感はありますが、一応 1,2 共に要件は達成できました。
ただ、まだこれも問題があってトランザクション管理の仕組みは ScalikeJDBC と密結合になっている為、アプリケーション層のコードは永続化の詳細から切り離せていません。
アプリケーション層からも永続化の詳細から完全に切り離すには、トランザクション管理の抽象レイヤも作ればいいとは思いますが、トランザクションのブロック内だけを見ればインフラ層の永続化詳細から切り離せているので、このレベルで割り切るのもありな気はしてます。

最後に重要なことを言っておくと、この様な Implicit Parameter で環境を引き回すのは id:xuwei さんが定期観測されている関数型ガチ勢的にはアンチパターンな様です。

関数型ガチ勢から見たScalaのアンチパターン - scalaとか・・・

正直どんなデメリットや、代替手段があるかコードレベルで全然思いつかないので、そろそろ Monad っておいしそうな名前だよねとか言っている場合じゃないと実感します。

所感

  • DDD は若干ポエムなのでコードレベルに落としこむのが大変。で、Java ベースではなく、今ある技術や考え方を使ってシンプルかつエレガントに実現するプラクティスやパターンを共有する必要性を感じる。
  • Implicit の多用は良くないのは分かっているけど、各レイヤーのグルーとして使うのはアリだと思っている。
  • Scala はプログラマの考え方を広げてくれる素晴らしい言語

なんか最後の方は無理矢理感がありますが、続編はすごいH本をちゃんと理解した後にまたやるかもしれません。 明日は k4200@github さんです!

終わったー! 酒だー!! (現在時刻 12/11 26:30)

Play 2.2 と ScalaTest 2.0 を使う

Play デフォルトのテスティングフレームワークは Specs2 だけど、理解のしやすさ、シンプルさに欠けていて、API の醜さみたいなものが気になってしまったので、よりクリーンな API を保っている ScalaTest が使えるか試してみた。

結果としては、依存性に追加するだけで特に問題なくいける。
以下、内容は適当なテストコードと build.sbt。

ScalaTest 2.0 RC1 with Play 2.2

Jenkins と連携する時も、build.sbt で指定している -u の方の JUnit 形式の XML を指定すれば OK。
Functional Test として場合は running(FakeApplication()) が美しくないのでなんとかしたいところですね。

ADT r20 の新機能について LT したよ

android adt

デモメインの発表用資料なので、あまりまとまっていないという噂もある。

やっぱり、個人的には Java ファイルの保存時の Lint の自動実行が一番嬉しいかな。
逆に言うと他はわりとどうでもいい。