ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Azure Pipelines で Android アプリの CI をやってみてる

最近 Microsoft から発表された Azure DevOps。 Visual Studio Team Foundation (VSTF) をリブランドしたものだそう。

VSTF のときに試しのプロジェクトを 1 個作ってそのまま放置していたのだけど、せっかくなのでこの機会に触ってみることにした。 まずは CI/CD サービスの Azure Pipelines を使ってみてる。

azure.microsoft.com

やってみた

単純なビルド

GitHubホスティングしている Android プロジェクトの CI として、単に ./gradlew build するぐらいのものを構成するだけならすごく簡単。

Azure DevOps で新しいプロジェクトを作成して、その中の 「Pipelines」 の 「Builds」 を開くと新しいビルドパイプラインの構成を行う画面が出てくる。 Web 上でぽちぽちするとビルドパイプラインの設定が作られてそのまま GitHub の pull request にしてくれた。

ただ、Gradle のビルドファイルだけを見て 「Gradle プロジェクトだ」 って判断されるみたいで、初期の構成は Android 向けではなくて普通の Gradle プロジェクト向けになってしまっている。 なので、「Build Android apps with Azure Pipelines or TFS - Azure Pipelines & TFS | Microsoft Docs」 を見ながら自分で構成をいじる必要がある。

pool:
  vmImage: 'macOS-10.13'
steps:
- task: Gradle@2
  inputs:
    workingDirectory: ''
    gradleWrapperFile: 'gradlew'
    gradleOptions: '-Xmx3072m'
    publishJUnitResults: true
    testResultsFiles: '**/TEST-*.xml'
    tasks: 'build'

『The Android Emulator is currently available only on the Hosted macOS agent.』 とのことで、主には VM image を MacOS にする必要がある *1

自動ビルドの設定

私の場合、初期状態だと GitHub からの web hook が無効になっていた *2。 ビルドパイプラインの設定 (YAML ファイルじゃなくて web 側) の中に 「Triggers」 ってのがあるので、そこで web hook を有効にするとコミットごとなどの CI 実行を設定できる。

テストカバレッジの Codecov へのアップロード

Codecov にテストカバレッジをアップロードするにはコマンドを実行する必要がある。 Bash タスクも用意されているので、簡単に実現できる。

steps:
- task: Gradle@2
  (中略)
- task: Bash@3
  inputs:
    targetType: 'inline'
    script: 'bash <(curl -s https://codecov.io/bash)'
    workingDirectory: ''
  env:
    'CODECOV_TOKEN': '$(codecovToken)'

codecovToken は自分で設定したビルドパイプラインの secret variable で、環境変数として使うにはこのように明示的にマッピングしてやる必要がある。 最初 env プロパティの部分を inputs の中に入れてしまっていて、YAML パースエラーが発生してちょっとはまってた。


雑感

まだビルド実行とカバレッジのアップロードぐらいしかやっていないけど、それぐらいなら (ちょっとハマった箇所もあったけど) 素直に構成できた。 ビルドパイプライン単体で他の CI/CD ツールと比較しての強みはまだ見えてないのだけど、Azure DevOps として他のサービスとの連携がやりやすい点は強みっぽい。

ライブラリなどのキャッシュ周りとか、ビルド環境を準備するのが大変な場合にどうするのか (CircleCI では Docker コンテナ内でのビルドという解決策が提示されたが、Azure Pipelines の方はどうなってるんだろう) というあたりは気になっている。 (まだ調べられていない。)

*1:MacOS エージェントでしか Android Emulator は使えない、と言っていて Android Emulator 以外の Android SDK への言及はないのだけど、少なくとも Ubuntu 18.04 には Android SDK の準備はなさそうだった。

*2:設定画面に、web hook が無効です、みたいなエラーメッセージが出ていたので、もしかしたら普通は有効になっているものなのかもしれない。

Gradle のマルチモジュールプロジェクトで JaCoCo の結果を集計する

