テスト工程・レビュー工程の全体の建付けを考える際に留意が必要な効果・法則

 テスト工程やレビュー工程の建付け(どのようなテスト工程をプロセス上に設けるか等)を考える際に、注意すべき効果や法則があります。今回はそのいくつかを紹介します。

多重チェックの落とし穴

 問題を見逃さないように、チェックを何重にも多重化すると、問題検出率がむしろ悪化するという傾向を多重チェックの落とし穴と呼びます。
 例えばレビューでのミス検出率について、レビューを1重化(一人が一回だけレビュー)すると検出率は65%になり、2重化(二人が別のタイミングでチェック)すると80%に改善する一方で、3重化すると65%、4重化すると55%と、多重化を増やすほど検出率が下がる調査結果が存在します。

 これは、レビューの多重化を増やすほど、個々の問題検出率も、全体の問題検出率も下がっていく傾向を示します。この傾向は多重化により自分が注力しなくても他が見つけてくれるという期待が発生することで生まれると考えられています。
 より難しい問題を見つけて高品質を実現したいという目的に対しては、逆行する傾向と言えます。

 この傾向は品質保証の体系や仕組みを構築する際にも注意が必要です。高品質の実現のため同じようなテスト工程・レビュー工程を何重にも設けても、工数やコストを悪化させる一方で品質を悪化させる可能性があるためです。
 なおこれは最初から「他者が見るから手を抜こう」という悪意を持った担当を避けても、発生しえる落とし穴です。というのも現場の開発ではスケジュールやリソースの不足に追われる場面は少なくありません。そこで他に同じようなテスト工程があれば、やむを得ず自分達はテストより他の作業を優先する、という判断はしばしば発生します。

 この多重チェックの落とし穴を回避して高品質を実現するためには、次のような工夫が求められます。

  • レビューやテストを多重化させる場合は、それぞれの視座や観点に違いを設け、各レビュー・テストのバリエーションを増やす
  • 各レビューやテストの責任を明確化する。例えば特定のバグはどこで防ぐべきか具体化する
  • レビューやテスト、あるいは品質保証についてのリーダーとその責任を明確にし、リーダーシップを発揮させる 

リンゲルマン効果・社会的手抜き

 共同作業をする際に、分担する人数が増えるほど、個々の個人の貢献やリソースパワーが減っていく効果を、リンゲルマン効果や社会的手抜きと呼びます(元の文献は、綱引きで人数を増やすほど個々人の力の入れ具合が弱まる傾向を示す)。これは、人数が増えるほど、個人の貢献や責任が希薄になるほか、連携のために効率が低下することで発生すると考えられています。

 このリンゲルマン効果・社会的手抜きも、多重チェックの落とし穴と同じように、テスト工程・レビュー工程の体系や建付けの構築において注意が必要です。例えばレビューを強化するにしても単純にレビューアを増やすだけだと、個々のレビューアの力が活かされにくくなる可能性があるためです。

 高品質の実現のためには、個人の能力の発揮を引き出すことが求められます。そこでこのリンゲルマン効果・社会的手抜きを回避して高品質を実現するためには、前述の多重チェックの落とし穴と同様のアプローチが求められます。すなわち各人に異なった視座や観点を割り当てる、各人の責任を明確化する、責任あるリーダーにリーダーシップを発揮してもらう、といった工夫が求められます。

今年の登壇・執筆活動まとめ

 登壇は基本的に受け身のみで、お声がけいただいたら対応しています。ただそれでも、今年は6月にソフトウェアテスト徹底指南書を出版してからありがたいことに多数の登壇依頼をいただき、かなりの講演・登壇をすることになりました。自分は講演上手でないと自覚しているので、ありがたい一方で、大変恐縮しています。振り返りとして、今年の登壇活動・執筆活動のうち、オープンにできるものをまとめたいと思います。

登壇系

■講師「テスト分析入門」

CADDi様社内勉強会

