はじめての品質

2020 T.Takeda

品質とは

  • 製品、サービスが使用目的を満たすことを評価する性質
  • 本来備わっている特性の集まりが、要求事項を満たす度合い、程度
  • 出荷後に社会に与える損失
💡ここではソフトウェア開発の品質について考える

失敗事例

  • とある製品で動作開始直後に0.5秒間のノイズが入る現象
  • 原因はソフトウェアによるレジストリの初期化漏れ
    • 状態を判断するメモリの1bitが常に初期値1となるハードウェアの特性をもつ個体でのみ発生
    • 事前のテストケースでは初期化したパターンを抜けもれなく全量テスト
    • 実機を用いたエージングテストでは検出できなかった
💡テストをしっかりやっていても市場に流出することはある
💡お客様にとっては1/1の実体験

顧客にとっての品質

  • 当たり前に備わっており、機能することを予期するもの
  • 機能に価値を見いだして、対価を支払うもの
  • 価値の度合いを左右するもの

狩野モデル(Kano model)

  • その機能があれば顧客はどう感じるか?
  • その機能がなければ顧客はどう感じるか?

当たり前品質

  • あって当たり前、ないと不満
  • これを超えることが常に求められる

一元的品質

  • あると満足、ないと不満足
  • 例)UIが使いやすい

魅力品質

  • あるとよいが、なくても文句は言われない
  • 例)他社との差異化機能

無関心品質

  • あってもなくてもよい
  • 例)利用規約、ロゴ

逆品質

  • あることで品質を下げるもの
  • 例)デザインにこだわりすぎて使いづらい

パレートの法則(PARETO Principle)

  • 80:20の法則
    • 価値の8割は2割の機能に含まれる
💡2割の機能はどれか?を早期に把握する

