この記事は 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