Java / Kotlin のコードカバレッジツールとして JaCoCo を使いたい。 Gradle のマルチモジュールプロジェクトでの JaCoCo の導入について記す。

f:id:nobuoka:20180814004742p:plain

(この図は JaCoCo によるコードカバレッジの集計結果の履歴を Codecov で表示した例。)

JaCoCo について

JaCoCo は、JVM 言語のコードカバレッジツールである。 Java Agent によるオンザフライ方式のバイトコード instrumentation によるカバレッジ計測が可能である。

以前、Kotlin のコードカバレッジツールについて書いたので、こちらも参考にどうぞ。

Gradle プロジェクトで JaCoCo を使ったカバレッジ計測を行う

Gradle の JaCoCo プラグインを使うことで、Gradle のプロジェクトで JaCoCo を使ったコードカバレッジ計測を手軽に行うことができる。 ちなみに Gradle 4.9 の段階ではこのプラグインは incubating である。

Junit 5 のテスト実行時にカバレッジを計測する

各モジュールのテスト実行時にコードカバレッジを計測するだけなら非常に簡単で、単に JaCoCo プラグインを適用するだけで良い。

While all tasks of type Test are automatically enhanced to provide coverage information when the java plugin has been applied, any task that implements JavaForkOptions can be enhanced by the JaCoCo plugin. That is, any task that forks Java processes can be used to generate coverage information.

The JaCoCo Plugin - Gradle User Manual

バージョンを表す変数の定義や repositories の定義などは省略しているが、おおよそ以下のようなビルドスクリプトJUnit 5 でのテスト実行時に JaCoCo によるコードカバレッジの計測がなされる。

apply plugin: 'kotlin'
apply plugin: 'jacoco'

test {
    useJUnitPlatform()
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"

    testCompileOnly("org.junit.jupiter:junit-jupiter-api:$junit_version")
    testRuntime("org.junit.jupiter:junit-jupiter-engine:$junit_version")
}

デフォルトでは、カバレッジの計測結果は build/jacoco/test.exec というファイルに出力されるようである。

複数モジュールのコードカバレッジの集計

難しいのはここからで、複数モジュールでのコードカバレッジをどう集計するかで私は結構悩んだ。 JaCoCo の結果集計のためのタスクとしては JacocoMerge というのがあるのだが、これをどう定義するのかが難しい。 (どのサブモジュールのカバレッジを集計するのかを明示的に指定するのであればそれほど大変ではないが、自動で全サブモジュールのカバレッジを集計するためのタスク定義をするのが難しい。)

結論としては、ルートプロジェクトに以下のようなタスク定義を行うことで対応した。 (ルートプロジェクトにも jacoco プラグインを適用している。)

task jacocoMerge(type: JacocoMerge) {
    // ルートプロジェクトの評価時にはまだサブプロジェクトは存在しないので、
    // 各プロジェクトの評価完了時に中の処理が走るように。
    gradle.afterProject { p, state ->
        // ルートプロジェクトと `jacoco` プラグインが適用されていないプロジェクトは除く。
        if (p.rootProject != p && p.plugins.hasPlugin('jacoco')) {
            executionData p.tasks.test.jacoco.destinationFile
            dependsOn(p.tasks.test)
        }
    }
}

大雑把に説明すると、jacocoMerge タスクの executionData に各サブモジュールのテスト実行時のカバレッジ計測結果を渡していき、かつ、jacocoMerge タスクの依存に各サブモジュールの test タスクを追加する、ということをしている。

gradle.afterProject を使っている理由や if 文を使っている理由はコメントに書いてあるとおりである。

Gradle のプロジェクトの評価順については下記ページを参照されたい。

集計結果のコードカバレッジのレポーティング

さらにレポーティングのタスクは、ルートプロジェクトに下記のように定義することで行える。 java プラグインが適用されている全サブモジュールのソースファイルをコードカバレッジのレポート対象に含める場合の例である。

