開発の悩み#
最近、体系的な開発をするための悩みをしています。ある機能を作りたいのですが、その機能をどういう順序で作るのが良いのか、今やっていることが正しいのか疑いながら結局コードをあまり書いていません。
今タイピングしていることよりも「きっともっと良い方法があるはず...」と考えながら、この疑問に正解があるのか、それとも面倒で幻想を追っているのかと思ってしまいます。ChatGPTにも似たような質問をしましたが、回答は既に知っていることばかりでも、実際にやってきたかどうか自分自身を振り返るきっかけになりました。
GPTは開発プロセス、アーキテクチャ、協業方式、ツール選択についてアプローチしてみるよう教えてくれました。この質問にAIが何と答えたか共有します。もちろんGPTの回答が悩みの解答にはなりませんでした。ある機能について作るべき順序が知りたかったからです。
質問#
開発言語は学びましたが、開発する方式についてはいつも悩んでいます。常に効率的で正しい方法で開発しているとは思えないんです。あなたの知識を総動員して、アプリ/Webサービス開発のための最も効率的な開発方式を教えてください。
GPTの回答#
1. 要求事項の定義と企画#
- 目標設定: アプリの目的と主要機能を明確に定義します。ユーザーの問題を解決するための具体的な目標を設定してください。
- ユーザーストーリー: ユーザーがサービスでどのような行動をするかを考えながらユーザーストーリーを作成します。これは要求事項を機能的に具体化するのに役立ちます。
- MVP (Minimum Viable Product) 定義: すべての機能を一度に実装するのは非効率的です。コア機能のみを含む最小限の製品を定義し、それを基に素早くユーザーフィードバックを得ます。
2. アーキテクチャ設計#
- モジュール化: 各機能を独立したモジュールに分けて設計します。これによりコードの再利用性と保守性を高められます。
- クリーンアーキテクチャ: クリーンアーキテクチャ(または階層型アーキテクチャ)を採用すれば、ビジネスロジック、UI、データアクセスを明確に分離でき、コード管理が容易になります。
- API設計: フロントエンドとバックエンドが明確に通信できるようRESTfulまたはGraphQL APIを設計します。APIドキュメントは必須です。
3. 技術スタック選択#
- フロントエンド: React、Vue.js、Angularなどのフレームワークから1つを選択します。各フレームワークは学習曲線と性能、コミュニティサポートが異なるのでプロジェクトの性格に合わせて選択してください。
- バックエンド: Node.js、Django、Ruby on Rails、Springなど、必要な性能と拡張性に合ったバックエンドフレームワークを選択します。
- データベース: リレーショナルデータベース(MySQL、PostgreSQL)とNoSQLデータベース(MongoDB、Firebase)から要求事項に合わせて選択します。
- クラウドサービス: AWS、Google Cloud、Azureなどのクラウドプラットフォームを使ってインフラを自動化し、デプロイを簡単に管理します。
4. バージョン管理と協業ツール#
- Git: バージョン管理のためにGitを使用します。GitHub、GitLab、Bitbucketなどで協業を強化できます。
- CI/CD: Jenkins、GitLab CI、CircleCIなどのツールでCI/CDパイプラインを構築し、自動化されたテストとデプロイを設定します。
- プロジェクト管理: Jira、Trello、Asanaなどのツールで作業を管理し、チームの生産性を最大化します。
5. テスト#
- ユニットテスト: 各機能のユニットテストを通じてコードが意図した通りに動作するか確認します。Jest、Mocha、JUnitなどのテストフレームワークを使ってください。
- 統合テスト: モジュール間の相互作用をテストします。APIテストはPostman、Newmanなどを活用できます。
- 自動化テスト: Selenium、Cypressなどのツールを活用して繰り返しのテストを自動化します。
6. デプロイと運用#
- デプロイ自動化: CI/CDパイプラインでデプロイを自動化し、ゼロダウンタイムデプロイ(Zero-downtime deployment)を目標とします。
- モニタリング: New Relic、Datadog、Prometheusなどのモニタリングツールでアプリケーションの状態をリアルタイムに監視し、問題を素早く把握します。
- ロギング: Logstash、Kibana、Grafanaなどのツールでログを管理・分析します。
7. 継続的な改善#
- ユーザーフィードバック反映: MVPまたは初期バージョンをデプロイした後、ユーザーフィードバックを積極的に反映して機能を改善します。
- コードリファクタリング: 定期的にコードをリファクタリングして保守性を高め、技術的負債を減らします。
- パフォーマンス最適化: アプリケーションのパフォーマンスを継続的にモニタリングし、必要に応じてデータベース最適化、キャッシング、コード最適化を行います。
ファイルを作成して単純に繰り返されるコードを書くのは嫌なので、freezedやjson_serializableのようなコード生成パッケージを探したり、VS Codeスニペットも試してみました。そしてFlutterで開発中のMacrosというものもあるので、これを活用すればもっと楽になるのではないかと思っています。
自分自身ともたくさん対話すべきですが、こういうことを望んでいるようです。
- あるサービスを作りたいが、すべて自分で作らなければならない状況
- アプリのリリースと継続的な運用のためにまずやるべきこと:企画、デザイン、開発
- 項目ごとに簡単に決めて進めたい
- やることが多い開発で生産性を確保したい
- 開発生産性を上げるためにフォルダ構成、Flutter開発アーキテクチャ、状態管理の使用、有用なパッケージ活用の調査などを探している
- 開発アーキテクチャと状態管理を組み合わせてコード品質を上げたい
アプリサービス開発にFlutterを選びました。しかしFlutterで開発していると、Springで使っていたMVCパターンのようなものはないか探すようになりました。Flutterにはいくつかの開発アーキテクチャがありますが、その中でもRiverpodと連携できるFeature-Firstアーキテクチャが気に入りました。このアーキテクチャを上手く使えているわけではありませんが、適用してみてはどうかと思いました。
しかし何か1つ作るたびに、アーキテクチャをちゃんと守っているか強迫的に考えてしまいます。こうしているうちに、このアーキテクチャをちゃんと守りながら機能を作る順序があるはずだと思いました。最初からUIを作るのか、Modelを作ってUIと分離した機能まで作ってからUIを最後に作るべきか。こんなことを考えてGPTにも以下の方式について聞いてみました。
質問#
開発アーキテクチャは作るべきファイルとフォルダが多い。Feature-First Architectureを作る順序を教えて。
GPTの回答#
Feature-First Architectureでファイルやフォルダが増えるのは自然なことですが、効率的に管理するためには構造を設定し機能を追加する順序が重要です。この順序に従うことでプロジェクトの複雑さを減らし、段階的に拡張できます。以下はFeature-First Architectureを構築する際に参考にできるステップ別の順序です。
1. 基本フォルダ構造の設定#
まず、プロジェクト全体の構造を決めます。これによりプロジェクト内でコードがどこに位置するかを明確にできます。
lib/
├── features/
│ ├── feature_name/
│ │ ├── data/
│ │ ├── domain/
│ │ └── presentation/
├── core/
│ ├── common_widgets/
│ ├── exceptions/
│ ├── network/
│ └── theme/
└── main.dartこの段階では各機能に該当するフォルダとコアフォルダを基本的に設定しておくことが重要です。機能が追加されるたびに該当機能に合ったサブフォルダを作成します。
2. 機能開発の順序#
個別機能を追加する際、各段階で必要なファイルとフォルダを作成して実装します。機能を追加する順序は以下の通りです:
- 機能要件分析と企画
- 該当機能の役割と要件を定義します。
- 主要なユースケースとユーザーフローを把握し、どんなデータとロジックが必要か決定します。
- ドメイン層の実装(Domain Layer)
- Entities: 該当機能で使用する主要エンティティ(ビジネスモデル)を定義します。
- 例:
lib/features/login/domain/entities/user.dart
- 例:
- Use Cases: ビジネスロジックを処理するユースケースを作成します。
- 例:
lib/features/login/domain/use_cases/login_use_case.dart
ドメイン層を先に実装する理由は、機能のコアロジックを中心にデータ層とプレゼンテーション層を設計できるからです。
- 例:
- Entities: 該当機能で使用する主要エンティティ(ビジネスモデル)を定義します。
- データ層の実装(Data Layer)
- Repositories: ドメイン層で定義したインターフェースを実装し、データソースを管理します。
- 例:
lib/features/login/data/repositories/auth_repository_impl.dart
- 例:
- Data Providers: API呼び出しやローカルデータベースアクセスなどを担当するクラスを実装します。
- 例:
lib/features/login/data/providers/auth_api_provider.dart
- 例:
- Models: APIやローカルデータで使用するデータをモデルとして定義します。
- 例:
lib/features/login/data/models/user_model.dart
この段階ではデータソースをドメイン層と接続し、機能が実際に動作するようにします。
- 例:
- Repositories: ドメイン層で定義したインターフェースを実装し、データソースを管理します。
- プレゼンテーション層の実装(Presentation Layer)
- State Management: Riverpodを使って状態管理を実装します。ここで必要な状態とビジネスロジックを含む状態ノティファイアを定義します。
- 例:
lib/features/login/presentation/state/login_state.dart
- 例:
- Screens: 機能の画面(UI)を実装します。状態に応じてUIがどう変化するか定義します。
- 例:
lib/features/login/presentation/screens/login_screen.dart
- 例:
- Widgets: 該当機能で再利用可能なウィジェットを実装します。
- 例:
lib/features/login/presentation/widgets/login_form.dart
プレゼンテーション層では最終的にユーザーに見える画面とインタラクションを実装します。
- 例:
- State Management: Riverpodを使って状態管理を実装します。ここで必要な状態とビジネスロジックを含む状態ノティファイアを定義します。
- ユニットテストの作成
- 各層に対するユニットテストを作成します。この際、ドメイン層とデータ層のテストを優先的に行います。
- 例:
lib/features/login/domain/use_cases/login_use_case_test.dart - 例:
lib/features/login/data/repositories/auth_repository_impl_test.dart
この過程は各層の機能が独立して正しく動作することを保証します。
- 機能統合とレビュー
- ドメイン、データ、プレゼンテーション層がうまく統合されたか確認し、ユニットテストと統合テストで検証します。
- 最初は小さな機能単位から始めて、全体的な構造と設計を繰り返しレビューし改善します。
3. 共通コードとリファクタリング#
機能が追加されるにつれて共通で使用するコードが増えます。そのためにcoreフォルダに共通のロジックやユーティリティ、ウィジェットなどをリファクタリングしてモジュール化します。
- 共通ウィジェット: 複数の機能で使用できる共通ウィジェットは
core/common_widgets/に配置します。 - ネットワーク管理: API呼び出しロジックは
core/network/にモジュール化して管理します。 - 例外処理: 共通の例外処理は
core/exceptions/で管理し、各機能で活用します。
4. プロジェクトの拡張とメンテナンス#
- 新機能を追加する際、上記で説明した順序を繰り返して段階的にプロジェクトを拡張します。
- 既存機能で見つかった問題や改善点があれば、リファクタリング段階で改善します。
5. 自動化とデプロイ#
- CI/CD設定: コードプッシュ時にテストとビルドを自動化し、安定的にプロジェクトを管理します。
- リリース管理: バージョン管理を通じて新機能が正しくリリースされるよう管理します。
主要機能から始めて段階的に拡張し、テストとリファクタリングを通じてプロジェクトの品質を維持することが重要です。
まとめ#
最近Threadsで開発に関連する投稿を読んだのですが、以下のフレーズが強く心に響きました。自分も一人で開発しているものがあるのですが、遅れているのは事実ですから。
コードが美しいスタートアップは潰れる。
あまりに長く、たくさん悩みすぎるのは良い成果を出せない近道のようです。このような良くないと思う習慣を直していかなければなりません。
コードの品質が高いのは良いことですが、仕事(ビジネス)を先に進めることを優先すべきですね。
Treat people as if they were what they ought to be, and you help them to become what they are capable of being.
— Johann Wolfgang von Goethe