スクラム鳥獣戯画

とあるPJの立て直し: 品質をあげるために

この記事の目次

 とあるプロジェクトの立て直しの記録に基づいて、プロジェクトマネジメントをテーマにシリーズで書き残したいと思います。今回は品質を上げるために行ったことについて触れます。

要旨#

  • 品質の性質に応じてそれぞれ対応しよう
  • 小手先ではなく設計から見直そう
  • 技術的負債の解消に注力しよう

主な品質の課題#

 本プロジェクトで顕在化していた品質の課題は設計の段階から発生していました。アーキテクチャそのものを見直さなければ改善しないものがほとんどです。

  • クラウド設計の課題
  • データベース設計の課題
  • コーディングの課題
  • アーキテクチャ設計の課題

 それぞれ個別に対応する必要があります。


クラウド設計の課題#

 今回はクラウドを用いたシステム開発を行っていました。しかし、クラウドを使用したことのない人が初期設計したと推測され、クラウドのよさを活かせていませんでした。例えば、クラウドの特徴の一つにスケーリングメリットがあげられますが、スケーリングできる部分とできない部分があります。この点を理解せずに、すべてリソースパワーに頼る前提となっていたため、すぐに制約に引っかかることが明らかでした。

  • クラウドがスケールする前提で力業に頼る設計になっていた
  • サーバーレスアーキテクチャを採用していたが、要件と合致していなかった
  • 各種制約を意識しない設計になっていた

 クラウドだからと無尽蔵にリソースがあるわけではなく、あらゆるサービスで制約を持ちます。確かに上限値を引き上げることができる側面もありますが、上限値の引き上げは十分に稼働しスケーリングの必要性が出てきたときに行うものです。まずは制約を理解して制約の中で正しく動作するように設計すべきです。クラウドだからとリソースパワーに頼る前提の設計は早々に破綻しますので、注意しましょう。

 また、サーバーを自前で持たないサーバーレスアーキテクチャもクラウドならではの特徴の一つです。例えば、リアルタイム性や応答速度をそれほど求められず、べき等性の高い処理などでは利便性が高まります。他方で、大きなデータを取り扱ったり、時間の掛かる処理には適していません。要件に合致しているかは慎重に判断して採用しましょう。

  • クラウドの制約を理解したアーキテクチャに変更する
  • リソースパワーに頼らずにスケールできる仕組みに変える
  • セキュリティを考慮した設計にする

 クラウド設計の課題は設計者のスキルアップが必要です。


データベース設計の課題#

 データベースはシステムの中心に位置するものです。データベースの設計がなされていない点は品質を大きく下げている要因となっていました。

  • NoSQLで設計したものを関係データベース(RDB)化していた
  • 関係データベースにも関わらず、正規化されていなかった
  • 全件取得を前提とした設計になっていた
  • トランザクションが意識されておらず一貫性が保たれていない
  • 実運用を考慮しておらず、単一のデータベースのみでバックアップがない

 1点目は通常起こりえないと思いますので補足しておきますと、元々Key-Valueペアで情報を取り扱う前提で設計していたためNoSQLの構造でデータをもっていました。ところが、ユーザーと紐付く1:1のデータを取り扱うよりも、横断的に情報を取得する管理システムに近い性質であったため、「ユーザー自身の情報は取得しやすいが、横断的な情報の取得がしづらい」という問題に早々にぶつかっていました。私が引き継いだ時点で、この「NoSQLをRDBに置き換えるところまで終えました」という不思議な状態で、なぜそうなったのかという議論の余地もありませんでした。

 2点目は前述の流れを引きずっているのですが、関係データベースに置き換える作業をNoSQLのスキーマのまま行っていました。NoSQLのベストプラクティスとRDBのベストプラクティスは異なります。必ず再設計が必要になりますが、RDBに向けたデータベース設計を行わずにデータベース形式だけを移行させたことが問題です。正規化されていないRDBはパフォーマンスやデータの管理に様々な問題を引き起こします

 3点目はそもそものデータの使い方の問題ですが、全件データを常にフロントエンドに取得して処理する思想になっていたため、データ量が増えると急激にパフォーマンスが低下する状態にありました。100MBのデータを一回のAPI呼び出しで取得しようとする挙動は初めて見ました。ページネーションやLIMITを活用して必要な情報を必要な範囲で取得するように考え直す必要があります。

  • ER図を作成し、正規化を考慮してデータベースを再設計する
  • データを全量取得することをやめて、必要なデータ取得を行えるように再設計する
  • トランザクションやカスケード更新などを活用し、データが不整合しないようにする
  • データベースを冗長化し、実運用に耐えられる設計にする

 データベース設計の課題は設計者、開発メンバーのスキルアップが必要です。