task jacocoMergedReport(type: JacocoReport, dependsOn: [tasks.jacocoMerge]) {
    executionData jacocoMerge.destinationFile

    sourceDirectories = files()
    classDirectories = files()
    gradle.afterProject { p, state ->
        if (p.rootProject != p && p.plugins.hasPlugin('java')) {
            sourceDirectories = sourceDirectories + files([p.sourceSets.main.allJava.srcDirs])
            classDirectories = classDirectories + p.sourceSets.main.output
        }
    }

    reports {
        xml.enabled = true
        xml.destination file("${buildDir}/reports/jacoco/report.xml")
        html.destination file("${buildDir}/reports/jacoco/html")
    }
}

本当は JacocoReport#sourceSets を使いたかったが、内部で gradle.afterProject を使われていて期待する挙動にならなかったので諦めた。

ちなみに上の xml.destination の値は Codecov が期待するレポートファイルの位置である。

サンプル

下記プロジェクトで今回書いたコードカバレッジ取得の方法を行っている。

ちなみに CircleCI でのビルド時にコードカバレッジを取得して Codecov で可視化してる。

参考

読んだ: アジャイル開発とスクラム 顧客・技術・経営をつなぐ協調的ソフトウェア開発マネジメント

開発手法としてスクラムを取り入れているチームに所属しているが、アジャイルスクラムといった手法についてあまり知識を持っていないソフトウェアエンジニア、という立場で本書を読んだ。 本書のカバー袖には 『企業の経営層に向けてソフトウェア開発手法の 「アジャイル」 とその手法の一つである 「スクラム」 を体系的に解説する』 とあるのだが、経営層に限らず、アジャイル的な開発手法を採用して開発プロセスを改善していこうとする人であれば、誰にとっても有益だと思う。

アジャイル開発については、ウォーターフォールとの比較として 「小さなサイクルを回して変化に柔軟に対応しながら開発を進める」 という程度の理解しかなかったので、本書を読んで 「人が知識を運ぶ」 とか 「人と人のコミュニケーションで知識を伝える」、「顧客と協調して開発を進める」 といった、どちらかというと社会的な活動やその意義についての部分が非常に参考になった。

アジャイル開発とスクラム 顧客・技術・経営をつなぐ協調的ソフトウェア開発マネジメント

アジャイル開発とスクラム 顧客・技術・経営をつなぐ協調的ソフトウェア開発マネジメント

本書は 3 部構成になっている。 第 1 部 「アジャイル開発とは何か、スクラムとは何か」 では、ソフトウェア開発手法であるアジャイル開発について、どういうものなのか、なぜそれが必要とされたのか、といったことが説明される。 そして、アジャイル開発の枠組として、スクラムについても詳しく説明される。

アジャイル開発については、「ウォーターフォールのように要件定義や設計、実装という各作業で開発プロセスを分断するのではなく、小さなサイクルを回して完成を目指す」 (すなわち、包括的なドキュメントより動くソフトウェアを重視し、計画に従うだけでなく変化に柔軟に対応していくことを重視する) というものだと思っていたのだけれど、それだけでなく、「個人と対話」 や 「顧客との協調」 といったことにも価値を置く価値観が根底にある、ということを知れたのが良かった。 この価値観は、「アジャイル宣言」 に述べられている。

また、朝会 (デイリースクラム) やプランニングポーカー、アジャイル開発の各種プラクティスについても説明される。 プラクティスとしてはアジャイルの右翼 (開発環境) に属するものと左翼 (チーム環境) に属するものがあり、それらをうまく組み合わせてアジャイル開発の目的である 「ビジネス価値」、「顧客満足」、「市場創造」 といったことを達成する。 ここら辺のプラクティスについては、軽く紹介されるだけという感じなので、実際に現場で実践するのであれば詳細は別の書籍などで学ぶと良いと思う。 次のようなスライドもある。

2 部では、実際にアジャイル開発を導入した国内の 3 社 (リクルート楽天富士通) の事例が紹介される。

