本稿は Goodpatch Advent Calendar 2023 の 21 日目の記事になります。
こんにちは、Goodpatch でソフトウェアエンジニアをしている 中田 です。
iOSアプリ開発は常に進化し、多様な技術と手法が生まれています。そのため、開発チームで共通の基準と慣習を持つことが、プロジェクトの成功に不可欠です。効率と品質を維持するためには、全体的な視点からプロセスを見直す必要があります。
私たちは、品質の向上、特にコードの保守性を高めることに重点を置きつつ、開発プロセスの効率化も目指しました。アーキテクチャの選定から Xcode プロジェクトの構造設計、コードの一貫性を保つツールの改善、CI の整備に至るまで、iOSアプリ開発プロセスの各側面を網羅的に見直しました。そして、これらの成果を GitHub リポジトリのテンプレートとして整理し、新しいプロジェクトの開始時に必要な手順書も作成しました。このテンプレートの活用は、ゼロからの開発プロジェクトが多い現場で特に有効だと考えています。
この記事では、iOSアプリ開発の標準化に焦点を当て、私たちが進めた標準化のプロセスと成果を共有します。
標準化前の課題
プロジェクト開始時のツールやルールが確立されていなかった
クライアントワークを含む様々な案件において、プロジェクト開始時に必要となるツールやルールが事前に定まっていませんでした。これにより、各プロジェクトごとに開発の土台を一から設定する必要があり、効率性に欠けるという問題がありました。標準化が行われていなかったため、各プロジェクトでの設定作業が重複し、無駄な時間と労力が費やされる結果となっていました。
アーキテクチャの一貫性の欠如
組織内で統一されたアーキテクチャが存在しなかったため、各チームはその場の状況に応じて最適と思われる設計をしていました。しかし、これは長期的な視点では最適な解決策とはならず、結果的にコードの一貫性と効率が損なわれていました。
再利用可能なコンポーネントの不足
コンポーネントやモジュールの再利用性が低いと、似たような機能を何度も開発する必要が生じます。これは時間とリソースの無駄遣いにつながり、プロジェクト全体の効率性を低下させていました。
コードレビューの負荷が高い
コードレビューのプロセスが不十分または非効率的であると、レビューの質が低下し、重要な問題を見逃す可能性があります。また、一部のメンバーにレビューの負荷が集中することもあり、レビュアーの負担が増えていました。
CI/CD のセットアップが特定の開発者に依存するリスク
CI/CD により、ビルドとテストのプロセスが自動化され、エラーの早期発見、迅速なフィードバックループの提供、およびリリースプロセスの効率化が可能になります。これは開発プロセスにおいて非常に重要です。しかし、CI/CD の適切な設定とメンテナンスは専門的な知識と経験を要するため、しばしば特定のチームメンバーや開発者に依存する傾向があります。これにより、CI/CD の維持やトラブルシューティング時に、そのメンバーへの過度な負担やボトルネックとなるリスクがありました。
標準化のプロセス
上記の課題の解決を目指して、チームを編成し標準化プロジェクトを開始し、始めにこのプロジェクトのゴールを次のように定めました。
- 標準化対象を集約した、動くiOSアプリを開発し、テンプレート化する
- テンプレートを使い、迅速にiOSアプリ開発を立ち上げられるようにする
標準化対象を個別に議論しドキュメント化するより、動くiOSアプリを開発することで、実際の使用シナリオに基づいた実用的な標準を確立できると考えました。
具体的なアプリケーション開発をとおして、標準化の効果を直接体験し理解を深めることが可能です。また、このアプローチにより、実際のアプリケーション開発の文脈で、標準化されたコードやアーキテクチャの利点を直感的に把握でき、将来的なプロジェクトでの応用が容易になります。さらに、テンプレート化されたアプリは新たなプロジェクトの迅速な立ち上げに貢献し、開発サイクルの短縮と効率の向上を実現します。
このプロセスは、エンジニア2名でペアプログラミングのスタイルをとり、週1ないし週2のペースで、2時間から3時間オンラインで実施しています。今年の4月からスタートして11月末にテンプレートがおおよそ完成しました。
各回に実施したことをオンラインホワイトボードに記録し、iOSアプリ開発のプロセスを辿れるよう工夫しています。
時間内に実施したことを黄色い付箋に、TODOやペアプロで見つかった解決したい課題を青い付箋として残す運用にしました。青い付箋が溜まると優先度を決めチームで解決策を議論します。このプロセスを通じて、プロジェクトの進捗状況を視覚的に追跡しやすくなり、課題解決に向けての具体的なアクションプランを作成できました。
標準化の対象を選定
はじめに過去のプロジェクトのコードベースを開発チームで分析しました。具体的には、各プロジェクトで頻繁に使用されていた機能、ライブラリ、コーディングパターンを特定し、これらの共通点を洗い出しました。この分析を通じて、下記の項目を標準化の主要な対象として選定しました。
- アーキテクチャ
- Xcodeプロジェクト + Swift Package Manager(以下、SPM)
- Web APIクライアント
- 使用するOSS
- エラー処理
- ローディング処理
- Logger
- 開発ツール(Linterのルール、Formatterのルール、Dangerのルール)
- CI/CDの設定ファイル
- Pull Request のテンプレート、Issue のテンプレート
- UIコンポーネントにおける実装方針(レイアウトや色、UIコンポーネントの粒度や構造、など)
分析したコードベースには多様な「UIコンポーネント」が実装されていました。しかし、各プロジェクトには独自のユーザーインターフェースの要件があり、それぞれに最適化された体験を提供することを優先するため、標準化の対象から除外することにしました。ただし、標準化対象よりもメンテナンスの優先度を下げ、「UIコンポーネント集」のようなリポジトリを用意し整理しました。
標準化対象の具体例
アーキテクチャの選定
iOSアプリの構造と拡張性を考慮して、システムアーキテクチャは Crean Architecture をベースとし、一部、UseCase(Interactor)を導入している箇所は VIPER に近い構成となります。GUIアーキテクチャは、MVP(Model-View-Preseter)です。
UI は UIKit と SwiftUI を併用する構成ですが、画面レイアウトは Storyboard を使わず SwiftUI で実装することを前提とし、画面遷移は UIKit で実装する方針としました。このアーキテクチャは、ビジネスロジックと UI の分離を促進し、テストのしやすさや保守性を高めます。将来、UI 層を SwiftUI だけで実装する際の移行作業もスムーズになると見込んでいます。
レイヤーが多く複雑な構造のため、初回の学習コストが高い、イベントやデータを伝搬させるコードの量が増える、などのデメリットがあります。しかし、チーム開発での実装のブレを無くし、レビューのコストを下げ、保守性を向上させることのメリットを優先しました。
Swift Package Managerを活用し、Xcodeプロジェクト構造の最適化
システムアーキテクチャをレイヤー単位で分割し独立したモジュールとして設計しました。このアプローチによりモジュールの独立性が高まり、テスト、デバッグ、保守が容易になります。さらに、Xcodeプロジェクト構造を標準化することで、新規開発者が迅速にプロジェクトに馴染みやすくなり、フォルダ分けやファイル命名規則の統一によりプロジェクトの可読性と管理しやすさを向上できます。
SPM の活用により依存関係の管理が容易になり、外部ライブラリの統合と更新が容易になり、開発プロセスがスムーズに進行します。また、複数人が同時に開発する際に生じる Xcode プロジェクトのコンフリクトも大幅に軽減され、チームでの開発がより効率的に行えるようになりました。
SwiftFormat と SwiftLint のルールを見直す
各プロジェクトで SwiftFormat と SwiftLint を導入していたのですが、ルールが統一されておらず、ルールを採用した理由が不明瞭でした。それぞれについて、1 からルールを見直し、各項目ごとに「いる」「いらない」を議論・判断し、ルールを再定義しました。各ルールには、理由をコメントに残しました。
理由を残すことで、個々のプロジェクトに適したルールの取捨選択の判断が容易になります。同様にルールを見直す際にも有効です。
標準化は、あくまでも最適なプラクティスを提供するものであり、すべての状況に適用するものではありません。標準化された枠組みの中で、個々のプロジェクトや状況に合わせた柔軟な対応が可能です。
下記は .swiftlint.yml
を抜粋したものです。
opt_in_rules: - all disabled_rules: - anyobject_protocol # 将来的に廃止されるルールなため - closure_body_length # SwiftUIで必須なため - conditional_returns_on_newline # 1行で書きたいケースもあるため - direct_return # BreakPoint等で開発しやすくするため - discouraged_none_name # 明確な意志を持って使用しているため - discouraged_optional_collection # 明確な意志を持って使用しているため - explicit_acl # SwiftFormatのルールと競合するため (中略) # 以下ルールは採用する # - accessibility_trait_for_button # アクセシビリティとして重要なため # - anonymous_argument_in_multiline_closure # 読みやすさのため # - attributes # 一部例外を定義して、読みやすさのため
カスタムルールを定義
コーディング規約を覚えることの実装者の負担を軽減するため、SwiftLint を活用し、特定のパターンを自動的に検出できるようカスタムルールを多く定義しました。
例えば SwiftUI の VStack
の spacing
について「暗黙の spacing によるレイアウトを避け、意図を持って spacing を指定させるため」ということを目的にカスタムルールを定義しています。これは「UIコンポーネントにおける実装方針の標準化」にも合致します。
(中略) custom_rules: vstack_spacing: included: ".*.swift" regex: 'VStack(?:(?!spacing).)*$' name: "VStack with spacing" message: "VStack では spacing が必須である。" severity: warning (以下、省略)
細かなルールの追加の積み重ねにより、実装者はコーディング規約を覚える負担が減り、レビュアーは細かいコーディングスタイルの問題ではなく、コードの論理的な側面や設計のレビューに集中できるようになります。これにより、レビューの品質が向上し、一貫性のあるコードベースを維持でき、開発者とレビュアーの作業負担の軽減が同時に達成されます。
CI/CD の設定
iOSアプリ開発においては、プロジェクトごとに Bitrise、CircleCI、GitHub Actions のいずれかを選択していましたが、直近のプロジェクトの実績・コスト面・自社内の他プロジェクトの実績を考慮して CircleCI に絞って標準化を進めました。Bitrise は、GUIで簡単にワークフローを設定できたり、iOSアプリ開発に便利な機能もありますが、長期的に継続して利用することを考えるとコスト面のデメリットが大きいと判断しました。また、CircleCI と GitHub Actions は似ている点もあることから、乗り換えコストが低いというメリットもあると考えています。
テンプレートの作成と活用
テンプレート化のプロセスは、効率的な開発環境を構築するための重要なステップです。まず、標準化されたアーキテクチャ、SwiftLint と SwiftFormat のルール、CIや開発ツールの設定を含む基本的なプロジェクト構造を決定しました。これには、多くのプロジェクトで共通して使用されるライブラリやフレームワークも含まれます。その後、これらの要素を統合し、再利用可能なテンプレートとしてまとめました。
これにより、GitHub の機能を利用して、新しいプロジェクトを開始する際に、テンプレートを簡単にクローンして使用できます。テンプレートには必要なドキュメント、コードのサンプル、および標準的なディレクトリ構造が含まれており、新しいプロジェクトのセットアップが迅速かつ容易になります。
テンプレートを使用する利点は多岐にわたります。最も顕著なのは、プロジェクトの初期セットアップの時間を大幅に短縮できることです。また、プロジェクト間での一貫性が保たれ、新しいチームメンバーがプロジェクトに迅速に取り組むことができます。
新しいプロジェクトの開始時に必要な手順書も作成
新しいプロジェクトの立ち上げに必要な、プロジェクト名やプロビジョニングプロファイルの設定などをステップバイステップで説明する手順書を作成しました。これにより、開発チームは迅速かつ効率的にプロジェクトを設定を完了できます。
テンプレートのメリット
テンプレートを使用する利点は多岐にわたります。最も顕著なのは、プロジェクトの初期セットアップの時間を大幅に短縮できることです。また、プロジェクト間での一貫性が保たれ、新しいチームメンバーがプロジェクトに迅速に取り組むことができます。使用する際は、GitHubからテンプレートをクローンし、プロジェクト固有のカスタマイズを行うだけで、すぐに開発を開始できます。これにより、プロジェクトの品質と効率が保証され、開発プロセスが大幅に改善されます。
標準化の取り組みの展望
当社のiOSアプリ開発における社内標準化の取り組みは、常に進化し続ける技術や環境に合わせて、今後も継続的に更新されていきます。現在は UIKit と SwiftUI を併用する UI 層のアーキテクチャを採用していますが、将来的には SwiftUI オンリーのアーキテクチャへの完全移行を視野に入れています。この変更は、アプリの UI 開発をより効率的でスムーズなものにすると同時に、最新のデザイントレンドに対応することを目的としています。
加えて、さらなるコーディングルールの充実を図っています。すでに述べた SwiftLint のカスタムルールの活用を強化し、コードの読みやすさと保守性を高めることだけでなく、新しい開発者がベストプラクティスを学ぶためのリソースとして機能することも期待しています。
また、効果的なテストを書くための指針も策定したいと考えています。コードカバレッジ100%を目指すというありがちな指針ではなく、退行に対する保護、リグレッションへの耐性を高めることを目的としています。チーム全体のテスト技術とプラクティスを向上させることも期待しています。
これらの取り組みを通じて、当社のiOSアプリ開発は、品質、効率、そして技術の面で常に最先端を保ち続けることを目指しています。
まとめ
本記事では、iOSアプリ開発における社内標準化の取り組みについて詳しく紹介しました。
品質と効率を重視し、アーキテクチャの標準化やSPMを用いたXcodeプロジェクトの最適化を行いました。この取り組みは開発プロセスの効率化と品質向上に貢献しています。さらに、GitHub上のテンプレートを用いて、新規プロジェクトを迅速に開始できる仕組みを整え、これを定期的に更新し続ける計画です。
重要なのは、これらの標準化したプラクティスやツールを作って終わりにせず、継続的なアップデートを行い、常に最新の状態を保つことです。iOSアプリ開発における標準化は、品質の維持と効率的な開発の両方において、非常に重要な役割を果たしています。
Appendix:FAQ
一般的な問答集になりますが、標準化に関して開発者が通常持ちうる疑問や懸念事項を FAQ 形式で整理しました。
Q: 標準化することで、独自のアプリ機能やクリエイティビティが制限されるのではないか?
A: 標準化は開発のプロセス、コードの保守性を統一しますが、独自の機能開発に対する創造性は制限しません。むしろ、開発基盤が安定することで創造性を発揮しやすい環境を提供します。
Q: 標準化によって新しい技術やフレームワークの導入に何らかの遅れが生じるのではないか?
A: 標準化は基本的な開発プロセスや原則に焦点を当てており、新しい技術の採用を妨げるものではありません。適切な標準化は新技術の統合をよりスムーズにするための土台を提供します。
Q: 小規模なプロジェクトにも標準化のプロセスが適用できるのか?
A: 標準化は規模に関わらず適用可能です。小規模プロジェクトにおいても、標準化により開発プロセスが明確化され、効率が向上します。これにより、プロトタイピング段階から迅速にコア価値に焦点を当てた実装に入ることができます。結果として、時間を節約しながら品質の安定したアプリケーションを生み出すことに貢献します。
Q: チームが既に慣れ親しんでいる開発フローを変更すると、生産性が一時的に落ちるのではないか?
A: あらゆる変更には適応期間が必要であり、一時的な生産性の低下は予想されます。しかし、適応期間が終われば、開発プロセスの効率化と品質の向上により、全体としての生産性は長期的に向上します。
Q: 標準化により、創造性や柔軟性が失われるリスクはありますか?
A: いいえ、標準化は基本的な品質と効率を保証するガイドラインであり、創造性を妨げるものではありません。むしろ、開発者は基本的な部分にとらわれず、クリエイティブな面に集中できます。また、標準化はあくまで最適なプラクティスを提供するものであり、個々のプロジェクトの特性に応じた柔軟な対応が可能です。
Goodpatchではデザイン好きなエンジニアの仲間を募集しています。 少しでもご興味を持たれた方は、ぜひ一度カジュアルにお話ししましょう!