🐥

ゆめみ社のFlutterエンジニア採用課題に取り組んでみた

2022/07/26に公開

背景

先月、偶々ゆめみ社の Flutter エンジニア採用時のチェック課題が GitHub に公開されているのを知り、内容をざっと見てみると多言語対応やダークモード対応など網羅性が高く、単純に良い学習になりそうと思って取り組んでみました。
サービスの要件次第では海外配信想定がなかったり、リリース時期に重きを置いて多言語対応・ダークモード対応を省くこともあり、この機会にひととおり触ってみようと思った次第です。
とくに評価ポイントの以下の部分は、一般的に言及されるエンジニアとしての能力はもちろん、アクセシビリティや表現方法、品質維持など、ユーザーと近い領域を担うアプリ開発者において大事なポイントだと思っています。

- 簡潔性・可読性・安全性・保守性の高いコード
- Dart の言語機能を適切に使いこなせているか
  - テスト
  - テストが導入しやすい構成
  - Unit・UI テストがある
- UI/UX
  - エラー発生時の処理
  - 画面回転・様々な画面サイズ対応
  - Theme の適切な利用・ダークモードの対応
  - 多言語対応
  - アニメーションなど
- CI/CD
  - ビルド
  - テスト
  - リント
  - フォーマット
  - 仮のデプロイ環境

https://github.com/yumemi-inc/flutter-engineer-codecheck

余談ですが、ゆめみ社は ゆめみオープン・ハンドブック のような組織図や福利厚生などもすべてオープンに公開していて、日々新しい取り組みを率先して行っていて良いですね👏

本記事の内容

こちらは、以前話題にもなっていたフロントエンドのコーディング試験に対する記事で、React に明るくない私でも要所が網羅されていてキャッチアップできたので、参考にしました。
https://bufferings.hatenablog.com/entry/2022/06/11/232314

とはいえ、一応公開されている課題内容ではありますが、すべてを記載してしまってはせっかく入念に作られたであろう課題の意味が薄れてしまう気がするので、個人の独断と偏見で項目を絞って記載します。

また、「普段使っていないけど気になっていたパッケージ」なども積極的に採用しており、それが必ずしも最良な選択とは限らない点をご了承ください。

以下、冒頭の評価ポイントからアクセシビリティを中心に掻い摘んで記載します。

成果物

こちらが、READMEに記載されていた動作要件 です。

動作要件
- 何かしらのキーワードを入力できる
- 入力したキーワードで GitHub のリポジトリを検索できる
- GitHub のリポジトリを検索する際、GitHub API(search/repositories)を利用する
- 検索結果は一覧で概要(リポジトリ名)を表示する
- 検索結果のアイテムをタップしたら、該当リポジトリの詳細(リポジトリ名、オーナーアイコン、プロジェクト言語、Star 数、Watcher 数、Fork 数、Issue 数)を表示する
Search Search - dark
Not Found Detail - ja Detail - en

簡潔性・可読性・安全性・保守性の高いコードか

関心ベースの構成

これらの基準を満たしているかどうかは分からないですが、今回はディレクトリ構成を関心ベースで作ってみました。この手の話題は、以下にある記事やツイートなどでも日々議論されていますが、個人的なこれまでのプロジェクトでは機能単位で区切っていたので心機一転試してみました。
結論、今のところ結構しっくり来ていて「この〇〇に関するファイルはどこに置こう?」といった思考リソースを費やすことが以前より少なくなった気がします(まだプロジェクト規模が少ないからかもしれないですが)。
最初、共通利用されるような features に入らないものはどうしよう、とは思ったのですが common を箱のように置いてからは取り回ししやすく、良い塩梅でバランス取れている感触がありました。一長一短だとは思うので既存プロジェクトを差し替えるまではしないですが、今後の新規プロジェクトでは基本的に関心ベースで作っていこうと思っています。

.
├── common
│   ├── extensions
│   ├── providers
│   └── widgets
├── features
│   ├── base
│   ├── repo
│   └── settings
├── gen
├── l10n
├── services
│   ├── api
│   └── paging
└── theme
├── extensions
└── theme_mode

https://codewithandrea.com/articles/flutter-project-structure/

単方向データフロー

前述のディレクトリ構成の通りですが、とくにクリーンアーキテクチャや DDD のような形式張ったアーキテクトというものは採用していません(個人的には結構この手の理論がイマイチ理解できておらず、その恩恵がまだ腑に落ちていないだけかもしれませんのでその点はご容赦ください🙏)。
とは言え、データの流れを単方向にする点だけは意識しており、以前ツイートされていた monoさんの手書きアーキテクト を強く意識して書いています。今回だと GitHub から API でデータをフェッチする形となるので、FutureProvider で Widget 層に流し込むイメージです。
一方、ユーザーによる操作(今回だと検索フォームの入力)では、UI からデータソース層へ流れる形になります(図の B のライン)。

UI/UX(アクセシビリティ)

Themeの適切な利用・ダークモードの対応

Flutter 3からTheme Extensionが追加 され、ThemeData を自由に拡張できるようになり、以前よりダークモードへの対応がしやすくなりました。

この Theme Extension, 非常に便利なのですが定義時に都度 leap,copyWith を追記する必要があり中々に面倒という課題がありました。Github Copilotが賢いので許容という声 もありますが、毎月$10のサブスク は少し悩んでしまいますよね。

今回は、以上の課題感に対するソリューションとして公開されている theme_tailor というパッケージを使ってみました。

https://pub.dev/packages/theme_tailor

使い方は至ってシンプルで、@tailor というアノテーションをつけて build_runner を走らせるだけです。あとは ThemeExtension 継承クラスを自動で生成してくれます。


class _$AppColors {
  static List<Color> background = [Colors.white, Colors.black];
}

