フォトシンス エンジニアブログ

株式会社Photosynth のテックブログです

CIツールの活用によるチーム課題の改善

この記事は Akerun - Qiita Advent Calendar 2024 - Qiita の 12日目の記事です。

こんにちは、Miwa Akerun Technologiesが運営する賃貸住宅管理システムの開発チームでエンジニアをしている島田です。

このチームでは、CIツールにCircleCIを利用しています。

今回はCIの設定(.circleci/config.yml)を見ながら、これまでのチームの課題とその改善活動を振り返りたいと思います。

前提

CI設定の説明をする前に、アプリケーションの説明をしたいと思います。アプリケーションはSPAから利用されるREST APIです。

利用している技術スタックの概要は以下になります。

当初のCI設定

私がこのプロジェクトにジョインした当時、CIが実行していた内容はザックリと以下になります。

  • セットアップ
  • Lintを実行する
  • テストの実行とカバレッジの保存をする
  • CIが失敗した場合はSlackに通知をする
version: 2.1

orbs:
  go: circleci/go@1.11.0
  slack: circleci/slack@4.9.3

jobs:
  build:
    docker:
      - image: cimg/go:1.23.1-browsers
        environment:
          ...
      - image: cimg/mysql:8.0
        ...
    environment:
      ...
    steps:
      - checkout
      - go/mod-download-cached
      ...
      - run:
          name: setup tools
          command: |
            sudo apt update -y
            sudo apt install -y default-mysql-client
            ...
            go install -tags 'mysql' github.com/golang-migrate/migrate/v4/cmd/migrate@v4.18.1
            go install github.com/jstemmer/go-junit-report/v2@v2.1.0
      - run:
          name: "Setup Database"
          command: |
            mysql -h ${TEST_DATABASE_ENDPOINT} -P ${TEST_DATABASE_PORT} --protocol tcp -u ${TEST_DATABASE_USERNAME} -p${TEST_DATABASE_PASSWORD} < db/init-database.sql
            migrate -database="mysql://${TEST_DATABASE_USERNAME}:${TEST_DATABASE_PASSWORD}@tcp(${TEST_DATABASE_ENDPOINT}:${TEST_DATABASE_PORT})/${TEST_DATABASE_NAME}?x-tls-insecure-skip-verity=true" -path="db/migrate" up
      - run: golangci-lint run
      - run:
          name: create a coverage report
          command: |
            mkdir -p ./coverage
            go tool cover -html=./coverage/gotest.profile -o ./coverage/cover.html
      - store_artifacts:
          path: coverage
      - store_test_results:
          path: coverage
      ...

      - slack/notify:
          event: fail
          channel: XXXX
workflows:
  build-workflow:
    jobs:
      - build:
          context: slack-secrets

APIドキュメント

APIのドキュメントはOpenAPI Specification(OAS)のフォーマットで管理しています。

APIドキュメントの利用目的は、

  • API実装前の設計レビューのため
  • 他チーム(他社を含む)と連携をする際にI/Fを説明するため

改善前は、APIドキュメントを別なリポジトリで管理をしていました。

そしてローカルで確認するために、Redocly CLIを利用していました。

課題

以下のような課題がありました。

  • レビューの負荷:新規でAPIを実装する際に、設計のみをレビューをするには問題ありません。しかし既存APIの軽微な修正の場合にドキュメントと実装を別々にレビューするのはレビュアー・レビューイーともに負担がありました。またドキュメントのレビューをマージし忘れることも度々ありました。
  • 保守の負荷:リポジトリを管理する保守の負荷(Redoclyを利用するためのNodeのバージョン管理など)がありました。

改善案

まず、APIソースコードと同一リポジトリーにドキュメントを移行しました。

CIでRedocly CLIを実行して、実行結果のHTMLをビルドアーティファクトとして保存するようにしました。

ローカルでの確認時にNodeを必要としないように、VSCodeの拡張「Redocly OpenAPI」を利用して確認する運用にしました。

これにより以下の改善を実現する事が出来ました。

  • コードレビューの負担軽減
  • (ドキュメントの)マージ漏れがなくなる
  • (ドキュメント)リポジトリの保守コストがなくなった
version: 2.1
...
jobs:
  build:
...
  doc:
    docker:
      - image: cimg/node:22.11.0
    steps:
      - checkout
      - run:
          name: install dependencies
          command: npm i @redocly/cli@latest
      - run:
          name: generate docs
          command: |
            npx @redocly/cli build-docs doc/api/openapi/api.yml --output api.html --config=doc/api/openapi/redocly.yml
      - store_artifacts:
          path: api.html
          destination: docs/api
workflows:
  build-workflow:
    jobs:
      - build:
          context: slack-secrets
      - doc

ディレクトリ階層

doc
└── api
    ├── openapi
    │   ├── api.yml
    │   ├── components
    │   ├── paths
    │   └── redocly.yml