https://speakerdeck.com/goyoki/test-analysis-tutorial speakerdeck.com

■講演「チームのテスト力を総合的に鍛えて品質、スピード、レジリエンスを共立させる」

「チームのテスト力を総合的に鍛えてソフトウェア開発の高品質と高スピードを両立させる実践技法」勉強会

https://speakerdeck.com/goyoki/testing-approach-that-improves-quality-speed-and-resilience speakerdeck.com

■講演・パネルディスカッション「書籍著者が語る!高品質と高スピードを両立させる!ソフトウェアテスト徹底指南書のご紹介」

コニカミノルタ様社内勉強会

■講師「プロダクト開発を成功させるためのソフトウェア品質保証のアプローチと技術」

DXPO東京25夏

https://speakerdeck.com/goyoki/software-qa-approach-for-puduct-success speakerdeck.com

■LT・パネルディスカッション「著者・訳者が徹底指南!2冊のフルスタックな書籍に学ぶ『テスト』の極意」

https://speakerdeck.com/goyoki/introduction-for-software-test-tettei-shinansho speakerdeck.com

■講演「チームのテスト力を鍛える」

「『ソフトウェアテスト徹底指南書』に学ぶ、品質とスピードを高める実践アプローチ」勉強会

https://speakerdeck.com/goyoki/develop-teams-testing-capabilities speakerdeck.com

■招待講演「チームのテスト力を総合的に鍛えてシフトレフトを推進する」

Veriserve Mobility Initiative 2025

https://speakerdeck.com/goyoki/shifting-left-with-software-testing-improvements speakerdeck.com

■壇上コンサルティング「テストデータの準備・保守の終わらない戦い…適切な方法は?」「AIが品質保証をする未来はどうすれば創れる?」

JaSST Online Gerbera
https://jasst.jp/online/gerbera-about/

■招待講演「自動テストを活かすためのテスト分析・テスト設計の進め方」

JaSST’25 Shikoku

https://speakerdeck.com/goyoki/jasst25-shikoku speakerdeck.com

■招待講演「 開発に寄りそう自動テストの実現」

ソフトウェアテスト自動化カンファレンス2025

https://speakerdeck.com/goyoki/automated-testing-integrated-with-development
speakerdeck.com

執筆系

■書籍「ソフトウェアテスト徹底指南書 〜開発の高品質と高スピードを両立させる実践アプローチ」

技術評論社
ソフトウェアテスト徹底指南書 | 技術評論社

■研究論文「Componentwise Automata Learning for System Integration」(共著)

ATVA 2025
https://arxiv.org/pdf/2508.04458

Bridgeパターン、Type Erasureパターンによる関心の分離の推進

 ソフトウェア設計で普遍的に重要な設計アプローチ:関心の分離(SoC)は、高凝集設計、疎結合設計両方を要求します。具体的に、高凝集設計の推進で、コンポーネントの責務が関心ごとに集中している設計を実現します。疎結合設計の推進で、コンポーネントに割り当てられた関心ごとが構造的に分離されている設計を実現します。

 この関心の分離は、抽象的な責務設計から詳細なコード実装まで、抽象・具体を横断的に工夫しながら推進する必要があります。コード実装なしに考えることができません。高凝集設計は契約による設計といった責務設計で推進できる一方で、疎結合設計は具体的なインターフェースやバウンダリの実装に依存しているためです。
 今回はこのコード実装の工夫による疎結合設計の推進で、関心の分離を支えるアプローチを解説します。

対象の題材

 今回は以下の仕様をC++で実装する場合を考えます。

  • ある制御システムの通信処理が対象。サンプルコードのoperate()はその一つ。operate()含め通信処理は共通化して実装する。
  • 通信処理の詳細はプラットフォームごとに異なる。プラットフォームはModel A、Model Bがある。今後も増える。プラットフォームごとに依存ライブラリが大きく異なる
  • 通信処理の詳細は通信仕様バージョンごとにも差異がある。仕様バージョンはcom_v1、com_v2があり、今後も増える。

 上記に対して、プラットフォーム、通信規格、それらを利用する通信処理実行者を関心事として関心の分離を実施し、疎結合にする方針をとります。

