頓挫したソフトウェアプロジェクトの立て直し:停滞したモバイルアプリを90日でリリース
2024年初頭、ベトナムの中堅小売チェーンが当社に相談を寄せた時点で、その内部指標が示していた状況は、我々がこれまで何度も目にしてきたものでした。当初6ヶ月の開発として計画された顧客ロイヤリティアプリは、開始から14ヶ月が経過し、予算を約180パーセント超過しており、40,000人のアクティブメンバーに対して依然として提供可能な状態ではありませんでした。すでに2社のベンダーが着任と撤退を繰り返していました。3社目の見積書がCEOの机に届いたばかりで、さらに9ヶ月の作業を約束する内容でした。その代わりに、クライアントは当社に2週間の監査を依頼しました。90日後、アプリはApple App StoreおよびGoogle Playで公開され、年末のロイヤリティキャンペーンを稼働させ、リリース初月で12,000件を超える新規登録を獲得しました。
本稿は、そこに至るまでの経緯、実際に何が問題だったのか、そしてベンダー間の政治的要素を取り除いたときに、頓挫したソフトウェアプロジェクトの立て直しがどのような姿を取るのかについての記録です。
プロジェクト概要
クライアントは3つの省にまたがる34店舗を展開するスペシャルティ小売ブランドであり、そのロイヤリティプログラムは、元来のSMSおよびプラスチックカードによるインフラでは対応しきれない複雑さに達していました。構想は明快でした。会員がポイント残高を確認し、店頭でリワードを引き換え、パーソナライズされたオファーを受け取り、店舗内サービスを予約できる、iOSおよびAndroid向けのネイティブモバイルアプリを構築することです。文書上は、成熟したライブラリと明確なビジネスロジックを備えた、十分に理解されている構築案件でした。
当初の納品計画では、ホーチミン市のスタジオとの6ヶ月契約に続き、社内チームへの3ヶ月間の引き継ぎが予定されていました。14ヶ月が経過した時点で、社内チームはまだ採用されておらず、2社目のベンダーはバックエンドを2度作り直しており、アプリ自体はロイヤリティスキャンのフローに入って2分以内にAndroid 12上でクラッシュしていました。クライアントのCFOは、直接コストがすでに41億VNDを超えていると見積もっており、これには印刷バウチャーに縛られたロイヤリティプログラムから失われた収益は含まれていませんでした。
クライアントが直面していた問題
2週間の監査で3つの異なる問題が明らかになりましたが、クライアントの経営陣にとって驚きだったのは、そのうち実際に技術的な問題は1つだけだったという点でした。
第一の問題は、機能拡張を装ったスコープドリフトでした。当初42ページであった仕様書は118ページに膨れ上がっており、3人の異なるステークホルダーが正式な変更プロセスを経ずに新機能を追加し続けていました。モバイルチームは動く標的を追いかけており、バックエンドチームはすでに2度再設計された画面のためにエンドポイントを構築していました。
第二の問題は、本番運用ではなくデモ向けに設計されたバックエンドでした。API層はクライアントのeコマースサイトも兼ねる共有のPHPモノリスの上に構築されていました。ポイント残高のクエリは、会員IDカラムにインデックスが張られていない210万行のMySQLテーブルを走査していました。引き換えトランザクションは冪等ではなく、これは店頭での不安定な4G信号が会員のポイントを二重に引き落とす可能性があることを意味していました。以前のベンダーはこれらの問題を認識しており、3社目の見積もりとして全面的な書き直しを提案していました。
第三の、そして最も深刻な問題は、自組織内で会話が成立しなくなっていたチームでした。12ヶ月目には、クライアントのプロダクトオーナー、デザインエージェンシー、およびデリバリーベンダーは、誰も信頼していない共有スプレッドシートを通じてほぼ排他的に連絡を取っていました。ミーティングは意思決定の場ではなく、責任追及の場と化していました。
技術的負債は現実に存在していました。しかし、リリースを実際に妨げていたのは、信頼とコミュニケーションの崩壊でした。
当社が提供したソリューション
当社は3つの明確なフェーズからなる90日間の固定スコープ立て直しを提案し、11月のピーク商戦期前にロイヤリティキャンペーンをリリースすることを商業条件の起点としました。
最初の2週間は、計画の削減、凍結、および再ベースライン化に充てました。当社はクライアントの経営陣とハードなスコープフリーズを交渉しました。リリースでは仕様書にある118機能のうち31機能を出荷することとし、これらは価値の最も高い3つの会員ジャーニーを網羅する基準で選定されました。それ以外はすべて、担当者を割り当てた文書化済みのv1.1バックログへ回されました。この単一の決定により、誰も要望していないエッジケースの対応に費やされていたであろう、およそ6週間分のエンジニアリング工数を回収しました。
次の6週間は、リスクの高い部分を再構築し、残りを維持することに充てました。既存のReact Nativeコードベースは、モバイルチームの作業自体は妥当であり、統合が不十分だっただけであったため保持しました。次に、構造的に破綻していた2つのコンポーネントを再構築しました。ロイヤリティAPIは共有PHPモノリスから切り出され、独自のPostgreSQLデータベース、会員検索に対する適切なインデックス、およびクライアント生成のUUIDをキーとする冪等な引き換えトランザクションを備えた小さなNode.jsサービスとして再実装されました。加えて、すべてのポイントトランザクションが生イベントから再構築可能となる軽量なイベントログを導入しました。これにより、財務チームはサポートチケットを起票することなく、ついに残高の監査を行えるようになりました。
最終月は、安定化、テスト、および引き継ぎに充てました。5店舗にわたる200名の実会員を対象としたクローズドベータを2ラウンド実施し、露呈したクラッシュをその都度修正しました。クライアントの新設社内チーム向けにランブックを執筆し、デプロイパイプラインを文書化し、引き継ぎ後にコードベースを保有することになる2名のクライアント側エンジニアをトレーニングしました。アプリは87日目、キャンペーン期限の3日前に両ストアへ出荷されました。
成功を支えた技術的ハイライト
3つのエンジニアリング上の選択が、重い作業の大半を担いました。
冪等な引き換えエンドポイントは、何ヶ月にもわたって会員の信頼を損なってきた二重引き落としバグのクラスを排除しました。モバイルクライアントからの引き換えリクエストはすべてUUIDを保持しており、バックエンドはそのUUIDをユニークインデックスに記録し、重複リクエストに対しては元のレスポンスを返して拒否しました。4G電波の悪い環境にいる会員が引き換えを3回タップしても、ポイントは1回しか失われなくなりました。
イベントソーシングされたポイント元帳は、可変な残高フィールドを、ポイントイベントの追記専用ログへと置き換えました。残高は派生的な状態となり、読み取り時に算出しキャッシュされました。財務チームにとって月次の悪夢であった突合作業は、単一のSQLクエリへと収まりました。これは新規のパターンではなく、金銭または金銭に類する価値を扱うあらゆる場面で用いられるべき正しいパターンです。以前のベンダーがこれを省いたのは、実装するよりも説明のほうが長くかかるからにすぎません。
iOSとAndroidで分離されたビルドパイプラインは、リリースサイクルを1週間から1時間未満へと短縮しました。2つのプラットフォームは互いを待たなくなり、片方でテストが失敗しても、出荷が止まるのはそのプラットフォームだけになりました。文書上は些細な変更ですが、1年にわたり恐怖に駆られて出荷を続けてきたチームにとっては、大きな士気の転換点でした。
結論
頓挫したソフトウェアプロジェクトが単一の要因のみで停滞していることは、まずありません。今回のケースでは、技術的負債は8週間で修正可能でした。より難しい問題はコミュニケーションの崩壊であり、それを解くには、機能追加にノーを突きつける立場と、その判断をCEOに対して弁護できる信頼性を備えた外部チームが必要でした。今回の立て直しが機能したのは、クライアントがスコープを凍結し、並行して信頼を再構築する意志を持っていたからであり、当社が何らかの奇抜な技術を持ち込んだからではありません。
もし貴社のチームが、予算を超過し、期限を過ぎ、ステークホルダーの信認を失いつつあるプロジェクトを目の当たりにしているのであれば、その答えがさらに9ヶ月と、より大規模なデリバリーチームであることは、ほぼありません。答えは通常、事業にとって意味のある一つの出荷イベントを軸に据えた、短く率直な監査に続く固定スコープの立て直し計画です。それこそがMerkTechsで当社が引き受ける仕事です。すなわち、進路を見失ったプロジェクトに新鮮で経験豊富な視点を持ち込み、余計なドラマなしに本番環境へと戻すことです。