3 部では、現在のアジャイル開発では明示的に言及されないような、企業経営とリーダーシップの側面から、アジャイル開発の考察がなされる。

個人的に気になった内容

アジャイルの各種プラクティス

プランニングポーカーは 1 回やったことがあるけど、ちゃんとしたやり方を知らなかったので 「なるほどー」 という感じで読んだ。 他のプラクティスについては大体知ってたけれど、いくつか新しい発見があった。

  • プランニングポーカー
    • 最初にベースラインを決める。 メンバー全員が知っているあまり大きくないタスクを選ぶ。 → こないだプランニングポーカーやったとき、最初のタスクは適当に数字を出してて 「こんなんでいいのかなー」 と思ったりしてた。
    • 見解は一番大きい数字と小さい数字の人が言う。 → 数字が合わなかったら全員が言うものだと思ってたけど、確かにそれだと時間かかりすぎるし、最大と最小の人が言えば良さそう。
    • 3 回で切り上げ。 → まあそんなものか。
  • 朝会でプロジェクトの外部の人の発言するタイミングを制限する。 → 外部の人が、自分がリスクを取るわけでもないのに気軽に口出しする、というのを防ぐ意味。
    • プロジェクト内の人をブタ、外部の人をニワトリと呼ぶことがある話。 ハムエッグを作るのにブタは自分を生命をかけるが、ニワトリは自身の生命に関わらない貢献の仕方をする。
    • ブタとニワトリの話、会社の人もしてた。
  • タスクかんばんのタスクは 2、3 時間程度で終わる粒度が良い。
    • 今のプロジェクトでもタスク粒度には悩んでる。
    • タスクが動くことで進捗状況の共有。 さらに達成感にもつながる。 1 日で動くようなタスク粒度にすべきとのこと。
  • バーンダウンチャート
    • 進捗状況の確認のための質問は 「完了までにあと何ポイント (理想時間) 必要か」 にする。 「何パーセント完了したか」 や 「残り何パーセントか」 ではない。
    • 作業が進むにつれて、見積もりよりも実際に必要な作業が多いことがわかったりするので、残り作業時間を把握すべき。
  • スプリントで完了できたポイント数をベロシティとして、スプリントあたりに進めることができるタスク量の目安とする。
    • 工数をポイントで見積もった場合に、実時間とどう変換するのがいいんだろうなーと思っていたけれど、実時間と変換するよりはスプリントあたりに進めることができるポイント数を把握してれば良い、という感じか。
  • スプリントの成果物はリリース判断可能なもの。
  • ユーザーストーリーには詳細な仕様は書かない。
    • あえてコミュニケーションを発生させる意図がある。
    • これに限らず、アジャイル開発では文書などで伝えられる 「形式知」 だけでなく、「暗黙知」 というところにも焦点を当てている。 人と人とのかかわりで伝えられる知識。
  • ペアプログラミング
    • 15 分おきにぐらいでペアを交代する。 開発のメリハリやリズム。 → 15 分がいいのかどうかわからないけど、まあそれぐらいかなーという気はする。
    • リスクが大きい作業や、クリエイティブな作業はペアで行った方が効率的。 → 設計とかも、設計をきっちり書いてレビューしてもらって大きな直しが発生する、というような状況になるぐらいなら最初からペアでやった方が良さそう。

事例

各社、それぞれ課題があったり良い取り組むをしていたりして興味深い。 他社がやってるからといって表面的に真似ても意味はないけど、どういう考えで各取り組みをしたのかが書かれているので、そこら辺がだいぶ参考になる。