関心の分離が破綻した駄目なコード

 関心の分離の点で悪い実装として、グローバルなフラグなどを観てDependency Lookupで処理を呼び出すパターンがあります。

void operate(int param) 
{
    if (comm_ver_ == PF_MODEL_A) {
        CommModelA::operate(param);
    } else if (comm_ver_ == PF_MODEL_B) {
        ...何かの特殊処理...
        CommMldelB::operate(param);
    } else if {
    ..フラグを使った分岐が続く..
}

 こうした実装は疎結合設計が破綻しており、関心の分離に失敗しています。インターフェース共通化を行えず、プラットフォーム・通信規格の関心ごとがぐちゃぐちゃにまとめられていて、関心ごとに分けて考えられなくなっています。またフラグの分岐構造が複雑化するほか、この分岐構造のコピーが散在する形となり、プラットフォームや通信仕様の増加に対応する保守性が崩壊しています。

ダメなコード:継承を使った関心の分離

 オブジェクト指向言語では、今回のプラットフォーム、通信仕様といった横断的なバリエーションがあるコードに対応する手段として、継承が使われる場合があります。
 継承を使う場合、次のように抽象クラスでインターフェースを共通化します。

// 通信処理の共通IF
class CommunicationBase {
public:
    virtual ~CommunicationBase() = default;
    virtual void operate() const = 0;
};

 そしてバリエーションごとに派生クラスを定義し、処理部を共通化します。

class PFModelAComm : public CommunicationBase {
public:
    void operate() const override {
       ...
    }
    ...
};

class PFModelBComm : public CommunicationBase {
public:
    void operate() const override {
       ...
    }
    ...
};

...

 継承の導入により、通信処理の実行者視点では、バリエーションを共通記述で扱えるようになります。

std::unique_ptr<CommunicationBase> comm;

...

//PF Model Aの制御を行う場合
comm = std::make_unique<PFModelAComm>(); 
...
comm->operate();
...

 ただこの継承依存の対応は問題が大きいです。例えば今回の例のようにプラットフォームに通信規格と、関心ごとの種類が複数ある場合、派生クラスが次のように爆発するか、保守性に問題ある多重継承に頼る形になります。

class PFModelA_V1_Comm : public CommunicationBase {
    ...
};

class PFModelA_V2_Comm : public CommunicationBase {
    ...
};

class PFModelB_V1_Comm : public CommunicationBase {
    ...
};

class PFModelB_V2_Comm : public CommunicationBase {
    ...
};

...

 これはバリエーションの増加に対する保守性が破綻しているほか、プラットフォーム、通信仕様と関心ごとの分離ができておらず、組み合わせが一体化しています。これは関心の分離が失敗している状態です。
 疎結合設計、関心の分離に継承だけで対応するのは多くの場合でベストではありません。また抽象インターフェースの仮想関数は処理にオーバーヘッドがかかり、効率の点でもよくない手段と言えます。

良い例:BridgeパターンやStrategyバターンを使った関心の分離

 今回の例のような、プラットフォーム、通信仕様と複数の関心がある場合で、関心の分離を行いつつ、呼び出し部を共通化して保守性を確保するのに有用なのが、BridgeパターンやStrategyパターンです。これらはバリエーションの組み合わせ対応をすべて継承で対応するのではなく、合成で対応します(いわゆる「継承でなくコンポジションの原則」)。
 今回のサンプルの場合、次のような抽象化層を定義します。

class Communication {
protected:
    std::unique_ptr<CommunicationImpl> pimpl_;
    
public:
    Communication(std::unique_ptr<CommunicationImpl> impl) : pimpl_(std::move(impl)) {}
    virtual ~Communication() = default;

    void operate() const {
        if (pimpl_) {
            this->send_operation();
        }
    }
    
protected:
    virtual void send_operation() const = 0;
};

 バリエーションをすべて継承で対応せず、プラットフォーム対応はCommunicationImplクラスのメンバオブジェクトで対応します。プラットフォーム対応の実装は次ようなコードになります。

class CommunicationImpl {
public:
    virtual ~CommunicationImpl() = default;
    virtual void send_data(const std::string& version) const = 0;
};

class ModelAPlatform : public CommunicationImpl {
public:
    void send_data(const std::string& version) const override {
       ...プラットフォームModel A固有の処理
    }
    ...
};

class ModelBPlatform : public CommunicationImpl {
public:
    void send_data(const std::string& version) const override {
       ...プラットフォームModel B固有の処理
    }
    ...
};

 通信仕様対応は次のような継承で対応します。

class ComV1 : public Communication {
public:
    using Communication::Communication;
    
protected:
    void send_operation() const override {
        ...V1固有処理
    }
};

class ComV2 : public Communication {
public:
    using Communication::Communication;
    
protected:
    void send_operation() const override {
        ...V2固有処理
    }
};

 すると、例えばoperate()呼び出し部は次のように共通化して記述できるようになります。

std::unique_ptr<Communication> comm;

...

// Model A、通信仕様V1の処理を行う場合
comm = std::make_unique<ComV1>(std::make_unique<ModelAPlatform>());    
...
comm->operate();
...

 こうしたBrigeパターンやStrategyパターンの導入で、関心ごとに実装を分けて疎結合にできます。特に今回のパターンでは、プラットフォーム依存のコードが完全に分離されるため、依存ライブラリの分離が促進されます。これにより、関心の分離を実現しつつ、さらにバリエーション増加に対応するための保守性も確保できます。

モダンなC++での良い例:Type Erasure(型消去)パターンによる関心の分離

 前述のBridgeパターンでは、保守性の確保と関心の分離の両立を実現できました。ただ関心ごとのバリエーション対応では、値セマンティクスに完全に対応し、例えばプラットフォームが異なるオブジェクトを一つのコンテナでまとめて管理、といった実装を行いたい場合があります。
 これに対する対応策として、モダンなC++ではType Erasureパターンの導入で、C++でありながら実行時多態を実現しつつ、参照セマンティクスから値セマンティクスに移行させるアプローチがあります。

 今回のサンプルでType Erasureパターンを導入する場合、次のType Erasureの抽象クラスを定義します。

class CommConcept {
public:
    virtual ~CommConcept() = default;
    virtual void operate() const = 0;
};

template <typename T>
class CommModel final : public CommConcept {
private:
    T object_;
public:
    template <typename... Args>
    CommModel(Args&&... args) : object_(std::forward<Args>(args)...) {}

    void operate() const override {
        object_.operate();
    }
};

class AnyCommunication {
private:
    std::unique_ptr<CommConcept> concept_; 
public:

    template <typename T>
    AnyCommunication(T object) 
        : concept_(std::make_unique<CommModel<T>>(std::move(object))) 
    {}
    
    AnyCommunication(const AnyCommunication& other) = delete;
    AnyCommunication& operator=(const AnyCommunication& other) = delete;
    
    AnyCommunication(AnyCommunication&&) = default;
    AnyCommunication& operator=(AnyCommunication&&) = default;

    void operate() const {
        if (concept_) {
            concept_->operate();
        }
    }
};

 こうしたType Erasureパターンを導入すると、operate()の呼び出しを次のように実装できるようになります。

//様々なバリエーションのオブジェクトを同一のvectorに格納してしまう
ComV1 comm_v1_a(std::make_unique<ModelAPlatform>());
ComV2 comm_v2_b(std::make_unique<ModelBPlatform>());
std::vector<AnyCommunication> any_comms;
any_comms.push_back(std::move(comm_v1_a));
any_comms.push_back(std::move(comm_v2_b));

...

//コンテナに対する一括実行が可能になる
for (const auto& comm : any_comms) {
    comm.operate();
}

 こうした実装を行うと、プラットフォーム依存部、通信仕様依存部と利用者の結合をより疎にでき、より関心の分離を推進できます。

データ汚染と汚染防御

セキュリティ脆弱性をついて不正な操作やアクセスを行うための異常コマンドなど、危険な入力データを汚染データと呼びます。例えばSQLインジェクションでの悪意あるSQLコマンドの入力が汚染データの一種です。

そして汚染データの害を無効化する対策を汚染防御と呼びます。また汚染データがどのようにコードで広がっていくか、汚染データがシンク(汚染データが実害を発生させる脆弱性のポイント)に到達するか解析する活動を汚染解析と呼称します。

例えばかなり極端な例ですが、以下のようなコードがあるとします。

import sqlite3

input = request.args.get('command')

conn = sqlite3.connect('secure_database.db')
cursor = conn.cursor()
cursor.execute(input)

このコードは外部からの入力をそのままsecure_database.dbへのコマンドとして実行しています。ここで外部から入力される不正なSQLコマンドが汚染データであり、SQLコマンド実行部分がシンクになります。

汚染防御

汚染データが実害を発生させるのを防ぐ機構を汚染防御と呼びます。
汚染防御としては以下があります。

  • 入力バリデーション。外部からの入力データに害がないか確認し、問題があればブロックする
  • サニタイズ。有害な可能性のある文字を、安全なものに置換・除去する
  • 汚染防御済みのサポートの使用。汚染防御が実装されたAPIフレームワークを使用する。例えば前述のコードの例ならば、プリペアドステートメントを使用し、実害を及ぼさないようにする

汚染防御を困難にする脆弱性

汚染対策は前述の例ですが、こうした入力に対する汚染防御を台無しにするものがあります。例えば次のようなものです。

  • バッファオーバーフロー。別領域のメモリに汚染データを書き込めるため、汚染データが特定不能に広がる
  • 境界外読み込み(Out-of-Bounds Access)。まったく別のコードから、汚染防御を適用する前の汚染データを参照できてしまう可能性を高める

こうした脆弱性は、汚染防御で品質リスクを抑え込めなくしてしまう深刻なものですが、昔からC/C++で書かれたソフトウェアでの定番の脆弱性になっています。これはCの採用を止める、C++は最新の言語仕様のみを使う、といった手段が推される背景の一つとなっています。

テスト設計にも使える設計原則:SoCの原則とSLAP

SoCの原則とSLAP

ソフトウェア設計の原則にSoCの原則とSLAP(単一抽象度レベルの原則)があります。これら原則は、アーキテクチャやコードの責務設計・構造設計の基本となります。以下それぞれ解説します。

SoCの原則

SoCは「Separation of Concerns(関心の分離)」の略称です。SoCの原則は、対象を関心ごと(何をしたいのか、何をすべきか)を基準に分離すべき、という原則になります。これは次の目的で実施します。

  • 規模が大きく複雑で扱いにくいものを、扱えるサイズになるまで関心ごとを単位に細分化します。
  • 責務やコンポーネントを、関心ごとを基準に設計することで、責務やコンポーネントの凝集性を高く確保します。これにより開発しやすさや、保守性(理解しやすいさ・テストしやすさ)の高さを確保します。

例えばウェブサービスアーキテクチャ設計で、ユーザ管理、認証、モニタリング、決済、注文と、関心ごとにサービスやコンポーネントの責務を割り当てるアプローチが、SoCの原則推進の一種です。それにより、以降は認証、決済といったサービスごとに担当を分けて開発を進められるようにします。

このSoCの原則は、医療機器ソフトウェア開発で特に重要視されます。医療機器ソフトウェア開発では、アーキテクチャ設計でのコンポーネント設計で関心の分離を実施した上で、各関心ごとで懸念レベル(Level of Concern)を分析します。そして関心ごとの懸念レベルに応じて、開発の厳格さを切り分けます。例えば手術装置ならば、電気メス制御など懸念レベルの高い関心ごとを割り当てたコンポーネントには厳格で重厚な開発プロセスを、USBメディア制御といった懸念レベルが高くない関心ごとを割り当てたコンポーネントには軽快な開発プロセスを適用します。

SLAP

SLAPは「Single Level of Abstraction Principle」の略称です。日本語で単一抽象度レベルの原則と呼ばれます。SLAPは、同じレイヤならば、設計やコードの抽象度を揃えることを求める原則です。例えば「同じメソッド内ならばコードの抽象度を揃えるべき」「同じレイヤなら属するコンポーネントの抽象度を揃えるべき」といった原則となります。
SLAPの推進により、理解しやすく、保守しやすい設計・コードを実現できます。

このSLAPを実践したアーキテクチャとしてレイヤドアーキテクチャがあります。例えば車載ソフトウェアのアーキテクチャ標準のAUTOSARでは、MCAL、ECUCAL、サービスレイヤなど、抽象度に応じたレイヤを設定し、直にマイクロコントローラやハードウェアを制御するコードはMCALやECUALに閉じ込めて、それより上のレイヤ(CDD除く)では一貫してハードウェアを抽象化したコードを書くようにする、レイヤごとの抽象度の統一を行っています。

テスト設計で使えるSoCの原則/SLAPの原則

上記のSoCの原則、SLAPは、レイヤー構造やツリー構造の設計・実装を行う際に普遍的に使えるものです。これはテスト設計でも同様です。

例えばテストコードの実装では、次のような推進が有効になります。

  • SoCの原則の推進:テスト設計で抽出した、テストコンテナやテストスイートを単位に、テストコードのクラスやモジュールを分ける
  • SLAPの推進:テストクラス、テストメソッドの抽象度を統一する。また、Page ObjectやFacadeパターンといった抽象化・ラッピングを行う設計パターンを採用する際はそれに応じた抽象化の統一を行う。例えばPage Objectパターンならば、Page Objectに具体的なテスト対象制御の記述を書き、テストコードでは抽象化されたテストのWhatを書くように抽象度を統一する

次にテスト設計でも、次のような推進が有効になります。

  • 全体のテストの責務を設計する際は、品質特性や、抽象度の高いテストの要求など、テスト分析に応じた関心ごとを単位に、テストレベルやテストタイプを分割する
  • 全体のテストの責務をツリー構造で設計する際は、兄弟ノードの抽象度を合わせる。例えば、テストを責務を、最初のレイヤでテストレベルに分け、次のレイヤで抽象的なテストタイプに分け、次の末端レイヤで具体的なテストタイプに分ける、といった全体設計アプローチを取る

ソフトウェアテスト徹底指南書を執筆しました

 2年前から、ソフトウェアテストをテーマにした「ソフトウェアテスト徹底指南書」を執筆していました。今月、その書籍が発売されます。
 今回はその書籍の概要と、執筆活動記をまとめたいと思います。

gihyo.jp

ソフトウェアテスト徹底指南書

 ソフトウェアテスト徹底指南書の内容は、開発チームが備えるべきソフトウェアテストの技術・アプローチ・知識を包括的に・体系的に解説したものになっています。開発チームを構成する様々な方(テストエンジニアに限らず、開発者やマネージャも含め)に広く読んでいただければと思います。

 アウトラインは次のようになっています。

  • Part I ソフトウェアテストと品質マネジメント
  • Part II テストの戦略とプロセス
    • テスト戦略
    • シフトレフトテストなど定番のテスト戦略
    • アジャイル開発、継続的デリバリ、DevOps、SPLEを支えるテスト戦略
    • テストプロセスの構築
  • Part III テストの作成と実行
    • テスト分析
    • テストアーキテクチャ設計/VSTeP
    • テスト設計
    • テスト実装
    • テスト環境構築
    • テスト設計技法の活用
    • テストの実行
    • ユニットテストの設計/性能テストの設計
    • リスクベースドテスト/探索的テスト/ユーザーストーリーテスト/静的テスト
  • Part IV 自動テストの活用
    • 自動テストの開発
    • 自動テストの品質作りこみ/フレーキーテスト/脆いテスト
    • 自動テストの評価/ミューテーションテスト
    • 自動テストの設計・実装の原則
    • 自動テストコードのパターン・イディオム
    • 開発者テスト
    • テスト駆動開発
  • Part V テストの計画とマネジメント
    • テスト計画
    • テストのモニタリングとコントロール
    • リスクマネジメント
    • テストで求められる能力
    • テストを担う組織の構築
  • Part VI テストを支える基礎作り
    • CI/CDの構築
    • バグ管理とバグチケット設計
    • テスト容易性の確保
    • テスト設計を支えるモデリング
    • テストを支える契約による設計
    • ブランチ管理とテストの連携
    • システムエンジニアリングで支えるテスト

 Partごとに独立して執筆しているため、読み方としては気になるPartをピックアップして選択的に読んでいただければと考えています。

執筆活動記

執筆のきっかけ

 本書の執筆のきっかけは、出版社の方が本ブログを読み、ブログ記事のようなテーマで本を執筆してみないかと直接連絡していただいたのがきっかけでした(はてなブログ様々です)。もともとテストをテーマとした本を書きたいと考えていたため、これ幸いと機会に飛び乗りました。そこから提案書とアウトラインをまとめ、出版社とすり合わせたのち、契約を結んで、執筆を開始しました。

執筆の経過

 執筆活動は2023年10月から本格着手し、2024年7月に脱稿しました。
 執筆進捗は文字数と清書度で管理していました。グラフは次のようになっています。

 執筆の主な課題は、本業との両立でした。
 当初の目論見は年末年始の連休で一気に書こうと想定していましたが、理想のようにはいきませんでした。(グラフ中の理想進捗線が年始に計画変更されている通りです)。
 自分は一気にたくさん書けないということで、そこで毎日2p分、(たとえ体調を崩していたり、酔っていたり、深夜残業があったりする中でも)書き進めることを習慣化する戦略に転換しました(Githubの草をはやす習慣づけを書籍執筆でやりました)。この書き方は自分の性分と合っていたようで、従来の働き方を変えずに、半年程度で原稿を書き上げることができました。

レビューと洗練

 書き上げた原稿に対しては、多くの方にレビューに助力いただきました。

 本書は、急逝された西康晴先生(にしさん)への恩義に報いたいという思いが執筆の原動力の一つとなっていて、にしさんの成果を商業出版で記録したいという思いを強く持っていました。その思いは、にしさんと長く共同で教材開発していた、JaSST Tohoku実行委員会の梅津正洋さん、浦山さつきさん、高橋理さん、竹内亜未さん、真鍋俊之さんの監修・レビューのご協力で実現されました。本書のVSTePの執筆は、その監修の力があってこそ実現されました。
 また妻、末村拓也さん、鈴木一裕さんには、全体にわたって詳細にレビューいただき、本書全体の洗練にご協力いただきました。末村さん、鈴木さんいずれも、本業と並行して書籍の翻訳や登壇、国際規格の作成など多忙な生活を送っている中にも関わらず多くの時間を投じていただき、深く感謝しています。
 さらにテーマに応じてエキスパートの方々にレビューをいただきました。静的テスト・プロセス改善については安達賢二さん、テスト自動化については山口鉄平さん、テスト設計については秋山浩一さん、伊藤由貴さん、河野哲也さん、テスト戦略については風間裕也さん、テストマネジメントについては朱峰錦司さん、松木晋祐さんにレビューいただきました。中でも秋山さんにはテスト設計について厳しく適確にご教示いただき、本書の洗練だけでなく、自身のテスト技術の向上の機会にもなりました。

 改めてご協力ありがとうございました。

今後の活動について

 本書を脱稿した後も、毎日技術文章を書く習慣が抜けず、暴走気味の執筆意欲も収まらずで、別テーマで新たな技術本を執筆しています(Mark Wardさんに出版社ととりなしていただき、ご機会を作っていただきました)。こちらは来年以降、情報を出せればと考えています。
 

人材採用でのマスアプローチからターゲットアプローチへの転換

 優秀なQAエンジニアは、チームやプロジェクトに大きな影響力を与えることがあります。例えば、チームの品質保証能力をイネーブリングして、迅速で的確な品質保証を実現します。チームを顧客満足に向け方向づけして、チームの力をより効率的に顧客価値につながるようにします。また、シフトレフトを促進して、チームの品質保証のコストや迅速性を高めます。
 こうした大きな影響力から、優秀なQAエンジニアの採用はソフトウェア開発でしばしば重要な課題になります。

 ただQAエンジニアに限らず優秀なエンジニアは売り手市場であり、他社との激しい競争になります。
 そこではターゲットとなるエンジニアに、競合他社より自社が魅力的であることを訴求して応募してもらう必要があります。大企業などに多い、エンジニア層になるべく広く告知して広く働きかけるマスアプローチでは、そうしたターゲット向けの働きかけの余力がなくなってしまうこともありえます。
 すなわち、優秀なエンジニアの採用では、人材採用の基本方針をマスアプローチからターゲットアプローチに転換する必要があります。

マスアプローチからターゲットアプローチへの転換

 マスアプローチからターゲットアプローチへの転換では、求人活動の多くでアプローチが変わります。その変化を以下にまとめます。

人材要項の表現
  • マスアプローチ:多くの人材にリーチするため、一定の曖昧さや幅広さを確保する
  • ターゲットアプローチ:採用したい人材が、入社後どのような地位でどのような業務を担当するか具体的にイメージを持てる情報を提供する。ターゲットに合わせてカスタマイズする
求人の告知
  • マスアプローチ:人材紹介会社や求人サイト、求人広告を通じて広く求人を広める
  • ターゲットアプローチ:リファラル採用やダイレクトリクルーティングで、採用したい人材にダイレクトに求人を働きかける
待遇の提示
  • マスアプローチ:幅広い人材に対応するため、広い待遇レンジを提示する
  • ターゲットアプローチ:具体的な好待遇を提示する。変動幅がある場合は特に下限を高く提示する
リファラル採用のアプローチ
  • マスアプローチ:所属社員全体に知人の紹介を依頼する
  • ターゲットアプローチ:採用したい人材と関係を持つ者を特定してそれ経由で求人を働きかける。あるいは採用したい人材と関係づくりを行い、リファラル採用につなげる
採用活動のタイミング
  • マスアプローチ:求人したいタイミングに求人活動を行う
  • ターゲットアプローチ:採用したい人材が転職できるタイミングを読んで、事前に働きかける

ターゲットアプローチのための採用技術の蓄積

 またターゲットアプローチの実践で留意が必要なのが、高い採用技術が求められるという点です。優秀なエンジニアに魅力的な職場という印象を持ってもらうためには、以下の採用技術が求められます。

  • 「優秀なエンジニア」への深い理解。エンジニアに求められる高度な技術、経験、ソフトスキル、カルチャーフィットを解像度高く、理解・定義できなければならない
  • 優秀なエンジニアの要求への理解。例えば良い開発者体験や、魅力的な業務についての理解
  • 優秀な技術の評価能力。高度な技術について、適切に高低を評価できなければならない。

 こうした採用技術は、人事評価のみに特化した人事や、管理のみに特化したマネジメント層は保有していないことが多いです。技術、採用スキル両方について高い能力・経験を積んだ採用担当が求められます。これは一朝一夕に確保できるものではないため、日頃から採用技術を磨いて実現する必要があります。