tomoima525's blog

Androidとか技術とかその他気になったことを書いているブログ。世界の秘密はカレーの中にある!サンフランシスコから発信中。

クライアントとサーバーどちらに実装するかの設計指針をチームで持つこと

f:id:tomoima525:20190106074708j:plain モバイルアプリケーションを開発していると、この要件や仕様はクライアントとサーバーどちらに置くべきか、という議論がチームでなされることがしばしばあります。例えば、

  • あるレスポンスAを受けて処理Bを行い、その結果をユーザーに提示する
  • 登録処理などで、処理C,処理Dという異なる処理を並列して行い、それらが完了したらユーザー側に通知する

やろうと思えばクライアント側で処理を全て持つこともできますし、サーバー側で実装もできますね。

このような仕様のディスカッションが起きたとき、チームで統一した判断基準を持っていますか?

自分の場合、クライアントアプリはロジックをなるべくサーバーに移譲すべき という設計指針をチームに提案します。
上の例で言うならば、

  • サーバーから処理Bも踏まえたレスポンスA'を返してもらい、ユーザーに提示する
  • クライアントは1リクエストをサーバーに投げる。サーバー側で処理C,Dを投げ結果を待ち合わせする。クライアント側は1レスポンスで結果を表示する

というふうに、極力クライアント側での処理を薄くするように実装すべきです。
理由は以下です。

理由 1) クライアントアプリはロールバックが効かない

クライアントアプリは一度デプロイされてしまうと、再度リリースする以外に上書きする方法はありません。またユーザーも頻繁にアップデートするわけではないので、一度不具合を出してしまうと強制アップデート以外に改修する方法がないです。バージョンコードで特定のバージョンは以前の仕様でリクエストハンドリングするといったことも可能ですが、サーバーサイドの複雑性はどんどん増します。

クライアント側は結果を出すだけのシンプルな作りにしておけば、ロジックに不具合があってもサーバー側のデプロイだけで修正ができます。

理由 2 )バグ調査をシンプルにすべき

クライアントアプリはネットワーク状況やメモリ、ハードウェア条件など千差万別のデバイスの上で動きます。これまで手元で再現できない不可解なバグを山程みてきました。

英語で本番リリースすることをスラング"out to the wild" と表現することがあります。ユーザー環境は時にわけのわからない野蛮な環境なわけです。

f:id:tomoima525:20190106075455p:plain
wild wild west

我々ができることは、不確定要素をなるべく減らし、不具合が起きることを極力減らすことです。クライアント側に固有の仕様が少なければ少ないほど、それは実現可能です。また、サーバーサイドにロジックがあると、不具合調査のためにクライアントとサーバーを行き来する必要もなくなりますし、テストもしやすくなります。

理由 3) クライアントアプリはユーザーエクスペリエンスにフォーカスすべき

クライアントアプリはユーザーがまさに触れる部分なので、そこをいかに気持ちよくするかにフォーカスするべきです。例えばロードを早くするためにキャッシュの最適化をすることや、画面遷移をスムーズにするためにロジックを組むようなことです。
逆に、ある処理がうまくいったかどうかを判定したり、リクエストで受けたデータをもとに複雑な処理を行うといったことは、ユーザーの目には触れないので、クライアントでやるメリットはないです。

例外もある

もちろん、プロダクトレベルの仕様でロジックをクライアント側に置きたいというケースもあります。例えば

  • バリデーションですぐにエラーをユーザーに教えてあげたい
  • 項目検索をオフラインで使うために、ローカルDBから検索できるようにしたい

といったものです。これらはユーザーエクスペリエンスにも関わるので、当然クライアントで実装することを検討すべきですが、前述の理由にあげたような問題は発生しうるということはプロダクトオーナーも含めて認識してもらったほうがよいです。

サーバーの実装が複雑化する!という意見

サーバーサイドを実装するエンジニア側からすれば、サーバーの実装が複雑化する!という意見は必ずあると思います。
それはそのとおりなのですが、一方でメリットもあります。

  • ロジックがサーバーサイドにあるので、開発時に検証しやすい
    例えばクライアント側からのパラメータを渡せばサーバー側がこれを返す、というテストがシンプルにかけます。クライアント側にもロジックがあると、クライアント側であれこれの条件を設定して…という風に手間がかかりますよね。
  • 不具合調査がしやすい
    これは理由2と全く同じですが、クライアントの特定の機能で起きているバグみたいなものは完全に調査不要になるので、無駄な労力を割くことが減ります。

まとめ

仕様の責任範囲の切り分けは単純ではないケースが多く、クライアント、サーバーどちらに処理を寄せるかという判断は毎回慎重に行われるべきです。しかし現実的にはサーバーはコストのかかるロジック、クライアントはユーザーエクスペリエンスの向上にフォーカスすることが、プロダクトにとって、そしてチームの生産性にとってメリットがあるということを合意することが大事だと思います。