リクルート
  • バーンダウンチャートに上限線を引く。
    • 予めバッファを持たせて、上限線を引いておく。 実績線が上限線を超えそうになったら要件の調整を行う。
    • 上限線を引いておくことで、要件の調整に対して事業部側の理解を得やすい。
  • 出世魚型のドキュメント。
    • ドキュメントは開発フェーズをまたいで使いまわし、更新していく。
    • ドキュメント作成の工数削減と、ドキュメントが分散して齟齬が発生することを防ぐ。
    • 言葉で聞いたら、さもありなん、という気はするけど、実際のところドキュメントどうするかって結構難しい問題ではある。
  • 最初はワンチームマインドの醸成に苦戦した。
    • 見積もりのずれに対する認識。 事業部側は見積もりは初期から変わらないという認識。
    • 意識の変革と共有。 地道な啓蒙や説得。
  • 会議からの持ち帰りの禁止。
    • 「持ち帰って検討」 が開発スピードを遅らせる。
    • 持ち帰って 100 % の精度で回答するのではなく、80 % の精度でいいのでその場で回答する。
    • 残り 20 % に起因する手戻りが発生しても、お互いに怒らないように。
    • 精度を求めすぎて時間がかかる、というのは避けるように自分も意識してはいるけど、それでも時間をかけて検討することは結構多いので、もうちょっとスピード重視に振ってもいいかもなーとか思ったり。
楽天
  • 割り込み作業が多いという問題。
    • スクラムマスター経由で依頼してもらうようにして、優先度などをスクラムマスターが調整。
    • こういうのはスクラムマスターの仕事として重要なものの一つなのだなー、と今のプロジェクトでも感じてる。
  • 新撰組の旗を立ててチームの共通認識にする。 目に見える形になっていることの重要性。
    • 「俺たち新撰組みたいだよな」 というような話をチームでしていて、そこから旗を立てたらしい。
    • こういう、チームの結束を高めるための取り組みの事例はいろいろ聞くけど、じゃあ自分のところは何かできるか、っていうと難しいよね。 旗立てればいいってものでもないし。
  • 横やり作業もタスク化する。
    • 必ずしもそれがいいかどうかはわからないけど、自分が何しているのか共有するためにプロジェクト外のことをタスクかんばんに乗せるのは一つの手ではあるなー。
富士通
  • チーム内は完全ペア作業。 教育効果。
    • 人数が多く人の出入りも激しいので、教育効果の高いことをする必要がある、という感じみたい。
    • ある程度少人数で固定のメンバーでやるなら、いい感じのところでペア作業すれば良さそう。
  • チーム間の Try 共有。
    • 振り返りで出てきた Try をチーム間で共有することで、チームとしての成長を共有する。
    • こういう取り組みはいいなーと思った。
  • 文書で伝えたつもりが伝わってないということもある。 文書ではなく人が情報を運ぶという意識。
    • 後でも出てくるけど、こういう 「暗黙知」 も重視する姿勢がアジャイルっぽい感じだなーと思った。

アジャイル開発とスクラム

アジャイル開発で重要そうなトピック。

  • 多層学習と多能力学習。
    • 個人、チーム、部、会社、という様々な層で学習が起こる。
    • チーム内にエンジニアやデザイナ、営業、といった様々な専門スキルを持つ人間がいるので、自分の専門外のことも学習する。
  • 暗黙知形式知、両方の形式が重要。
    • SECI モデル。 暗黙知形式知にする (文書化) ことで複数形式知をまとめたりできるし、さらにそうしてできた形式知暗黙知として人が取り込む、みたいな感じのモデル。
    • 自転車に乗る乗り方、みたいな体でわかることが暗黙知
    • 暗黙知は人と人が直接やり取りすることで伝えられる。 アジャイル開発ではこういう人と人のやり取りが重要視されていると感じる。
  • 実践知リーダー。
  • PDCA (計画、実行、検査、適応) サイクルの前に共同化。
    • 計画は形式知からくるもの。 イノベーションにはもっと主観的な動機が伴っているはず。 それを共同化 (チームメンバーとの共有) する。
    • 何を作るか、ではなく、なぜ作るか、という思い。
    • 合宿の意義もここにある。

アジャイル開発とスクラム 顧客・技術・経営をつなぐ協調的ソフトウェア開発マネジメント

アジャイル開発とスクラム 顧客・技術・経営をつなぐ協調的ソフトウェア開発マネジメント