トレードオフに関する考察

프로필

2024年12月21日

334 1

Trade-Off について

Trade-Off とは、単純に説明すると、何を放棄して何を得るかを決定する過程を意味する。

ソフトウェア開発におけるTrade-Off

(1) 性能 vs メンテナンス性
- 高性能のために複雑なアルゴリズムや最適化を適用すると、コードが読みにくくなり、メンテナンス費用が増加する。
- シンプルで明瞭なコードは、性能で損を見ることができますが、長期的に安定性と拡張性が高い

(2) 時間 vs 品質
- 迅速なリリースが必要な場合には、技術的な負債を背負っても迅速な開発を選択しなければならない。
- 品質を優先する場合には、リリースが遅れたり、リソースがより多く消費される可能性がある。

(3) モノリシックvsマイクロサービス
- モノリシックはシンプルで初期構築が速いが拡張性と柔軟性に限界が存在する。
- マイクロサービスは拡張性に優れているが初期複雑度が高く、管理コストが大きい。

FastAPI の Trade-Off

私は長い間Flaskで開発をしていましたが、今回のブログプロジェクトでDjangoをスキップしてFastAPIに移行することになりました。DjangoではなくFastAPIを選んだ理由は次のような理由です。
1.Flaskと構造、機能が似ている。
2.Djangoの厳格なルールが嫌だった。
3.Djangoより早い初期開発速度

結果的に全ての理由が両刃の剣であり、Trade-Offそのものでした。思ったよりFlaskと似た構造のおかげでそれほど難しくなくMVPを実装することができましたが、Flaskで経験したCircular Import(循環参照)をまた経験することになりました。

Circular Import(循環参照)とは?
- モジュールAがBを参照し、Bが再びAを参照する場合。

私が実際経験したケースを添付します。

Traceback (most recent call last):
  File "/Users/cliche/Desktop/project/backend/app.py", line 4, in <module>
    from utils import main_logger as logger, schedular, check_data
  File "/Users/cliche/Desktop/project/backend/utils.py", line 4, in <module>
    from models import AppleMusicChart, YouTubeChart, SpotifyChart, db
  File "/Users/cliche/Desktop/project/backend/models.py", line 1, in <module>
    from app import db
  File "/Users/cliche/Desktop/project/backend/app.py", line 4, in <module>
    from utils import main_logger as logger, scheduler, check_data
ImportError: cannot import name 'main_logger' from partially initialized module 'utils' (most likely due to a circular import)

1.app.pyでutils.pyを参照し
2.utils.pyでmodels.pyを参照します。
3.models.pyでapp.pyを参照し
4.再びapp.pyからutils.pyを参照します。

今見てもクソみたいな構造で、モジュール化の概念を浅く理解したまま迅速な開発のためだけに構造を構築した結果でした。
さらに、このプロジェクトはSQLAlchemyではなく、Flask-SQLAlchemyを使ってたので、Flaskとのコンテキスト依存性のような強結合の問題もあったので、結局は大々的にリファクタリングを一回やって、今はフレームワークをFastAPIに切り替えた状態です。

話が少し脱線しましたが、それを解決した方法も本当に無知でしたが、3回目のdb宣言を再びmodels.pyに移して、4回目のロガーをapp.pyの最初でimportせず、必要な部分でlazy importを使うことで解決しましたが、これは本質的な解決方法でもなく、性能オーバーヘッドが問題になるほど大きなプロジェクトではありませんが、一度コードが汚くなることは確かでした。

FastAPIとFlaskの特徴が似ているのは、Djangoのようなフルスタックフレームワークではなく、マイクロフレームワークとして、柔軟性と自由度を提供するという点ですが、逆に言えば、Djangoは厳格なルールが存在する代わりに適切な構造を組んでくれますが、FastAPIの場合は私が構造を設計してすべての責任を私が負わなければならないということです。

私がモノリシックではなくモジュール化を採用するようになったのはメンターの教えが大きかったのですが、最初は理由が分かりませんでした。

一つのファイルの中にすべてのコードがある方が楽なんじゃないの? 分けるのも仕事なのに、面倒くさいのに?

なぜなら、その当時はコードが長くてもすべての機能を全部入れて300~400行もなかったし、何か一つを分割する度にエラーが発生して不満が大きかったようです。

しかし、だんだんプロジェクトが大きくなり、様々なライブラリの多様な機能を使うようになり、コードの長さがとんでもなく長くなりました。このブログのような場合にもモジュール化する前はMVPだけ実装したのに2000行を超えるくらいでした。 実際、上の例で挙げたクローラープロジェクトを進める時、モジュール化の利点を一番大きく実感しましたが、今はそうではありませんが、上のコードを見ると分かるように、各サイト別にクロールコードが分かれていて、ユーティリティは別にユーティリティに保管し、どこかが壊れた時、修正がとても簡単だということを実感しました。

しかし、今もそうですが、まだモジュール化やデザインパターンに対する理解度が足りないので、勉強を進めようと思って調べている最中に突然そのような考えが浮かびました。

FastAPIの長所である自由度と柔軟性がデザインパターンやボイラープレートを導入して定型化したら、Djangoと差別化される長所が薄れてしまうのではないか。

この考えを色んなAI(o1, Claude, Perplexity)に同じように質問してみました。
ほぼみんな答えは同じようなものでした。

FastAPI開発者の間でよく議論されるTrade-Offであり、最も重要なことはバランス点を見つけることです。

そして、一番心に響く答えは私の精神的支柱であるClaudeの

私が思うに、これは"自由度の喪失"というよりは"自由度の体系化"に近いです。
デザインパターンを適用しても、選択の自由は依然として存在し、また、パターンの柔軟な適用も可能です。
コード構造化の自由度も依然として存在します。
つまり、デザインパターンやボイラープレートの導入は「制約」というよりは「ガイドライン」に近いです。 これはまるで、開発者がコーディングのコンベンションに従うようなものです。コンベンションがあるからといって、プログラミングの自由度がなくなるわけではなく、むしろ、より体系的できれいなコードを書くことができるようになるように。
結局、重要なことは、プロジェクトの規模とチームの状況に応じて、バランス点を見つけることです。
小さなプロジェクト:FastAPIの柔軟性を最大限に活用し、最小限のパターンだけを適用する。
中規模:必要な部分だけ選択的にパターンを適用する。
大規模プロジェクト:体系的なパターンを適用
このように柔軟に適用することが望ましいと思います。どう思いますか?

実際、実務家でもないAIが屁理屈に過ぎないかもしれませんが、この言葉が一番心に響いたし、私が無駄な悩みをしていたという思いと同時に、今もそうですが、あと数年経てばジュニア開発者が立つ場所はないだろうという気がしました。

#Trade-Off #FastAPI #디자인 패턴

コメント 1件

コメントを投稿するにはログインが必要です

2024-12-23 20:59
레포좀 보여주시면 안돼요?