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

朝Lint活動で細かな技術的負債を返済する

Android

買物情報事業部の八木です。クックパッド特売情報のAndroid部分を担当しています。普段はクックパッドのAndroid版(以後、本体アプリとします)の開発プロセスの中で特売情報の機能を開発しています。

本エントリでは細かな技術的負債を解消する為に本体アプリの開発チームが行っている朝Lint活動を紹介します。

2年近く経つ本体アプリのコードベース

私が買物情報事業部に所属する前は本体アプリを1から書き直すチームで働いていました。書き直し始めたのは2013年10月からなのでそろそろ2年が経とうとしています。2年前に設計された本体アプリは現在ではおよそ17万行を越え、日々どんどん変更が加えられています。

それらの変更の中には残念ながら悪いコードが含まれている場合があります。テストしづらいコードやテストがないコード、レビューに対する場当たりな対応や緊急のbug fixのために追加された汚いコード、スケジュール上むりやりねじ込んだコード、minSdkVersionに対応するためのコード、deprecatedになってしまったAPIの利用、メンテナンスされなくなったライブラリの利用など細かな問題が日々増えていっています。

細かな技術的負債の弊害

技術的負債のうち大きなものはissueを立ててマイルストーンを設定し開発プロセスに乗せるのでそこまで致命的な問題にはなりません。どちらかというと以下のような性質のコードが長く残りがちです。

  • 古い実装方針だが動作は正しいため次に変更する時まで放置されている
  • テストが書けない状態だがテストが書ける状態にする為には影響範囲が大きすぎる
  • 機能追加の為に多くの変更が必要だが定形的なためコピペに近い変更で済んでしまい構造の再設計へのモチベーションが低い
  • Lint警告が出ているが問題は起こっていない

こうしたコードは正しく動作していて致命的な問題をあまり起こさないように見えるため修正へのモチベーションが低く大抵後回しになります(あるいは永遠に対応されません)。こうしたコードを残しておくリスクとして次のような事が考えられます。

  • 本体アプリに初めて変更を加える人が悪い見本を使ってコードを書いてしまう
  • 構造的な問題による新機能の追加コスト、既存機能の変更コストの増加
  • 可読性の低下と歴史的経緯を学習するコストの増加、学習不足によるバグの混入
  • 新しい方針を適用するハードルが上がる

これらは改善したいけど手を付ける暇がない問題として存在しつづけていました。

潜在的なリスクを可視化する

ある時の振り返りで「間違っているけど動くコードが放置されていたため問題が起こった」というリスクがそのままバグになって現れたようなProblemが出てきました。これに対して「まずは潜在的なリスクを可視化する」というTryを行うことにしました。

具体的にはbuild.gradleにLint警告を表示するコンパイルオプションを追加して怪しい警告を見逃さないようにしよう、というものでした。

allprojects {
  gradle.projectsEvaluated {
    tasks.withType(JavaCompile) {
      options.compilerArgs << "-Xlint:all,-serial"
    }
  }
}

これにより、アプリをビルドする度にAndroid StudioのMessagesの部分に大量の警告が出るようになりました。まずはこれらをピックアップして改善する習慣をつけるとよいのではないかと考えました。

f:id:sys1yagi:20150915182349p:plain

Lint警告が邪魔

Lint警告の表示を導入した次の振り返りで「ビルドする度に出てくる大量の警告が邪魔」というProblemが出てきました。たしかに警告をもとに改善点を洗い出すのが目的であれば便利ですが、普段の開発においてはノイズです。特にコンパイルエラーとなった時に警告とエラーが混ざってしまい原因が探しづらくなってしまっていました。また期待していたより改善が促進されていませんでした。

Lint警告の表示そのものは有用なので外したくない、しかし普段邪魔になるのでなんとかしたい、他に良い方法はないか。この振り返り時点での細かな技術的負債に対する感情を整頓するとこのように分解できそうです。

  • 普段の開発の負担は増やしたくない
  • 改善はしていきたいがモチベーションがあがらない
  • 単発の大きな改善ではなく小さな改善を継続的にしたい
  • Lint警告は有用だが毎回出るのはイライラするので消し去りたい

これらの感情をうまくカバーする必要がありそうです。

朝Lint活動へ

「朝練...朝れん...朝りん...朝Lint!」

振り返りのさなか私は適当につぶやきました。Lint警告のイライラをバネに朝にパパッと修正する時間を作ったらいいんじゃないかという発想です。はからずもメンバーから「いいのでは」という反応が得られ、早速Tryすることになりました。

朝Lintの方針は次の通りです。

  • 主に軽微なLint警告を片付ける(ビルドのたびに出てくる警告を駆逐していく)
  • レビューの負担を鑑みつつAndroid Lint、Find Bugsなどの警告や、古い実装方針を片付けてもよい
  • 変更は少量でよい(ある警告をいちどに全て片付けなくてもよい)
  • 実施は任意でよい

それまでおこなっていた警告の中から自主的に問題を探しだす方法の場合どうしても修正を加えるに足るほどの問題なのかと考えてしまって手が止まってしまっていました。朝Lint活動という仕組みを導入した事によって修正への心理的ハードルが大きく下がりました。

朝Lint活動の経過

運用し始めておよそ2ヶ月程度が経過しています。それまでに作られた朝LintのPRはおよそ20件ほどです。まだ細々といった感じですが継続的に改善ができています。以前はほぼ0だったので大きな進歩です。

f:id:sys1yagi:20150915182422p:plain

朝Lint活動は細かな技術的負債への対応に対する各種感情をカバーできているように見えます。

  • 普段の開発の負担は増やしたくない
    • 任意、軽微でよいので気が向いた時にやれる
  • 改善はしていきたいがモチベーションがあがらない
    • 軽微な修正でも改善が重なっていく。朝、気分が乗らないときはサクッとPRして弾みをつけるといった使い方もできる
  • 単発の改善ではなく継続的に改善したい
    • 修正中に他の修正箇所を発見するが翌日に持ち越せばよいと思える。
  • Lint警告は有用だが毎回出るのはイライラするので消し去りたい
    • イラッとして修正したくなる。修正してもよい。

朝Lintの中でbugを発見して潰したり、朝Lintの範囲では修正できない問題の検出ができたりなど良い効果もあらわれていて、習慣として定着しそうな手応えを感じています。

おわりに

以上が本体アプリの開発チームが行っている朝Lint活動の背景と内容です。ここに至るまで様々な試みをしてきましたがなかなかうまくワークしていませんでした。今思えば感情をカバーするという点が重要だったのかな、と思います。皆様も朝Lint活動を試してみてください。

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('http://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/