ER図

DDLの実行には、Go製のgolang-migrateというマイグレーションツールを利用しています。

課題

テーブルの全体像を知ることが出来ないという課題がありました。

Ruby on Railsであれば、schema.rb で全てのテーブル・インデックスを確認することができます。

しかし私たちのプロジェクトで利用しているmigrationツールでは、Up・Downの差分実行のコードしかないため全体像を確認する術がありませんでした。

もちろん、DBのクライアントツールを利用して開発環境などへ接続すれば確認することは可能です。ただ、スキーマ変更のDDLレビュー中などにクライアントツールを起動せずにサッと現状のスキーマや全体像を確認出来るようにしたいと考えていました。

改善

schemaspy を利用するようにしました。

schemaspyに決めた理由は、テーブル一覧・詳細、ER図の視認性が良い事とそれをHTMLで生成出来る事が決めてとなりました。

APIドキュメントと同様に、CIで実行して生成されたスキーマ定義に関するHTMLをビルドアーティファクトとして保存するようにしました。

これによって、簡単にDBのスキーマ構造が俯瞰的に確認することが出来るようになりました。

version: 2.1
...
jobs:
  build:
    docker:
     ...
    steps:
      - checkout
      - go/mod-download-cached
      ...
      - run:
          name: schemaspy
          command: |
            curl -L https://github.com/schemaspy/schemaspy/releases/download/v6.2.4/schemaspy-6.2.4.jar \
              --output ./schemaspy.jar
            curl -OJL https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.30/mysql-connector-java-8.0.30.jar

            java -jar ./schemaspy.jar \
              -t mysql \
              -db ${TEST_DATABASE_NAME} \
              -host ${TEST_DATABASE_ENDPOINT} \
              -port ${TEST_DATABASE_PORT} \
              -u ${TEST_DATABASE_USERNAME} \
              -p ${TEST_DATABASE_PASSWORD} \
              -dp mysql-connector-java-8.0.30.jar \
              -s ${TEST_DATABASE_NAME} \
              -o output \
              -vizjs
      - store_artifacts:
          path: output
...

コードの複雑度

プロジェクト初期の設計から時間が経過して、機能が増えて徐々にコードベースが大きくなり、変更や拡張がやりにくい状態となってきました。

そのため柔軟に変更・拡張が出来るようにリファクタリングをすすめていくことになりました。

課題

リファクタリングを進めるにあたって、

  • どの箇所から対応していくかの優先度付けの問題
  • ビジネスサイドへの説明と作業進捗の可視化

という課題がありました。

改善

コードの複雑度を客観的に判断できて、修正難易度を可視化できるツールとしてCodeClimateの導入を決めました。

コードの複雑度を知るためには、CodeClimateとリポジトリを同期をするだけで可能になります。

CodeClimate上でコードカバレッジも管理したかったので、CIでカバレッジの情報をアップロードするようにしました。

コードの複雑度と変更頻度から、客観的にリファクタリング箇所の優先度を判断することが出来るようになりました。複雑度を数値化してくれるのでビジネスサイドへの説明もしやすくなりました。

また、カバレッジの一覧はGo純正のものよりも分かりやすいので大変重宝しています。

version: 2.1
...
jobs:
  build:
    ...
    steps:
      - checkout
      - go/mod-download-cached
     ...
      - run:
          name: Setup Code Climate test-reporter
          command: |
            curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
            chmod +x ./cc-test-reporter

            go install github.com/jandelgado/gcov2lcov@latest
      ...
      - run:
          name: create a coverage report
          command: |
            mkdir -p ./coverage
            go test -v -count=1 -coverprofile=./coverage/gotest.profile.tmp -p 1 -covermode=set ./... -coverpkg=./... 2>&1 | go-junit-report -set-exit-code=true > ./coverage/test-report-tmp.xml
            cat ./coverage/gotest.profile.tmp | grep -v "**/mocks/.*.go" > ./coverage/gotest.profile
            go tool cover -html=./coverage/gotest.profile -o ./coverage/cover.html

            gcov2lcov -infile=coverage/gotest.profile -outfile=coverage/coverage.lcov
      - store_artifacts:
          path: coverage
      - store_test_results:
          path: coverage
      ...
      - run:
          name: CodeClimate
          command: |
            ./cc-test-reporter before-build
            ./cc-test-reporter format-coverage -t lcov -o coverage/codeclimate.json coverage/coverage.lcov
            ./cc-test-reporter upload-coverage
...

まとめ

以上のように、APIドキュメント管理、ER図の生成、コードの複雑度の可視化など、CIの設定を通じてチームの課題解決を進めてきました。

これらの改善により少しづつですが着実に、開発効率の向上とコードの品質維持を実現することができています。

今後も継続的な改善を行い、より良い開発環境を目指していきたいと思います。


株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 photosynth.co.jp

Akerunにご興味のある方はこちらから akerun.com