はじめに
この記事はアプリケーションを開発するにあたっての方法論を記載しています。
いくつか前提となることを記載しておきます。
- GUIを持つアプリケーションであること
- フルスクラッチ開発であること
アプリケーションの至上の価値
まず最初に、アプリケーションで最も大事なものとは何でしょうか。
これには諸説ありますが、私はUX(ユーザ・エクスペリエンス)であると考えています。
また、同時にUXを最適化させることが、最も重要な仕事であることになります。
アプリケーションを完成させるためには、あらゆる作業が必要になりますが、最高のアプリケーションになれるかどうかは、ほぼ全てここにかかっています。
これ以外のすべてのものは、あくまで最高のUXを実現するための手段でしかありません。手段が目的を先んずることは、決してありません。
この記事はまず、この考えを土台の最も基礎部分に置いています。そこをご念頭においてください。
こんなはずじゃなかった
数ヶ月、数十人が関わって、満を辞してリリースしたプロダクトが、全然思ってたのと違う、なんてことは珍しくないと思います。
きっと青写真では素晴らしいものになる予感がしていたはずなのに、なぜでしょう。
ちょっとだけ立ち止まって思い出してください。
開発の過程で、当初考えていたことと異なることが発覚することはなかったですか?
その問題が発生した時、何らかの都合でそれを諦めたことがなかったですか?
いかがでしょう。
一つや二つではなかったのではないでしょうか。
神は細部に宿る、という言葉があります。私の解釈では、細かい仕事の総和によって、素晴らしいものができ上がる、というような意味で捉えています。
結論からお話ししましょう。
違和感の正体は、**「諦めた小さな改善」**の積み重ねです。
ではなぜ諦める必要があったのか、それを紐解いていきましょう。
アプリケーション開発とインターフェイスの役割
アプリケーションを一人で作りきることは稀でしょう。
きっと職能ごとにチームを組んだり、スクラムチームを構成したりして、開発に望むはずです。ひょっとすると、複数の会社間のプロジェクトかもしれません。
特に近年では、アプリケーションを大きく以下の二つに分けて開発することが多いです。
クライアントサイド・アプリケーション
UI(ユーザ・インターフェース)を構築するアプリケーションのこと
(WEBフロントエンドアプリケーション、ネイティブアプリケーションなど)
バックエンド・アプリケーション(サーバーサイド・アプリケーション)
クライアントからのリクエストに応じて、ビジネスロジックを実行し、結果を返すアプリケーションのこと
(RESTful サーバー、GraphQL サーバーなど)
これらは別チームで構成して開発することが多く、また同時並行して開発することが常です。
その理由としては、なんと言ってもリリースタイミングを早くできることでしょう。
また、それを可能としているのが、
- 明確なインターフェイスの定義
- モックサーバー
の二つの要素です。
インターフェイスを前もって相互に定義しておくことで、それを正として開発することができます。お互いに実装の詳細は知らずとも、接合面はわかっているためです。
モックサーバーについてはクライアントサイド向けですが、インターフェイスの定義にのっとってリターンするデータをモッキングする仕組みがあります。これにより、クライアントサイドは仮の値を使い、アプリケーションの開発を進めることができます。
これらの技術により、晴れて並行開発が可能となりました。
ですがこの仕組みには大きな問題点が潜んでいます。
その根本原因が、「予め決められたインターフェイス」です。
コンコルドの誤りは至る所に潜んでいる
決めたことを覆すことは大きな痛みを伴います。
それが自分の心の中だけのことであれば、比較的痛みは小さいでしょう。自分の誤りを内省し、改めるだけで済みます。
しかしながら、集団の中だとそう簡単には行きません。
インターフェイスの決め事は、当然ながらチームや会社間で行います。
集団開発に関わったことのある方ならイメージできると思いますが、インターフェイスの変更はハードルの大きいアクションです。みんな避けたがります。
その理由としては
- 単純に決めたことを覆すことに抵抗を感じる
- 自分(のチームが担当する箇所)を起因として変更が生じた場合、責任が発生する
- 調整が面倒
- 現在のインターフェイスに沿って開発を進めてしまっている(手戻りが発生する)
この辺りの要素です。
自分たちで決めたことを覆すのが好きな人はいないでしょう。また、その原因が自分の考慮漏れだったりした場合、それによって発生する追加コストは自分たちの責任になるかもしれません。そして、「じゃあどうあるべきか」をまた決める必要が出てきます。あまり気がすすむ作業ではないでしょう。何より、現行を正として開発していた場合、最悪丸ごと作り直し、ということにもなりかねません。
特に
現在のインターフェイスに沿って開発を進めてしまっている(手戻りが発生する)
この要素は覆すことを止める、その正当性の根拠になりがちです。
せっかく作ったのだから...、今からやり直すとまた予算の取り直しが...、スケジュールの調整が必要...など、いくらでもそれっぽい理由が思いつきます。
覆すことを止める、というとわかりにくいですので言い換えます。
つまり、
改善を無視する
ということです。画してアプリケーションを改善する機会は失われ、当初決められたインターフェイスを正として開発は止まることなく進みます。
インターフェイスという境界での綱引きが発生した場合、その際に軍配が上がるのは、多くの場合インターフェイスの維持です。
完璧なインターフェイスを予め作ることはできない
当たり前のことですが、具体的なものが出来上がっていない段階で、完璧なインターフェイスを作ることはできません。
具体的な使用例のないまま作られた共通部品は、大抵どれも特定のユースケースに最適化されたものではありません。
そもそもソフトウェア工学は、人間の不完全性を前提とした進化を遂げているはずです。
CI/CDの自動化などはその最たる例でしょう。
スクラム開発は、「見積もり」という人間の不確かなもの実績ベースで予測するモデルを提示しました。また、それすらも完璧ではないことを織り込んでいます。
インターフェイスを決める時だけ、人が完璧にそれを行えるはずはありません。
インターフェイスは確実に変更を求められます。また、根底から覆るような変動もしばしば起きることを念頭におくべきです。
また、最悪なのはインターフェイスを変動に備えて設計することです。
これはUXへの最適化という観点からは最大のタブーであり、汎用化したインターフェイスは使いづらい上に非効率です。世に公開されているAPIのほとんどはそういう作りですが、スクラッチでアプリケーションを構築する時に、そんな作りを目指す必要はありません。
念のためですが、広く外部に公開するAPIを提供する場合は、当然ながらこれは当てはまりません。十分に汎用化した作りにすべきでしょう。
あるユースケースに対して、最適化は100点を目指すことですが、汎用化は60点を目指すことです。初めから100点をとることを諦めるのは、プロとして失格でしょう。
ではなぜインターフェイスに変動が起きるのでしょう。
また、主にどこからそれが起きるのでしょうか。
きっかけは常にUXから
変動のきっかけは、常にユーザエクスペリエンスの検証から起こります。
クライアントサイドアプリケーションを構築し、それを検証する中で、「ここの表示項目が足りない」「ここにボタンを一つ増やしたい」などの改善点が見つかってきます。
それらがクライアントサイドだけで完結できることならそれでいいでしょう。ただ、しばしばそれはインターフェイスの変動が必要なものとなって現れます。
それは何故でしょうか。
それはUXが未完成の段階で、インターフェイスを定義したからです。
ひょっとしたらデザインすら完成していない段階で決めたインターフェースかもしれません。
アプリケーション開発が進行する限り、最適なUXの発見を止めることはできませんし、発見は歓迎すべきことです。
UXを先に確定させる
UXを先に確定させると、インターフェイスの変動は起きなくなります。
ではUXを確定させるとは具体的にどうすればいいのでしょうか。
それは、
クライアントサイドアプリケーションを完成させる
ということです。
ここでいう完成の定義は難しいですが、結合試験のみを残した状態、というのが正解に近いと思っています。裏を返すと、「もうバグ修正以外しないよ」という状態です。
ただ誤解を生みそうなので補足すると、単体試験が完了している状態、というのでは全く足りません。モックデータを利用しているとはいえ、ユーザのシナリオに沿った確認をひたすら行い、ユーザに期待する体験をチームが提供できていると判断していることが大事です。
何より大事なことを先に決めるのはとても良いことです。
それ以外のものについて決める時に、明確な基準ができるからです。
UXに最適化したインターフェイスを確定させる
晴れてUXが確定したタイミングで、最適なUXのために必要なものリストが出来上がっているはずです(整理は必要です)。それが真に確定したインターフェイスです。
これを正として、ようやくバックエンドの開発がスタートできます。
決してバックエンドに着手しない(重要)
そもそも着手するから手戻りが発生するのです。
未着手であれば、インターフェイスがいくら変わろうが、自分たちの作業が無駄になることはありません。また、調整も必要ありません(常に一方的ですみます)。
また、この手法はバックエンドチームにとっても大きな価値があります。
それは、アーキテクチャの選択を先延ばしできることです。
前段ではあえて書きませんでしたが、クライアントサイドからの要求でインターフェイスが変更になるのは、まだ最悪のケースではありません。
最悪のケースは、アーキテクチャの見直しが必要となることです。自分が以前参加したプロジェクトでは、当初データベースにKVSを選択していたものの、開発が進むにつれ、RDBでなければ実現できないことがわかったケースがありました。
アーキテクチャの見直しなどは、よほどのことがない限り無視されます。改善が無視される可能性の高いケースです。
UXを確定させることで、確定した要求に最適なアーキテクチャの検討から始められます。
これは何より嬉しい点でしょう。
バックエンドは本質的にUXに影響を与えない
バックエンドの振る舞いがUXに影響を与えることはほぼありません。
そのため、常にUXの都合に合わせるべきですし、この記事では確定させた上で開発を進めることを提案しています。
しかしながら、一部だけ影響を与える要素があります。
それは
- パフォーマンス
- 品質
- リリース頻度
の3点です。
しかし、いずれもバックエンド特有の問題ではなく、クライアントサイドと共通の要因です。
特に品質とリリース頻度については、後続で開発することによって発生する影響はありません。これについてはバックエンドのエンジニアが単純に最適化を目指すべき項目です。
唯一にして最大の問題点が、パフォーマンスです。こればかりは、UXに最適化したインターフェースであることが、何やらデメリットを与える可能性が捨てきれません。
それどころか、UX向上のための「便利なもの」は、高いリソースを要求しやすい傾向にあります(データを全て一回で取得する、など)。つまりはパフォーマンスが劣化する懸念があります。
この点については、最終的な歩み寄り(つまりはオミット)もないではありませんが、原則的に禁止すべきです。
UXの確定によってもたらさせる真に完璧なインターフェイスは、それ以上は何も決まっていません。パフォーマンスの問題は、アーキテクチャや採用する技術によって回避すべきです。
バックエンドの技術者がその間にすべきこと
かと言ってバックエンドの技術者はその間何もできないか、というとそんなことはありません。もちろんメンバー全員が参加する必要はないかもしれませんが、有意義な貢献の方法があります。
それがUX最適化の途中で、パフォーマンス的観点から「非常に」大きなリスクについて進言することです。
「非常に」 というのがとても大事な点です。通常のパフォーマンスに懸念がある要因であれば、それはバックエンドで吸収すべき問題です(UXが最優先です)。
ただ、圧倒的に非効率であったり、そもそも同じソリューションを別の方法で提供できる技術的アイディアを提案できる可能性はあります。
UXを最適化する過程では、その方法について着目する必要はありませんが、あえて非効率な方法を選ぶ理由もまたありません。そういう観点で意見することは、とても意味のあることですし、これはバックエンドのプロフェッショナルにしかできません。
同じ商品が手に入るのなら、それが近所で買ったものであろうと、遠くで買ったものであろうと構わないはずです。UXの探求者が遠くへ買い出しに行こうとしているのを止めることは、バックエンドのプロフェッショナルにしかできません。
スケジュールについて
とは言ってもシリアルで開発が進むということは、それだけスケジュールが長引きガチになるのは事実としてあります。
ただ、各開発工程でパラレルに作業することは可能です。
不確かなもの(インターフェイス)を頼りに並行して開発することが問題なのであって、自由な枠組みの中で人を多く割いて開発することは何ら問題ないです。
原則、インターフェイスの変更を起因とした開発コスト増のリスクがないため、スケジュールの観点からもこれまでの開発手法に大きく劣後するということはないはずです。
スキーマ駆動開発との違い
スキーマ駆動開発はスキーマをインターフェイスとしたこれまで通りの開発です。
Graphqlを用いる場合、クライアントとサーバーがそれぞれ同じスキーマに依存した各種型定義やボイラープレートを取得できます。これによって、同時並行した開発がかなり容易になります。
ただ、これまでのインターフェイスを軸とした開発手法と本質的には変わらず、型定義の手間やヒューマンエラーのリスクを下げる効果を提供しているに止まります。
実現するための方法(GraphQLを利用した場合)
これまで説明してきた内容を実践するための方法をまとめます。
クライアントアプリケーションを開発するにあたっては、スキーマを柔軟に変動させながら開発していくのが良いでしょう。UXの都合に合わせて、スキーマを変更することに抵抗を覚えないでください。全く問題ありません。
インターフェイスにGraphQLを選んだ場合、apollo-server
などを用いることで、スキーマの変更に自動的に適合したモッキングサーバーを構築することが可能です。
前提
- アプリケーションの価値は、UXで決まる
- 最高のUXへの探究は、UIとインターフェイス定義のみで足りる(バックエンドの実装は影響を与えない)
- 絶対にUXが完成するまで、バックエンドの開発を開始しない(重要)
手順
- UXを確定する
- スキーマをUXファーストで最適化し、確定する
- スキーマを元にバックエンドアプリケーションを作成する
- パフォーマンスのチューニングを行う
最後に
繰り返しになりますが、決めたものを変更するのはとても大変です。
これが違うチーム、はたまた違う会社であったらその大変さは何倍にもなります。きっと落ち着くところは、「前に決めたから」と必要なことをオミットすることでしょう。
私はそんな場面をこれまで何度も見てきました。また、果敢にも変更を選んだ場合、現場は大きく疲弊します。それにせっかく作ったものを作り直すのは、できれば誰もやりたくないのです。
何より、UXの最適化のために必要な、小さな改善はいずれも無視されていきます。UXを突き詰めるにつれ、どんどん出てくる改善のアイディアの積み重ねが、最高のUXを生み出すのです。一つ一つは小さく、無視しても問題なさそうに見えるものが、何より大切だったはずなのです。
しかしながら、きっと白紙から作るのであれば、そういう判断にはならなかったのではないでしょうか。「いいね。やろう」そう言えたはずです。
結局、すでにあるものを改変するからこそ、この問題は生じます。だから、UXが確定するまで、バックエンドは作るべきではないと考えます。また、バックエンドのアーキテクチャも、決めるべきではありません。インターフェースが、文字通り確定した後、それに最適なものを選ぶべきです。