長く続くサービスが
モダンであり続けるためには

フリュー株式会社
角田拓己(@chan_kakuz)

#jjug_ccc #ccc_e4

JJUG CCC Fall 2019 2019/11/23

角田拓己

FURYU Corp. @Kyoto

ServerSide Engineer

  • Java
  • Kotlin

 

Spring/Vue.js/Architecture/Test

Who?

takumiz19

chan_kaku

chan_kakuz

Agenda

  • 弊社サービス(ピクトリンク)の話

  • 今回の対象者とゴール

  • モダンって?

  • 今回の話の結論

  • 改善活動とは?

  • 話のきっかけ

  • どのように改善活動を進めていったのか?

  • 実例

  • まとめ

弊社サービスの話

ピクトリンクについて

プリ画取得サービス

プリ画を使ったSNS機能

コスメなどのプレゼント

筐体口コミ機能

ピクトリンクについて

ターゲット層

中学生〜大学生のGIRLS

会員数

1400万人以上

卒業式、ハロウィン、クリスマスの時期は
アクセスが多い

特徴

JJUG CCCとピクトリンク

ピクトリンクは昔からJavaを使っている

今回の対象者とゴール

対象者

そもそも改善活動ってなに?という方

改善活動していきたいけど具体的に何をすればいいのかわからない方

改善活動に少しでも興味を持ってもらって、チームに共有してもらいたい!

(あわよくば、習慣として根付いたら嬉しいです)

ゴール

モダンって?

現代的である様など

今主流の技術を
使っている状態

そもそもモダンである必要が
あるのか?

あります!!
(個人的な意見)

タイトルを振り返る

モダンであり続ける
必要はあるのか?

あります!!
(個人的な意見)

プロジェクトがモダンで
あり続けるのは大切

なぜか?

自分たちが楽をするため

ドキュメントが残っていたり実例が多い

メンテナンスされている

例えば...

エンジニアの欲求として
新しい技術を
使いたい/取り入れたい

個人的には...

今回の話の結論

日々の努力が重要!!

ただ、維持するのは大変

新卒2年目でもできた
プロジェクトが
モダンであり続ける
ための仕組みを紹介

注意

モダンであり続けること
はあくまで手段である

改善活動とは?

誤りや欠陥、ミスを是正し、より良い状態にする事、行為。(*wikipedia参照)

改善

プロダクトをより良いものにするための試み

良いもの?

開発のしやすさ
(DX、開発スピードの向上)

プロダクトの品質向上
などなど

ユーザ的には変わらないけど、
自分たちが嬉しい

話のきっかけ

その前に対象アプリケーションの利用技術の紹介

言語:Java,Kotlin

フレームワーク:Spring Boot

ビルドツール:Gradle

Spring Boot 1.5.xのEOL

Spring Boot使ってますか?
🙋‍♀️🙋‍♂️

もう1.5.xがEOLになりましたがみなさんどうですか??

対応終わってますか
🤔🤔

1.5.x → 2.0.0 Migration Guide

これ見ながらやれば
余裕だろうと思ってた、、、

ハマりどころが多すぎてとても手こずった

Gradleのメジャーバージョンアップ(3.x→5.x)

Spring Cloud
(FeignClientとか)

デフォルトコネクションプールの変更
Tomcat JDBC → Hikari CP

Spring Boot
マイグレーションの奮闘記

このようにEOL,脆弱性発表があってから対応することがあった

EOLなら前もって
発表されるけど、
脆弱性は急にくる、、、

発表された時に
リソースに余裕があるとは限らない

それなら定期的に
アップデートしていこう!

大切なこと

一気にやろうとせず、
チームにあったスピードで
段階的にやっていくのがおすすめ
(改善専門のチームがあれば話は別)

成功体験があったほうが
モチベーション維持につながる

実例

1.Integration Test
(自動テスト)の導入

背景

ユニットテストはあるけど、API全体のテストとなると
手動で行っていた

毎回テストデータを入れて、
テスト環境にアプリケーションをデプロイして、、、

テストに結構コストがかかっていた

もう少し気軽にテストをしたい

自動テストで
考えないといけないこと

最近流行っているから
やってみるでは動機が弱い

なんのためにやっているのか
目的を明確化しよう

自動化しないほうがいい状況もあったりする

テスト自動化するのにも
コストがかかる

手動テストを全て自動テストに置き換えることは難しい場合がある

手段

Spring Boot Starter Test

Spring Boot Starter Test

・JUnit  :ユニットテスト
・Spring Test & Spring Boot Test:Spring or Spring Bootアプリケーションの結合テスト
・AssertJ: アサーションライブラリ
・Hamcrest: マッチャーオブジェクトのライブラリ
・Mockito: Javaのモックライブラリ
・JSONassert: Jsonのアサーションライブラリ
・JsonPath:Json用のデータアクセスライブラリ

dependencies {
	testImplementation("org.springframework.boot:spring-boot-starter-test");	
}

build.gradle

Integration Test Sample

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(SpringExtension.class)
public class HogeAPITest {

    @LocalServerPort
    private int port;

    private static RestTemplate createTemplate() {
        return new TestRestTemplate().getRestTemplate();
    }

    @Test
    public void 正しいクエリを指定したとき_hoge3にクエリで指定した値の10倍で返ってくる() {
        int query = 10;
        String url = "http://localhost:" + port + "/hoge/fuga";
        UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(url)
                .queryParam("a", query);
        ResponseEntity<SampleResponse> result = createTemplate()
        										.exchange(uriBuilder.toUriString(), HttpMethod.GET, null, SampleResponse.class);
        SampleResponse expected = new SampleResponse() {{
            setHoge1("ほげ1");
            setHoge2("ほげ2");
            setHoge3(100);
        }};
        assertThat(result.getBody()).usingRecursiveComparison().isEqualTo(expected);
    }

}