コーディングの課題#

 このときは、プログラミング言語としてPythonとJavaScriptが採用されていました。どちらにも言えることですがスクリプト言語であるため、C++やJavaのようなオブジェクト指向でコンパイラ型の言語に比べて、品質の担保がより難しい言語です。初期実装の容易性はありますが、実働環境としての堅牢性の担保にはより一層の注意が必要です。

  • テストが正常系の1ケースのみしかない
  • エラーが考慮されていない
  • 型を意識しておらず、容易にエラーする
  • 計算オーダーが意識されていない
  • ライブラリの特性を理解せずに使用している

 テストケースがゼロでないことはよかったですが、1APIにつき正常系の1ケースのみのため、値のバリエーションをちょっと変えるとうまく動かない箇所が多くありました。仕様が不明でテストケースがない箇所ではどのような振る舞いが正解なのかわからないため、そこから考え直す必要がありました。エラーケースのテストがないため、実装にもエラーケースを考慮した部分がありません。アプリケーションエラーなのか、予期せぬエラーなのか判別できません。

 型の重要性については機会があれば別の記事として触れたいと思いますが、品質の担保を容易にしてくれる重要な概念です。

 最後の2点はパフォーマンスに関わる課題です。ループが5重になっていたり、必要のないデータまで取得して処理するため、少量のデータにもかかわらず長時間待たされるような実装が散見されました。

  • とにかくテストケースを拡充する
  • エラーケースを定義し、正しくエラーするようにする
  • JavaScriptからTypeScriptに移行する
  • ライブラリの使い方を勉強会
  • パフォーマンスを考慮した記述ができるように勉強会

 コーディングの課題は開発メンバーのスキルアップが必要です。


アーキテクチャ設計の課題#

 ここまでの前述の問題も広くはアーキテクチャ設計の課題ですが、これら以外の側面でもアーキテクチャ上の課題がありました。

  • アーキテクチャ図がない
  • 要件定義がない
  • ドキュメントがない
  • パフォーマンス目標や制約がまとまっていない
  • モジュール単位で設計されていない
  • 必要以上に抽象化されており、不要な依存を生み出している
  • ハードコーディングされている箇所が多い
  • エラーをすべて握りつぶしている

 引き継ぎ段階でアーキテクチャ図が存在しなかったため、アーキテクチャ図を起こしながら全体像を把握する作業を行いました。結果的には内容の把握と課題の把握を同時に効率的に行えました。要件定義書や設計に掛かるドキュメントがないため、どのような振る舞いが正解なのかわからない状況でした。これについては、分かる範囲でくみ取りつつ、もう一度顧客と調整し前進することを選択しました。

 「うまく動かないのにエラーがでていない」という状況に度々出くわしました。驚くことにエラーを補足し握りつぶしている処理が入っていたのです。これはさすがに想定外で予見できませんでした。これでは自分たち自身もデバッグができません。このような箇所を特定し、エラーが出るようにすると大量のエラーが出力されるようになりました。エラーを握りつぶしてもエラーがなくなるわけではありません。絶対にやってはいけません。すべてのエラーに正面から対応しました。

 アーキテクチャ設計の課題は設計者のスキルアップが必要です。

  • ドキュメントを残す活動を行う
  • 前述のすべての問題に対応するために開発を一時停止し、技術的負債の解消に注力する

 品質を上げるために最も重要だった要素は、開発を一時停止し技術的負債の解消に注力することでした。前述のように課題が明らかになり対応方法が見えていても対応ができなければ画に描いた餅です。顧客との合意が不可欠です。次回は顧客との関係性について触れたいと思います。