こんにちは スナックミー CTO の三好 (@miyoshihayato) です
はじめに
スナックミーはマイページのAPI開発をFastAPIで開発し、フロントはReactで開発しています。
詳しくは FastAPIについての採用背景
フロントの取り組み
こちらをご覧ください。
背景・課題
以前は、品質とスピードの間にトレードオフがあるという思い込みにより、プロダクト開発を進めていました。その結果、アップグレードへの対応力が低く、アップグレード作業には多大な労力が必要でした。
上記込みで様々な課題を解決するために 2021年にPHPからFastAPIのリプレイスすると決めて動き始めました。 課題は先ほどのFastAPIの採用背景をご覧ください。要約すると以下が要因になります。
* 高学習コスト * コードの質 * 心理的安全性
今年7月、FastAPIのアップグレードが発表され、これを機に変更容易性を意識したアップグレードに取り組みました。(今は 2023/12/7 現在 0.104.1 が動いてます)
IT'S HERE! 🍾🎉
— FastAPI (@FastAPI) 2023年7月7日
FastAPI 0.100.0, final release 🚀
Official support for @pydantic v2 😎
Still supporting Pydantic v1 🤓
Is there a better way to celebrate 60k GitHub stars? ✨
Check out the release notes: https://t.co/nccy2aDrdB pic.twitter.com/oF8yQTXcx1
FastAPIの対応
主に対応したところのリスト 主に Pydantic V2に関するアップグレードがメイン
dict()
からmodel_dump()
への移行__root__
からRootModel
への移行__fields__
からmodel_fields
への移行class Config
からmodel_config = ConfigDict()
への移行@validation
から@field_validator
と@model_validator
への移行Optional
の挙動の整理Field
のexample
をexamples
への変更deprecated
をjson_schema_extra
への変更
非推奨から推奨方法へ
dict()
からmodel_dump()
へ__root__
からRootModel
へ__fields__
からmodel_fields
へclass Config
からmodel_config = ConfigDict()
へ
必須対応
Optional
の挙動の整理Field
のexample
をexamples
への変更deprecated
をjson_schema_extra
への変更
Optional
挙動の整理
v1
class UserName(BaseModel): name: Optional[str] = Field(title="ユーザー名", example="須那田 クミ子")
以前は、nameがNoneの場合、Noneとして扱われましたが、v2ではエラーになるため、以下のようにFieldにNoneを宣言する必要があります。
v2
class UserName(BaseModel): name: Optional[str] = Field(None, title="ユーザー名", example="須那田 クミ子")
Fieldの仕様変更
example
からexamples
へdeprecated
からjson_schema_extra
へ
v1
class UserName(BaseModel): name: Optional[str] = Field( None, title="ユーザー名", example="須那田 クミ子", deprecated=True )
v2では、example
を examples
に変更し、deprecated
を json_schema_extra
で表現します。
v2
class UserName(BaseModel): name: Optional[str] = Field( None, title="ユーザー名", examples=["須那田 クミ子"], json_schema_extra={"deprecated": True} )
Python 3.11の対応
Python 3.11へのアップデートは、Pythonのバージョンを3.8から3.11にすることによるもので、主に以下の点に対応しました。
- Dict, List, Optional, Tuple, Union の基本廃止
- インポート量の削減
- コードのシンプル化
3.8
class SnaqmeInfo: def __init__(self) -> None: self.__name = str self.__items: List[Dict[str, int]] = [] self.__status: Optional[Union[int, List[int]]] = None
3.11
class SnaqmeInfo: def __init__(self) -> None: self.__name = str self.__items: list[dict[str, int]] = [] self.__status: int | list[int] | None = None
まとめ
今回のアップグレードは、時間的にも問題的にもスムーズに本番環境へデプロイできました。時間は合計で10時間ほどで20時間はかかってないと思います。 その成功要因としては、
- テストのカバレッジが高い(95%以上)
- ステージング環境での事前テスト
- 異常検知システムの有効活用
などが挙げられます。新しい取り組みではなく、基本をしっかり押さえることがスケールアップにおいて重要であると改めて実感しました。弊社は全てを完璧に押さえることは難しいかもしれませんが、常に意識して取り組むことで、現状と将来への最善のバランスを図りながら、チームとしての開発を進めていきたいと考えています。
また、Pydantic V2へのアップグレードにより、型の取り扱いがより厳密になったと感じています。これにより、以前は曖昧だった部分をより明示的に表現しやすくなったという印象を持っています。
募集ポジション
ソフトウェアエンジニア Backend (lead) / 株式会社スナックミー