JUnit4の場合は

@RunWith(SpringRunner.class)

独自の工夫

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(SpringExtension.class)
public class ホゲテストAPITest {

    @Resource
    private リクエスト リクエストは;

    @Test
    public void 正しいクエリを指定したとき_hoge3にクエリで指定した値の10倍で返ってくる() {
        int query = 10;
        レスポンス レスポンスの = リクエストは.次のリクエストメソッドで(HttpMethod.GET)
                .次のリクエストクエリで("a", String.valueOf(query))
                .以下URLに送信する("/hoge/fuga");

        SampleResponse expected = new SampleResponse() {{
            setHoge1("ほげ1");
            setHoge2("ほげ2");
            setHoge3(query * 10);
        }};

        レスポンスの.ステータスコードは次の値であることを検証する(200);

        レスポンスの.ボディは次の型で(SampleResponse.class)
                .次の値と一致することを検証する(expected);
    }
   }

クラス、メソッドなどは日本語で定義

・網羅性担保のため
・レビューのしやすさ

理由

詳しくはこちら

効果

テスト実施工数の削減

気軽にテストを回せるので、リグレッションテストのように使える

例えば
Java → Kotlin

IntelliJ使えば
Convert to Kotlin
できるが、、、

Koltin化しただけなので、
APIの仕様としては変わっていない
元々のIntegration Testを
再利用できる

もし自動化してなければ、、、

手動で
Integration Test
やり直し、、、、

リファクタリングや、
依存ライブラリの
アップデート時
などにも使える

2.継続的アップデートの仕組み導入

背景

EOL,脆弱性発表があってから対応することがあった

導入してみよう

プロダクトの依存ライブラリのバージョン情報をチェックしてくれる

build.gradle

plugins {
  id "com.github.ben-manes.versions" version "$version"
}
./gradlew dependencyUpdates

あとはタスクを実行するだけ

Title Text

- org.springframework.boot:org.springframework.boot.gradle.plugin [2.1.7.RELEASE -> 2.2.0.RELEASE]
https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-tools/org.springframework.boot.gradle.plugin
- org.springframework.boot:spring-boot-gradle-plugin [2.1.7.RELEASE -> 2.2.0.RELEASE]
https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-tools/spring-boot-gradle-plugin
- org.springframework.boot:spring-boot-properties-migrator [2.1.7.RELEASE -> 2.2.0.RELEASE]
https://projects.spring.io/spring-boot/#/spring-boot-parent/spring-boot-properties-migrator

このままだと、
本リリース以外(α版など)も出てしまう

細かい調整

こちらでルールの設定ができる

dependencyUpdates.resolutionStrategy = {
    componentSelection { rules ->
        rules.all { ComponentSelection selection ->
            boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm', 'preview', 'b', 'ea'].any { qualifier ->
                selection.candidate.version =~ /(?i).*[.-]${qualifier}[.\d-]*/
            }
            if (rejected) {
                selection.reject('Release candidate')
            }
        }
    }
}

せっかくだから通知
したい

Gradleのタスクの結果をslackに通知してくれる

build.gradle

buildscript {

    repositories {
    	....
        maven {
            url "https://jitpack.io"
        }
    }
    
    dependencies {
    	...
        classpath 'com.github.Mindera:gradle-slack-plugin:1.0.7'
    }
}

apply plugin: 'com.mindera.gradle.slack'
slack {
    url 'your WebHook URL'
    // 依存タスク
    dependsOnTasks 'dependencyUpdates'
    //slack通知のタイトル
    title 'my app name' 
    // 環境変数とか入れてCIツールからしかこのタスクを実行しないとかできる
    enabled = isCDMachine() 
}

設定

slack

バージョンアップ情報

通知

構成図

効果

自分以外のチームの
メンバーも気にかけてくれるようになった

Integration Testが活きてくる

ライブラリ
アップデート
対応

Unit,
Integration Test回す

テスト環境で
動作確認

リリース

このサイクルが
早くなる

ここにとても
時間がかかる

問題

チケット作るのが
面倒、、、

もうひと改善してみた

チケット作成まで
自動化

構成図

slack

バージョンアップ情報

通知

チケット作成

バージョンアップ情報

 JIRAのREST APIを利用してGradleタスクで
チケット自動生成

参考

Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築
綿引 琢磨 (著), 須江 信洋  (著), 林 政利  (著), 今井 勝信  (著)

Gradle 2.xをベースにしているので
注意が必要

効果

チケットを手動で作る作業がなくなった

今までは気づいた人がチケット作るという運用

その他の改善活動

3.ログ周りの整備
4.E2Eテストのカバレッジあげる
など、、、

まとめ

  • 長く続くサービスが
    モダンであり続けるためには

    ・日々の努力が重要
  • モダンであり続けることは大切

    ・ただし手段と目的を逆にしないよう注意
  • 改善活動の例を紹介

    ・改善活動は大変なことはあるけど誰にでもできる
    ・ぜひチームの皆さんに共有してください!

最後に

エンジニア募集!!

Java,Kotlin

Kotlin

推しポイント!!

  • 京都駅から濡れずにいける!!
  • カンファレンスは社費で!
  • 社内勉強会

こちらから応募して
ください!!

参考

fin.