品質と開発速度(Quality and Speed)

  • 品質と速度はトレードオフの関係にない

    • 品質が高い人は開発も速い
  • 品質を後回しにするほど速度は低下していく(技術的負債

💡品質を犠牲にしなければ、守れない期日や体制が根本的な問題かもしれない

品質を高めるために

フールプルーフ(Fool proof)

  • 利用者が使い方を誤っても安全なように設計すること
    • ×こんなことしないだろう→○こんなことをするだろう

フェイルセーフ(Fail safe)

  • 故障したり、予期しないときに安全側に倒すこと
    • 「起こらない」ことはないと考える
  • とある車両の逆走事故
    • 進行方向を示す回路が断線
    • 元々の進行方向を維持してしまったため、終着駅にもかかわらず進行し車止めに衝突
    • 安全装置は「進行方向と逆の場合に作動」する仕組みだった

エラーとアサート(Error or Assert)

  • エラーは起こりえる正常系以外のパターン
    • ユーザーへエラーを通知する
  • アサートは想定しない、起こりえないパターン
    • ここのロジックに引っかかるときは故障や不具合がある
    • 動作しないように停止させる

0除算(Division by zero)

  • コンピューターは無限大を表現できない
    • 動作が不定になる
  • 母数が変数でゼロになり得るときは必ず事前にチェックする

メモリー管理(Memory management)

  • 確保、サイズ変更、解放のタイミングに注意
  • メモリリーク
    • 長時間稼働で挙動が徐々に怪しくなっていく(再現が難しい)
    • 影響を受ける範囲があらゆる箇所となり、不具合を起こす箇所が予測不可能になる
    • デバッガーなどで観測してリークしていないことをテストする

配列(Array)

  • メモリ同様に配列のサイズを意識する
    • 存在しないメモリアクセスは危険なので、範囲チェックをやる
  • 辞書の場合はキーの存在を意識する
    • なければNullとする言語処理系もあるが、やはり実装者が意識する

オーバーフロー(Overflow)

  • コンピューターはビット長に限界がある
    • 符号付きか、符号なしか
    • 32bitsか、64bitsか
    • 64bitsは32bitsの上位互換ではない
  • 2038年問題
    • 32bitsで表現できるUNIXエポックタイムの限界が訪れる
    • 半分である2004年以降の日付と日付を加算してもオーバーフローする

キャッシュ(Cache)

  • キャッシュは理解して活用する
    • なぜキャッシュしているのか
    • そこでキャッシュすることは適切か
    • キャッシュのライフサイクルは把握できているか
  • 特にメモリキャッシュはマルチプロセスで罠になる
💡メモリのミクロな話だけでなく、ネットワーク上でのCDNコンテンツキャッシュなどもある

型を意識する

  • 型を壊してはいけない
  • 一貫した型を使う
    • やむを得ず型を変えるときは明示的にキャストする

パフォーマンス(Performance)

  • 応答速度も品質の一つ
    • ユーザーの時間を奪うため、遅いことは比較的致命的である
  • 「動いたからよい」ではなく、パフォーマンスをよりよくできないか考える
    • データが増えたときに問題ないオーダーか?
    • 今は問題ないが、将来に繰り返し実行したときに問題にならないか?
  • 一元的な品質であることが多い
    • 待たせられる蓋然性があるケースではこの限りではない

試験をしっかりやる(Test)

  • 網羅的に考える
    • 通常系
    • エラー系
    • 異常系
    • 境界条件、範囲外(境界条件)
    • 初期状態、連続動作(冪等性)
    • 連続稼働(エージング)
    • ゼロ、少量、大量データ(限界値)

低品質は脆弱性にもつながる

  • バッファーオーバーラン
    • 不正なコードを実行させる攻撃
    • 入力サイズを意識・チェックする
  • XSS/SQLインジェクション
    • 不正なコードを実行させる攻撃
    • 制約チェックの甘さから起こる

品質は上流で押さえる

下流になるほど対応が重くなる

ユーザーストーリー/要求分析 仕様の品質はここで押さえる
モデリング/設計 設計の品質はここで押さえる
実装 ここで見つかる不具合は実装に起因するものだけにしたい
試験 最後の砦だが、修正対応を見込んでいないとリリースに影響する
運用/保守 ここで見つかると市場不具合となる

品質はさらに上流から決まっている

  • そもそもの開発体制、組織体制(Conway’s law)から品質が決まってくる
    • 品質を高く生み出せる体制か?
    • 品質を犠牲にしない進め方ができるか?
    • チームに必要なリソースはあるか?

設計/実装ではデザインパターンを正しく使う

  • デザインパターンの乱用はオブジェクト指向言語でありがち
  • 何でも継承しない
    • ほとんどの場合Composite patternで代用がきく
  • シングルトンは便利なグローバル変数ではない
    • ハードウェアが単一のように、真に単一な場合に利用する
  • オブジェクトにしたからといってインスタンスをあちこちで生成しない
    • Factory patternで集約する

コードレビューでは品質は上がらない

  • 品質は生み出された時点で決まっている
  • 流出を防止できるかもしれない程度
  • メンテナンス性や将来的な影響を人の目で見ることが目的

仕様か不具合か?

  • 満たすべき要求として定義づけられているかどうかで決まる
    • 考慮漏れは「仕様」でも「不具合」でもなく、結果的に「不定」であることが多い
  • 「それは仕様です。」という回答はユーザーからすると仕様の不具合
    • ユーザーがペインポイントを教えてくれているので、真摯に受け止める

ゲームにおけるバグ

  • ゲームは「ユーザーが楽しい」ことが目的
    • バグが楽しさを生むのであれば許容される
    • ゲームの進行を妨げるバグは致命的
    • デバッグコードは残さない方がよい

ツールに頼る

  • 静的解析ツール/コーディングチェック(Lint)
  • 網羅的に動かす(Coverage)
  • デバッガーの活用(IDE/DeveloperTools)
  • ベストプラクティスを知る(デザインパターン/定石)
  • アンチパターンを知る
  • テストケースを充実させる(Unittest/E2Eテスト)
  • CI/CDで自動化、効率化

「こうしたらどうなる?」

  • 「こうしたらどうなる?」を常に考える
    • ユーザーの使い方で考える
    • 意地悪な目線で考える
    • 最小粒度の要件一つ一つについて考える
    • 1行書いたら考える癖を付ける
💡不具合を引ける人、引けない人の差はここの想像力にある

レガシーとの向き合い方

  • 既存のコードは高品質か?
    • レガシーなコードは動いている実績はあるが、高品質かどうかはわからない
  • 安易な流用をしない
    • DRY原則とのバランス
    • コピー元の品質に引っ張られるため、理解していないコードは盲目的に流用しない
    • 新しく実装しても単なる重複となる場合には流用する

ユーザーとの接点を持つ

  • アプリストアの対応は真摯に
  • レビュー一つ一つに気を配る
  • ユーザー行動の分かるデータを集める
  • NPS計測を行う

より高い品質ライフを!