Upgrade to Pro — share decks privately, control downloads, hide ads and more …

大規模改修の裏でTypeScriptとテスト導入をすすめた話

 大規模改修の裏でTypeScriptとテスト導入をすすめた話

Amon Keishima

June 05, 2020
Tweet

Other Decks in Programming

Transcript

  1. ⼤規模改修の裏で
    TypeScriptとテスト導⼊をすすめた話
    LINE Growth Technology株式会社 UITチーム
    けいしま あもん

    View Slide

  2. ⾃⼰紹介
    名前
    Twitter @pittanko_pta
    所属 LINE Growth Technology株式会社 UITチーム
    (けいしま あもん)
    慶島亜⾨

    View Slide

  3. 今⽇お話すること
    担当しているLINEポイントクラブで
    メインタスクの合間をぬってTypeScriptやテストの導⼊をしている話
    現状 作戦 やったこと 知⾒やハマり
    の順でお話していく

    View Slide

  4. LINEポイントクラブとは
    過去6ヶ⽉間に獲得した「LINEポイント」の総量に応じて決まる
    4段階の「マイランク」ごとに、毎⽉LINEの各種サービスが
    お得に利⽤できる、様々な特典を受けられるプログラム
    「LINEポイント」は
    「LINE」の各種サービスの利⽤や、LINE公式アカウントの
    友だち追加、動画視聴などで貯めることができる
    前⾝はLINEフリーコインと呼ばれるもので
    LINEの中でも⻑く続いているサービスのひとつ
    ・実はWebベース
    ・Vue.jsで作られている

    View Slide

  5. 現状

    View Slide

  6. プロジェクトが抱えていた問題点
    2013年にスタートしたサービスであるため…
    テストコードが書かれていない
     ・Karmaが導⼊だけされているものの、テストコードがない
     ・しかし、リリースの度にQAを⾏うので品質は担保できていた
    いろんな環境が古い
     ・Webpackのバージョンが古い
     ・Babelのバージョンが古い
    サブプロジェクトとメインプロジェクトのビルド環境が違う
     ・Browserifyでビルドしている(gruntも使ってた)
     ・module.exports / requireを使っており import / export ではない
    QA: Quality Assuranceのこと

    View Slide

  7. ⻑期的にその問題点を考える
    今まで⻑くやってきたプロジェクトなので仕⽅ない⾯もありますが
    今後も⻑く続いていくだろうことを考えると…
    新しく⼊ってくるメンバーが古い技術を勉強することよりも
    モダンな技術の知識で
    即戦⼒になれるような環境
    を整備するべきと考えた

    View Slide

  8. 問題点を解決するために…
    ・TypeScriptを導⼊すること
    ・テストを本格的に導⼊すること
    の2点はマストだと考えた

    View Slide

  9. TypeScriptへのモチベーション
    コードに型がつくことで潜在的なミスを減らすことができる
    ・ビルドをする段階でミスに気づけるので、サービスの品質向上につながる
    ・コードレビューの負荷を軽減(しょうもないミスは事前に解決できそう)

    View Slide

  10. テスト導⼊へのモチベーション
    今後やりたいコードのリファクタリングを安⼼して⾏いたい
    ⼩さいバグを減らすことができれば、QAのコストも下がるかも

    View Slide

  11. 作戦

    View Slide

  12. 短期・中⻑期的な話
    古いプロジェクトで機能追加案件も並⾏する場合
    TSを導⼊して、テストを導⼊するのは時間がかかってしまうもの
    その中で早期に環境を整え、運⽤に組み込み基盤を作るのがプロジェクトでの最⼤の課題
    そのために必要なのは、中⻑期を⾒据えた上での作戦(計画)を⽴てることだと思う
    短期でTS運⽤基盤を構築することと、
    中⻑期でのプロジェクトに適応させていくことが⼤きな鍵といえる
    今回はまず、短期でのTS運⽤基盤の構築に注⼒した

    View Slide

  13. 作戦
    TypeScriptの環境を導⼊ / Vue.js の TypeScript対応
    Jestの導⼊ / Babelのアップデート / Webpackのアップデート
    脱Browserify / 脱grunt など…
    やりたいこと(やらないといけないこと)はたくさんあるが、
    ⼀気にいろいろ変えてしまうとコードの差分が⼤きくなってしまう
    動作を確認するにも時間かかるし
    レビュワーの負担も⼤きくなってしまう
    今回の作戦は なるべく⼩さく⼊れること とした

    View Slide

  14. (余談)社内の他案件の状況や傾向
    LINEとしては2017年の上半期からTypeScriptの対応をリードエンジニアが始めている
    新規に開発するプロジェクトではTypeScriptの採⽤に積極的な⼀⽅で
    古くから存在するプロジェクトでは導⼊が遅れているようなものもある
    TypeScript導⼊済みのプロジェクトを⾒てみると…
    社内サービスやSDKといった
    社内で閉じたもの や ユーザーが直接⽬にしないもの であることが多い
    ユーザーが直接に⽬にするものついては…

    View Slide

  15. やったこと

    View Slide

  16. まずはTypeScriptをコンパイルできるように
    ・既存のjsファイルのビルド結果を変えないように
    ・JavaScript → TypeScript に変換したものや
     新規で書いた TypeScript のコードだけが正しくトランスパイルできればOK
    ・ビルド時間の増加について⼀旦スルー
    ・Vueのファイルについても⼀旦考えない
    以下の⽅針でまずははじめてみる

    View Slide

  17. まずはTypeScriptをコンパイルできるように
    1. TypeScriptのビルド環境を整えつつ、1つだけ⼩さめのファイルをTS化した
     ・ビルド⽣成物を⾒て、そのファイル以外の差分がないことを確認した
    2. tsconfig の allowJS オプションを有効にした
     ・TS→JSの参照の場合、jsDocがちゃんと書かれていれば 型チェックをしてくれる
    /**
    * @param {Number} id
    */
    export function getArticle(id) {
    //

    }
    import { getArticle } from 'foo'

    export function doSomething(id: string) {
    const article = getArticle(id)
    }
    foo.js bar.ts

    View Slide

  18. まずはTypeScriptをコンパイルできるように
    3. tsconfig の target を es2019 にした
     → ts-loader→babel-loaderを通すときに、tsでトランスパイルをしてほしくなかった
     → spread-arrays や rest-spread は、今まで通りbabelでpolyfillを⼊れてほしい
      
       → tsのトランスパイルで差し込まれるpolyfillで挙動が変わるかもしれない
         みたいな⼼配をしたくなかった
    // spread-arrays
    const nums = [ 1, 2 ];
    const newNums = [ 0, ...nums ];
    console.log(newNums); // [ 0, 1, 2 ];
    // rest-spread
    let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
    console.log(z); // { a: 3, b: 4 }

    View Slide

  19. VueのSFCファイルをTS対応にする
    ・Vueの型定義を追加
    ・まずはVue.extendを使う形に書き換えてみた(1ファイルだけ)
     → 差分が出るので、ここは⾃分で動作確認。問題ないことを確認
    declare module '*.vue' {
    import Vue from 'vue';
    export default Vue;
    }
    export default Vue.extend({
    props: {
    propA: {
    type: Number
    }
    }
    })
    export default {
    props: {
    propA: {
    type: Number
    }
    }
    }

    View Slide

  20. VueのSFCファイルをTS対応にする
    import { Vue, Component, Prop } from 'vue-property-decorator'

    @Component
    export default class FooComponent extends Vue {
    @Prop(Number) readonly propA: number | undefined
    }
    export default {
    props: {
    propA: {
    type: Number
    }
    }
    }
    "vue-class-decorator" や "vue-class-component" があるが、導⼊を⾒送った
    ・書き換え量が多いこと
    ・Vue3のRFCではrejectされていること
     → これは特に⻑く続いているプロジェクトとして気にした
       また⼤規模に書き換える必要がありそうなら、ここでやることはないと判断
    Example: vue-class-component

    View Slide

  21. ビルド時間の短縮
    TypeScriptを導⼊してから、ビルドに倍以上の時間がかかるように
    "fork-ts-checker-webpack-plugin" を導⼊して、型チェックのプロセスを分離した
    Before / 117s After / 41s
    ts-loader
    fork-ts...
    ts-loader
    ts js ts js
    type-check & transpile
    type-check
    transpile

    View Slide

  22. ビルド環境のシンプル化
    ts-loaderでJavaScript / TypeScript 両⽅ビルドするようにした
     → 数秒ビルド時間が伸びる程度・環境のシンプルさを優先
    module.exports = {
    module: {
    rules: [
    {
    test: /\.js$/,
    use: babelLoader,
    },
    {
    test: /\.ts$/,
    loader: [ babelLoader, tsLoader ],
    }
    ]
    }
    }
    module.exports = {
    module: {
    rules: [
    {
    test: /\.(js|ts)x?$/,
    loader: [ babelLoader, tsLoader ],
    }
    ]
    }
    }
    Before / 41s After / 45s

    View Slide

  23. テストの導⼊
    QAをしてもらえるとはいえ、しょうもないエラーを出すのは申し訳ない
    ⼀定のクオリティーを担保するためにテストを導⼊した
    ・KarmaからJestに移⾏した
     ・windowオブジェクトを使うテストが書きやすい
      → location.href
    を変更するようなコードとか
     ・jest.resetModules()
    が便利だった
      → コード中の変数でキャッシュしているコードなど
    ・新しく書くコード(特にユーテリティ系とか)を中⼼にテストを追加した

    View Slide

  24. 知⾒やハマり

    View Slide

  25. リファクタ / コード修正したい気持ちはグッとこらえる
    書き換える時点で「このコードおかしくない?」とか「改善できそう!!」 
    など、気づく点がいっぱいあると思う
    ⼀緒に着⼿してしまうと
    ・レビューのコストが上がる
    ・ts化とコード改善を⼀緒になると、問題が起きたときの切り分けしづらくなる
    みたいな問題があるので、⼼を⻤にしてコード⾃体の改善はしないようにした
    コード中にコメントを埋め込んだり、PRにメモを残すようにして
    別のコミットやPRとしてコードの改善は⾏った

    View Slide

  26. TODO: any で "攻めながらanyを使う"
    js → ts に拡張⼦を変えただけでビルドが通らなくなるケースはあるはず
    そんなときにTODO型を使うと
    ・とりあえずビルドを通すことができる
    ・やるべき型をメモしておけるので、anyより便利
    //
    内部的にはany
    でエラーを回避しつつ、本来当てたい型をメモしておける
    const foo: TODO = getArticles();

    const foo = getArticles(); //
    なんかエラー出るんだけど!!!


    View Slide

  27. Vueの型解決エラーはComputedの返り値を書け
    ・VueのSFCをTS化する最中に、どうしても
     this.hogehoge の型が解決できないときがある
    ・こういうときはcomputedの返り値を明⽰的に書くことでほとんど解決した
    computed: {
    getOpacity(): number {
    return this.isActive ? 1 : 0;
    }
    }
    注釈: 循環参照の都合上こうなってしまうようです。詳しくは公式ドキュメント参照
    → https://jp.vuejs.org/v2/guide/typescript.html#戻り値の型にアノテーションをつける

    View Slide

  28. まとめ

    View Slide

  29. まとめ
    ・TypeScriptは段階的に導⼊することができる
    ・いきなり全部TSに書き換える必要はない
    ・可能なら⼀緒にユニットテストも書くとより安⼼かも
    ・プロジェクトに⼊ったばかりだからこそ、⾔える/思うようなこともある
     ・⾃分が困ったことは、今後新しくプロジェクトに⼊るメンバーも困るはず
     ・昔の技術を勉強するより、持っている最新の技術スタックで
       即戦⼒になれるほうが双⽅ハッピーなはず

    View Slide

  30. お知らせ
    LINEやLINE Growth Technologyで働くことに興味のある⽅向けに
    社員と直接情報交換ができるカジュアル⾯談を実施しています
    興味のある⽅は、「LINE Developer meetup」で検索をお願いします!

    View Slide

  31. ありがとうございました!

    View Slide