一点、自動生成される BuildContext の拡張だけ気になりました が、ひとまず上辺だけ触ってみた感想としては悪くなかったです。ただ確かに、ThemeExtension を弄る機会は少ない(今回も一度生成して残り少し調整した程度)なので、わざわざパッケージ依存増やして自動生成に頼るかは悩みどころですね🤔

多言語対応

公式の Internationalizing Flutter apps | Flutter の通りに行いました。他のアプローチとしては pub.dev で localization 用のパッケージがいくつかあり候補になるそうですが、今回はとくに気になることはなかったので至ってシンプルに公式通りで済みました。
その他、とくに言及することはないですが VS Code を使っている方はぜひこちらをご利用ください。便利すぎです。
https://twitter.com/_mono/status/1543802124534153217?s=20&t=PKPO0EA1crTEiWlmm-W6zQ

異なる解像度やアクセシビリティを動的に切り替えて確認

前述したアクセシビリティ周りの確認には device_preview を使うと捗ります。
https://pub.dev/packages/device_preview

enabled プロパティを持っているので、公式ドキュメント通り !kReleaseMode とすると、リリースモード以外で有効になります。ただ使ってみるとわかりますが、スマートフォンサイズでは設定値を変更するのが煩わしいので、個人的には macOS のみオンにするのが気に入っていて以下の指定にしてます。

DevicePreview(
  // DevicePreviewはスマートフォンサイズでは設定がしづらいので、macOSでのビルドのみ使うように指定。
  // iOS/Androidビルドでは操作感を確かめる用途もあるのでデバイスプレビューは使わない。
  enabled: !kIsWeb && Platform.isMacOS && kDebugMode,
  builder: (context) => const App(),
),

1点注意として、macOS ビルド時にはデフォルトでクライアントネットワークの設定がなされていないため、DebugProfile.entitlements に以下を追加する必要があります。

DebugProfile.entitlements
  <dict>
+   <key>com.apple.security.network.client</key>
+   <true/>
    ...
  </dict>

操作感はこちらのツイートのとおりです。
https://twitter.com/h_tsuruo/status/1551105787363495936?s=20&t=fewGIAK8TLW6NUwuCh0j6A

以上を踏まえて、device_preview を駆使しながら主に下記の観点を中心に確認します。

- Device(Model):
    - 解像度の違いでレイアウトが崩れていないか
    - 視認性が落ちていないか
    - OSの仕様で壊れていないか
- Locale:
    - 対応の漏れやタイポがないか
    - 改行位置がおかしくないか
    - 表現に違和感が無いか
- Theme:
    - カラーが潰れていないか
    - 視認性が落ちていないか
- Orientaion:
    - 特にLandscape状態時にoverflowしていないか
    - SafeAreaを干渉していないか
- Text scaling factor:
    - 拡大/縮小時にレイアウトが崩れていないか
    - 見切れてしまう箇所が無いか

Text scaling factor については、ユーザービリティ観点では可変にするべきだと思いますが、要件次第では固定にせざるを得ない場面もあると思います。そういった場合でもこちらの記事の通り、ある程度の設定を許容しつつ min() で制限してしまうのがちょうどよい塩梅だなと最近思っています。
https://zenn.dev/ktakayama/articles/1b741f765b4f24#特定の範囲内なら文字サイズの変更も許可したい


device_preview も便利ではありますが単に普通のアプリケーションの機能の1つなので、iOS シミュレータのような高機能(キャッシュの削除やスクリーンムービーの作成等)は無く、好みや場面に応じて開発しやすい方を選ぶと良いと思います。iOS シミュレーターは Toggle Appearance をショートカットで切り替えられる一方、Location は再起動がかかってしまうので一長一短という感じですかね。
また、最終的には実機での確認がメインとなるはずなので、あくまでその前段の「開発サイクルを早めるための1つのアプローチ」程度に思っておくと良いと思います。

まとめと余談

今回はゆめみ社の Flutter エンジニア採用時のチェック課題を試してみました。評価ポイントで挙げられていた項目をすべて満たせたわけではないですが、改めて手を動かしてみると自分の苦手な部分や、学習を必要とする領域がより明確になって良い機会でした。個人的にはあまりテストを書く癖を付けられておらず弱い部分だと気づき、その辺り中心に改めて学習を進める必要があると認識しました。
昨今、アプリエンジニアやフロントエンドエンジニアも UI やアクセシビリティに対する深い知識や感性が求められ、カバー範囲が益々広がってきていると感じます。これに加えて、各プラットフォームの理解に始まり、直近だと iOS 16, Android 13の新仕様や変更の追従、アプリストアの諸々など担う領域はたくさんありますね。個人開発でこれらをすべて1人でこなしている人を見ると、いつもすごいなと感心しています。
昔に議論(?)があったこちらの記事 の主張についても、flutter run して手元で動くものを作る程度を指しているのであれば納得できるものの、世の中のプロダクションレベルな開発をしているアプリエンジニアはそうとも限らないので、どこまでを見据えるかで感覚は変わってくると思いました。あくまで個人的には、本記事で取り扱ったような部分をケアして作り込もうとすると、「難しくない」という感覚は持てませんでした(感じ方は人それぞれで良いと思います)。
というより、アプリ開発に限らずフロントエンドやバックエンドもやはりソフトウェア開発は謙遜無くどれも大変ですよね。業界のトレンドにアンテナは張りつつも、自分が得意な領域を見定めてしっかりスキルを磨いていこうと思いました。

最後に、前述の余談に関連してエンジニアのキャリア形成を考える上で参考になるスレッドだったので引用しておきます。

https://twitter.com/_mono/status/1548302419263889408?s=20&t=ifMK6cJT8LfHs9mUciwKSg

参考

Discussion