技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

情報処理技術者試験対策「スコープコントロール」

「スコープ」とは、直訳すると「範囲」という意味です。
プロジェクトマネジメントにおいては、「スコープコントロール」という言葉が「現在のプロジェクトで対応する範囲をコントロールする」という意味で使われます。
スコープコントロールについては、情報処理技術者試験でどのような意味か問われることがあります。そのような問いについては、以上のことを理解していれば解くことができます。
 
なお、実際のプロジェクトマネジメントでも、スコープコントロールが重要になることがあります。
炎上するプロジェクトでは、このスコープコントロールが上手く行っていないことが炎上の原因となっていることがあります。
要件や設計を先方と検討している時に、あれもこれもとシステム化したいことが増えていくような状態です。
プロジェクトで用意されている予算や人員でできることは限られていますし、無理をして全ての要望に応えようとすると採算性や品質が犠牲になるので、そのような場合は今のプロジェクトでどこまでやるのかを明確に決めることが重要になります。
例えば、「○○の要件と○○の要件は近々の課題になっているのでこのプロジェクトでシステム化しましょう。他の要件は今のプロジェクトが終わってから考えましょう。」といった具合です。
----------------------
目次

https://1drv.ms/b/s!AivF3bzWXOzuhG1Xk5hscKYqkLkM

java:ミュータブルな参照型変数の初期化の注意点

ミュータブルな参照型変数を初期化する場合、初期化の方法を間違えると他の変数も一緒に初期化してしまいます。
この記事では、ミュータブルな参照型変数の初期化方法を解説します。
 
【基本データ型変数と参照型変数】
変数は大きく分けて、基本データ型変数(プリミティブ型変数、値型変数)と参照型変数(オブジェクト型変数、クラス型変数)の2つに分けることができます。
 
基本データ型変数とは、以下の8つの内の何れかの型で定義された変数であり、オブジェクトを持ちません。
・byte
・short
・int
・long
・float
・double
・boolean
・char
 
参照型変数は以上の8つの型以外の型で定義された変数であり、オブジェクトを持ちます。
実際には、メモリ領域の番地を指し示すポインタのようなものが格納され、ポインタを辿って値を参照します。
C言語のポインタの概念を理解しているとこの概念も理解できます。C言語のポインタについては以下のページをご参照ください。
 
C言語:ポインタの概念の図解

https://cyzennt.co.jp/blog/2019/05/04/c%e8%a8%80%e8%aa%9e%ef%bc%9a%e3%83%9d%e3%82%a4%e3%83%b3%e3%82%bf%e3%81%ae%e6%a6%82%e5%bf%b5%e3%81%ae%e5%9b%b3%e8%a7%a3/

 
【イミュータブルとミュータブル】
参照型変数は、更にイミュータブルとミュータブルの2つに大別することができます。
 
イミュータブルは、日本語で言うと「不変」という意味であり、オブジェクトの中の値を変更することができない変数を指します。
setter等でオブジェクトの中の値を変更できないようにする、メンバ変数をprivateで定義してオブジェクトの中の値を変更できないようにする、等の条件を満たすと、イミュータブルな型となります。
イミュータブルな型となる条件はかなり例外的であり、自分でクラスを作成する場合は意識的に条件を満たそうとしなければイミュータブルな型にはならないはずです。
標準で用意されている型の中では、String型がイミュータブルな型として有名です。
イミュータブルな参照型変数はオブジェクトの中の値を変更できないため、中の値を変更する場合は新たにメモリ領域を確保してオブジェクトを作り直すような動きになります。今回取り上げる記事上は、基本データ型変数と同じような動きになります。
 
ミュータブルはその逆で、オブジェクトの中の値を変更することができる変数のことであり、多くの参照型変数はこちらに該当します。
String型も、配列にした場合はミュータブルな型になります(配列がミュータブルなので)。
そして、注意が必要なのはこのミュータブルな参照型変数です。
 
【ミュータブルな参照型変数の初期化の注意】
参照型変数の中には、ポインタが格納されています。
 
例えば、
String[ ] hoge = {"あ","い","う"};
String[ ] fuga = hoge;
とした場合、fugaには「{"あ","い","う"}」が入るわけではなく、fugaのアドレスが格納されます。
つまり、hogeとfugaは同じ位置を指し示すことになります。
 
ここで、fugaのみ初期化(値を変更)しようとしたとします。
String型のようにイミュータブルな型であれば、fugaの値を変更された時点でメモリ領域が新たに確保されるので、hogeとfugaは別々の値となり、何の問題もありません。
しかし、String[]型のようにミュータブルな型の場合は、hogeとfugaが同じ位置を示しており、新たにメモリ領域が確保されることもないので、hogeが示す値も一緒に書き変わってしまいます。
 
例えば、
String[ ] hoge = {"あ","い","う"};
String[ ] fuga = hoge;
for (int i=0;i<3,i++){
    fuga = " ";
}
のようにfugaを全角スペースで初期化しようとすると、fugaだけでなくhogeも初期化されてしまいます。
 
これを防ぐためには、以下の2つの手順を踏む必要があります。
(初期化用のメソッドが用意されているクラスであればそれを使っても良いですが、ない場合も多いです)
 1.初期化したい変数にnullを代入してメモリ領域への参照を切る
 2.newで初期化したい変数用のメモリ領域を新たに確保する
 
以下、サンプルコードで動きをまとめたいと思います。
 
【サンプルコード】
public class InitTestMain {

    public static void main(String[ ] args) {

        System.out.println("■プリミティブ型");
        int a = 1;
        int b = a;
        b = 2;
        System.out.println(a);
        System.out.println(b);

        System.out.println("■参照型(イミュータブル)");
        String c = "あ";
        String d = c;
        d = "い";
        System.out.println(c);
        System.out.println(d);

        System.out.println("■参照型(ミュータブル)");
        String[ ] e = {"か"};
        String[ ] f = e;
        f[0] = "き";
        System.out.println(e[0]);
        System.out.println(f[0]);

        System.out.println("■参照型(ミュータブル)…初期化実施");
        String[ ] g = {"さ"};
        String[ ] h = g;
        h = null;
        h = new String[1];
        h[0] = "し";
        System.out.println(g[0]);
        System.out.println(h[0]);
    }

}
 
【実行結果】
■プリミティブ型
1
2
■参照型(イミュータブル)


■参照型(ミュータブル)


■参照型(ミュータブル)…初期化実施

ExcelのVLOOKUP関数の高速化

何番煎じかわかりませんが、Excelの小技です。
VLOOKUP関数を高速化についてです。
 
VLOOKUP関数は、第四引数にTRUEかFALSEかを設定します。
通常はFALSEで使用すると思うのですが、TRUEを指定するとあいまい検索になります。
「あいまい検索」と言われると部分一致検索のようなものを思い浮かべるかもしれませんが、実際は二分検索だそうです。
二分検索については情報処理技術者試験でも出題されるので各自調べてほしいのですが、平たく言うと検索対象のデータが昇順にソートされていることを条件に高速に検索する方法です。
 
FALSEの場合は線形検索(上から順次検索)になるので、検索対象のデータ量がN倍になると検索にかかる時間もN倍になります。
しかし、TRUEの場合は二分検索になるので、検索対象のデータ量がN倍になっても検索にかかる時間はlogN(底は2)倍の増加で済みます。
例えば、データ量が2倍になった場合は、FALSEだと2倍の時間がかかるようになりますが、TRUEだと1.414…倍になります。データ量が3倍になった場合は、FALSEだと3倍の時間、TRUEだと1.732…倍になります。
 
注意点としては、TRUEにした場合は満たす値が無かった場合にも値を返すようになるということがあります。
検索条件を満たす値が無かった場合は、満たす値未満の最も大きな値を返します。
例えば、以下のようにデータ並んでいて11を検索した場合は、けが返ります。
10 け
12 こ
 
FALSEの場合と同じように一致するデータが無かった場合に#N/Aとしたい場合は、以下のようにする必要があります。
=IF(VLOOKUP(C3,D3:E12,1,TRUE)=C3,VLOOKUP(C3,D3:E12,2,TRUE),NA())
まずは検索条件と同じデータが存在するかどうかを調べ、存在する場合のみ検索をする、存在しない場合は#N/Aとする、ということをしています。
VLOOKUP関数を2回発行しているのでデータ量が少ないとFALSEの場合よりも時間がかかる場合もありますが、データ量が増えてくると効果を発揮します。

f:id:akira2kun:20190714114337j:plain

 

ループ処理のテスト観点

ループ処理を単体テストする時の小ネタです。
ループ処理を最初から最後まで全件チェックするのは大変なので、以下のように誤りが生じやすい箇所に絞ってテストを行うと効率良くテストを行うことができます。
(ループはN回行うものとします)
 
・ループの1回目をチェックする
 ループの開始時点を誤るバグはよくあるバグなので、それを発見する
 
・ループのN回目をチェックする
 ループの終了条件を誤るバグもよくあるバグなので、それを発見する
 
・ループの2~N-1回目の中から1回分をサンプリングしてチェックする
 前レコードの情報が現レコードの読み込みに影響を与えていないことを確認する
 
----
 
例えば、以下のCSVファイルについて、全件読み込んだ後、ヘッダレコード(1レコード目)とトレーラレコード(最終レコード)を除いて2項目目を取得する処理があるとします。
 
・C:\tmp\test.csv
1,20210602
2,0000001,ほげ商品,100
2,0000002,ふが商品,250
2,0000003,ぴよ商品,1000
9,3
 
これをjavaで実装する場合、正しい実装は以下です。
(実行結果はソースコード中に記載しています)
 
・FileReadTest.java
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class FileReadTest {

  public static void main(String args) {

    // 1バイトずつ読込
    BufferedReader br = null;

    // 読み込んだレコードをメモリ(配列)に保持
    ArrayList<String> list = new ArrayList<>();

    try {
      // 入出力ファイルパス
      br = new BufferedReader
        (new InputStreamReader
        (new FileInputStream("C:\\tmp\\test.csv"),"MS932"));

      // データ読み込みループ
      while (true){

        // 1行読み込み
        String str = br.readLine();
        if (str == null) {
          break;
        }

        // メモリに保持
        list.add(str);

      }

    // 例外処理
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        br.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    // ループ処理で中間レコードの2項目目を取得
    for (int i = 1; i < list.size() - 1; i++) {
      System.out.println(list.get(i).split(",")[1]);
    }

    // 結果
    /*
    0000001
    0000002
    0000003
    */

  }
}
 
----
 
上記の例において、ループの開始時点のバグ、ループの終了時点のバグ、ループ途中のバグが埋め込まれると、以下のようなコード・処理結果になります。
 
・ループの開始時点のバグ
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class FileReadTest {

  public static void main(String args) {

    // 1バイトずつ読込
    BufferedReader br = null;

    // 読み込んだレコードをメモリ(配列)に保持
    ArrayList<String> list = new ArrayList<>();

    try {
      // 入出力ファイルパス
      br = new BufferedReader
        (new InputStreamReader
        (new FileInputStream("C:\\tmp\\test.csv"),"MS932"));

      // データ読み込みループ
      while (true){

        // 1行読み込み
        String str = br.readLine();
        if (str == null) {
          break;
        }

        // メモリに保持
        list.add(str);

      }

    // 例外処理
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        br.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    // ループ処理で中間レコードの2項目目を取得
    // 1レコード目除外忘れ
    for (int i = 0; i < list.size() - 1; i++) {
      System.out.println(list.get(i).split(",")[1]);
    }

    // 結果
    /*
    20210602
    0000001
    0000002
    0000003
    */

  }
}
 
・ループの終了時点のバグ
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class FileReadTest {

  public static void main(String args) {

    // 1バイトずつ読込
    BufferedReader br = null;

    // 読み込んだレコードをメモリ(配列)に保持
    ArrayList<String> list = new ArrayList<>();

    try {
      // 入出力ファイルパス
      br = new BufferedReader
        (new InputStreamReader
        (new FileInputStream("C:\\tmp\\test.csv"),"MS932"));

      // データ読み込みループ
      while (true){

        // 1行読み込み
        String str = br.readLine();
        if (str == null) {
          break;
        }

        // メモリに保持
        list.add(str);

      }

    // 例外処理
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        br.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    // ループ処理で中間レコードの2項目目を取得
    // 最終レコード除外ミス
    for (int i = 1; i <= list.size() - 1; i++) {
      System.out.println(list.get(i).split(",")[1]);
    }

    // 結果
    /*
    0000001
    0000002
    0000003
    3
    */

  }
}
 
・ループ途中のバグ
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class FileReadTest {

  public static void main(String args) {

    // 1バイトずつ読込
    BufferedReader br = null;

    // 読み込んだレコードをメモリ(配列)に保持
    ArrayList<String> list = new ArrayList<>();

    try {
      // 入出力ファイルパス
      br = new BufferedReader
        (new InputStreamReader
        (new FileInputStream("C:\\tmp\\test.csv"),"MS932"));

      // データ読み込みループ
      while (true){

        // 1行読み込み
        String str = br.readLine();
        if (str == null) {
          break;
        }

        // メモリに保持
        list.add(str);

      }

    // 例外処理
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        br.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    // ループ処理で中間レコードの2項目目を取得
    for (int i = 1; i < list.size() - 1; i++) {
      // 次レコード取得ミス
      System.out.println(list.get(1).split(",")[1]);
    }

    // 結果
    /*
    0000001
    0000001
    0000001
    */

  }
}

情報処理技術者試験対策「定量的品質評価」

定量的品質評価」とは、その名の通り品質を数値で評価です。
そのことにより、品質の良し悪しを客観的に把握することが可能になり、有効な対策を打てるようになります。
 
今回の記事では、情報処理技術者試験に良く出てくる2つのモデル(信頼度成長モデル、エラー植付けモデル)と、実務で良く使う管理図について説明します。
(他にも定量的品質評価の手法はあります。「メトリクス」とかでWeb検索すれば出てくると思います。)
 
【信頼度成長モデル】
テスト工程の序盤はテスト手順確立のため発見されるバグが少なく、中盤になりテストが進むようになるとバグが順調に見つかるようになり、テスト終盤はテストの残項目が少なくなるため発見されるバグが少なくなる、というものです。
グラフにすると以下の通りです。

f:id:akira2kun:20190713182111j:plain
 
標準(類似プロジェクト)よりもテスト序盤でバグが多く出る場合は、テスト工程開始時点での品質が悪いことが予想されますので、その場合は前工程に戻っての品質強化が有効になります。
(そのまま進めても、バグでテストが順調に進捗しなくなることが予想されます)
また、その逆にテスト序盤で標準よりもバグが出ない場合は、テスト方法に問題がありバグが有効に見つけられないことが予想されますので、その場合はテスト方法の見直しが有効になります。
(そのまま進めても、次工程やリリース後にバグが残存することが予想されます)
 
なお、信頼度成長モデル(ゴンペルツ曲線)は新人教育の上でも重要な概念なので、別記事も書いています。
よろしければこちらも参照してください。
https://akira2kun.hatenablog.com/entry/2020/12/19/180000
 
【エラー植付けモデル】
意図的にバグを埋め込み、「システム全体のバグ数と埋め込んだバグ数の比率」と「検出したバグ数とそのうちの埋め込んだバグ数の比率」は等しいという前提の元、システム全体のバグ数を予想するものです。
例えば、以下の例だと、システム全体のバグ数は36と予想されます。
 
システム全体のバグ数:x
埋め込んだバグ数:12
テストで見つけたバグ数:27
テストで見つけた埋め込んだバグ数:9
 
x:12 = 27:9
x=36
 
【管理図】(1000ステップあたりバグがいくつあるか)
管理図とはQC7つ道具の一つで、品質のばらつきの上限と下限を定め、上限と下限の範囲に品質が収まっているかどうかをもって品質の良しあしを測るというものです。
ソフトウェアの品質を測る際にもこの考え方を適用することができ、「1000ステップ(1Kステップ)あたりバグがいくつあるか」という観点で品質を測ります。
例えば、1000ステップあたりバグが3~5個あるはず、この範囲で収まっていなければテストやソフトウェアの品質が悪い、と判断することができます。
基準の数字は言語やシステム特性によっても違うので、システム毎に品質測定して基準の数字決めたりします。言語毎に基準の数値を公表している機関もあります。
 
---------------------
 
正直、定量的品質評価は開発者からの評判が良くなかったりはしますが、私の経験上は、管理者やリーダーが品質管理のために自発的に取り入れるなら効果を発揮します。
実際に、他のプログラムに対してテストで見つかるバグが多すぎたり少なすぎたりするプログラムは、「複雑性が高く、バグが大量に出る」「ある担当者のテストが甘く、発見されるバグが少ない」といった原因が隠れており、その原因に対処するためのレビュー強化が有効だったりします。
 
----------------------
目次

https://1drv.ms/b/s!AivF3bzWXOzuhG1Xk5hscKYqkLkM

7payの脆弱性残存の原因の考察(形式的チェックの問題点、集団浅慮の問題点)

(2019/07/13に一部表現を見直しました。内容は変えていません。)
 
ニュースでも大々的に取り上げられていますが、7payがサービスイン直後にセキュリティ上の脆弱性を突かれて不正利用されてしまいました。
三者のログインを許し、クレジットカードから7payへのチャージ、及び7payでの購入をされてしまうことで、約900人の被害者数と約5500万円の総被害額を出してしまいました。
また、表立った実害は出ていないものの、7payのIDを無制限に作れるために登録特典のおにぎりが無限に手に入るという指摘もTwitter上でされました(https://twitter.com/athlonz/status/1146713927885082624?ref_src=twsrc%5Etfw)。
 
私は決してセキュリティの専門家ではなく、金融系SIer下流に生息する至ってノーマルなSEなのですが、この脆弱性を事前に発見するにはどのようにすれば良かったのか、自分の知識と経験を元に考えてみました。
 
■残存していた脆弱性の内容
下記の2つの記事にて詳しく書かれています。
 
「7pay緊急会見」で露呈した3つの大問題。なぜ脆弱なセキュリティ対策で開始したのか  BUSINESS INSIDER JAPAN

https://www.businessinsider.jp/post-194071

 
7payクレジットカード不正利用:第三者乗っ取りがあり得る致命的な2つの弱点(三上洋) - 個人 - Yahoo!ニュース

https://news.yahoo.co.jp/byline/mikamiyoh/20190704-00132766/

 
まとめると、下記の4つの脆弱性がありました。
 
①会員登録時のメールアドレスの確認方法の不備
 7payでは、IDとしてメールアドレスを使用している。
 この際、メールアドレスの有効性を確認していなかった。
 そのため、誤ったメールアドレスでも登録できた。
 また、後述の本人確認もされていなかったため、IDを無制限に作成できた。
 
②本人確認方法の不備
 一般的には、登録した電話番号にSMSを送る「二段階認証」が使われる。
 しかし、7payではこれをしていなかった。
 そのため、第三者が他人のメールアドレスで登録することが可能であった。
 また、IDとパスワードだけでログインできたため、リスト型攻撃に脆弱であった。
 
③パスワードリセットの方法の不備
 7payでは、パスワードリセットの際、以下の3つの情報の入力を求められる。
  ・生年月日
  ・電話番号
  ・会員ID(登録時のメールアドレス)
 この際、前述の二段階認証を行っていない。
 そのため、3つの情報を知っている第三者がリセットを要求することができる。
 (これらの情報はパスワードと異なり、会員名簿等から比較的容易に取得できる)
 更に悪いことに、パスワードリセットのメールアドレスを指定可能であった。
 そのため、リセットを要求した第三者がパスワードを変更できる状態であった。
 
④会員登録時の必須項目設定の不備
 会員登録時、登録ルートによっては生年月日を省略することができた。
 この場合、生年月日には固定値(2019/01/01)が設定される。
 このことが、前述のパスワードリセットの脆弱性を更に強めた。
 (生年月日を省略した会員については、2情報でリセット要求可能)
 
上記以外にも、被害発生時の初動対応の不備として以下の指摘もされましたが、それについてはこの記事では深く取り扱いません。
・パスワードリセット画面のメールアドレス入力欄をCSSで隠しただけ
 知識があればメールアドレス入力可能
・新規会員登録・チャージ機能は停止したが決済機能は停止しなかった
 既に第三者にチャージされてしまっていた場合に不正利用されてしまう
 
■形式的なセキュリティー審査の問題点
脆弱性の関する記事にて、気になる記述がありました。
 
「会見で同社は、「セキュリティー審査では脆弱性を指摘されなかった」とした」
 
どのような体制でどのような方法でセキュリティー審査を行ったのかは明らかではないですが、少なくとも何かしらのチェックは行っていたと考えられます。
 
7payは複数のベンダー(NTTデータNECNRIOracle)の共同チームであったと言われており、恐らくは日本で一般的なSIer主導の開発であったと思われます。
SIer主導の開発では、少人数の専門家チームが開発するというよりは、人月商売で開発者をかき集めてくる色が強くなるので、玉石混合の開発者が入り込みやすくなります。
セキュリティの専門家と言えるような開発者が然るべきポジションにアサインされていなかった可能性も十分にあります。
 
また、社内で審査したのか社外のサービスを使ったのかはわかりませんが、「セキュリティー審査」という表現をされる場合は、多くの場合は特定のチェック項目を形式的にチェックするという形になります。
形式的なチェックとしては、チェックリストや脆弱性診断ツールを用いたチェックがありますが、決められたチェック項目をチェックするという意味ではチェック方法に本質的な差はありません。
 
ここでは、形式的なチェックとして、Webで出回っているチェックリストを使ってみます。
Webで出回っているチェックリストとしては、IPAが発行しているセキュリティ実装チェックリストがあります。
試しにこれをやってみました。
(会員登録機能は既存サービスのomni7の機能なので全て対象外にする、という判断は行われていなかったものとします)
 
安全なウェブサイトの作り方:IPA 独立行政法人 情報処理推進機構

https://www.ipa.go.jp/security/vuln/websecurity.html

 
このチェックリストでは、①~④の脆弱性何れも発見できないと思います。
今回使用したチェックリストは高度な技術を用いたハッキング対策寄りであり、今回のような業務フロー上の問題を発見するのには不向きであったと思います。
もしかしたら、別のチェックリストを使用したり、もう少ししっかりチェックしたりすれば見つかるのかもしれませんが、選定したチェックリストやチェック者の技術力に依存してしまう(どこかで非形式的な判断を入れる必要がある)、ということは言えると思います。
正直、形式的なチェックでは限界があると思いました。
専門家による非形式的なチェックが必要だと思わされる事案だと思いますし、7payのセキュリティー審査も形式的なものであった可能性が決して低くないと思います。
(①~④の脆弱性は何れも言われてみれば当たり前のものであり、専門家でなくても発見できて当然だと思えるものなのかもしれませんが、後知恵無しの状態、しかも脆弱性含みの設計が正しい設計として仕様書に書かれている状態で発見できるかどうかは、また別の問題です。また、末端の開発者だと個々のモジュールの仕様書のみ渡されることも多く、システム全体の構成を理解することすら困難な場合が少なくありません。)
 
■社内の空気が原因である可能性
もしかしたら、社内の開発者は脆弱性に気付いていたかもしれません。
しかし、下記の記事に書かれている通り、脆弱性に気付いたとしても、脆弱性を残存させることのリスクを説明できない、説明できたとしても経営層が納得しなかった(リリース延期して脆弱性を対策する等の判断をしなかった)のかもしれません。
 
7Pay不正に潜む大きな脆弱性 システムの穴指摘できない社内の空気? - ライブドアニュース

https://news.livedoor.com/article/detail/16731357/

 
この記事にある通り、いち早く利益を出すためにリリースを急いだのかもしれません。
また、会見では「UX(顧客体験)をどう作っていくかという話と、セキュリティーをどう確保するかは、常にどうバランスさせるか難しい問題」という発言もあったので、利便性の高い快適なサービスにすることで、7payへの移行を促す戦略だったのかもしれません。
(7payは後発のサービスであり、予定通りリリースしたとしても先行者利益は得られない、その代わり既存サービスからシェアを奪う必要があるので、リリースを急いだ可能性よりも、既存サービスに負けないように利便性を重視した可能性の方が高いと個人的には思っています)
支配的な企業統治を行う経営者に「脆弱性は後で直せば良いから早くリリースする」「多少の脆弱性を犠牲にしてでも利便性を確保してnanacoや競合サービスからの移行を促してシェア拡大を図る」と言われてしまったら、開発者を束ねる管理者は何も言えないと思います。
 
利便性を重視して競合からシェアを奪う、という経営判断は必ずしも間違った判断ではありません。
しかし、今回の場合は、サービスイン直後に脆弱性を突かれ、大々的にニュースで取り扱われ、7pay(及びその親会社)の信頼を揺るがす結果となった(7payのシェア拡大が困難になった)ため、結果的には間違った判断であったと言えます。
 
今回の事例は、集団浅慮の失敗ケースの題材であるとも言えるかもしれません。
 
グループシンク(集団浅慮)とは?ジャニスが提唱する8つの症状や対策方法もご紹介  BizHint(ビズヒント)- 事業の課題にヒントを届けるビジネスメディア

https://bizhint.jp/keyword/76354

 
各々のメンバーが対等の立場で検討し、批判的な意見(今回のケースでは迅速なリリースや利便性を犠牲にしてでも、脆弱性を対策するべきだ、とする意見)も受け入れる体制が整っていれば、もしかしたら正しい判断ができたかもしれません。
こればかりは技術力でどうにかなる問題ではありません。経営者も巻き込んで、全社の意思決定のあり方を見直す必要があります。

リーダーシップ状況論の実践

リーダーシップ状況論(組織の成熟度に適したリーダーシップの分類)については、以前に情報処理技術者試験対策として記事にしました。
 
情報処理技術者試験対策「リーダーシップ状況論」

https://akira2kun.hatenablog.com/entry/2018/07/10/234418

 
試験対策としては上記の記事のみで良いのですが、今回の記事では実務に活かしやすくするように、もう少し具体的なことを補足します。
 
-------------------------
 

https://cdn-ak.f.st-hatena.com/images/fotolife/a/akira2kun/20180710/20180710234353.jpg


 
最も成熟度が低いaの状態というのは、構成員のスキルも自主性も不足している状態のことです。
もう少し具体的に言うと、成果を出すために何をすれば良いのかわからない状態や、そもそも成果を出すための意欲が低い状態のことを指します。
このような状態の組織を引っ張る上では、成果を出すことを最優先で考え、成果を出すための道筋を示すことができるリーダーが求められます。
道筋を示す上で、リーダー自らが判断を行う必要があるので、リーダー自身にもプレイヤーとしてのスキルが求められます。
リーダーのワンマンプレイのようになるので人間関係に軋轢が生じることもありますが、この段階では副作用としてある程度受け入れざるを得ません。
 
成熟度が少し高くなり、bの状態になると、構成員にスキルが身に付き始めます。
この状態になると、末端の細かい判断や作業は構成員自らできるようになります。
まだまだリーダー自ら動かなければならない場面も多いですが、このあたりからは構成員に任せる仕事の範囲を徐々に広げ、構成員と良い人間関係を築くことも考える必要が出てきます。
あまり長い間リーダーが出しゃばっていると、構成員の自主性が育つのを阻害し、次の成熟度へ進むのが難しくなります。
 
成熟度が更に高くなり、cの状態になると、構成員に自主性が育ち、自ら考えられるようになります。
この状態になると、これまでリーダーが行っていたような判断を構成員自ら行うようになりますし、その上で足りないスキルがあれば構成員自ら努力して身に付けるようになります。
この段階になれば、リーダー自ら動く必要はなくなります。構成員が気持ちよく仕事ができるように(このリーダーの下で働きたいと思ってもらえるように)、人間関係のメンテナンスに注力するのが有効になります。
 
成熟度が最高に高まり、dの状態になると、構成員自らリーダーシップを取るようになります。
この段階に到達すると、リーダーは人間関係のメンテナンスをする必要もなくなります。
リーダーの仕事は、構成員に任せて見守るというものになります。
 
-------------------------
 
IT業界で言うと、aの状態の組織というのは、前任者が逃げ出して残された構成員が右往左往している組織や、組織再編や短納期化でこれまで頼っていた社内プロセスを見直さざるを得なくなった組織のことを指します。
この状態の組織では、十分な能力や経験を持つ者がリーダーとなり、リーダー主導で作業計画や品質強化を行う必要がありますし、リーダー自ら設計や開発に関わる必要もあります。
炎上プロジェクトの火消し役、というのもここに含まれます。
 
bの状態の組織は、プロセスが正常に回り始めた組織と言うことができると思います。
この状態になると、構成員がプロセスに沿って自ら仕事ができるようになります。
リーダーは、構成員がプロセスに沿って仕事ができるようにサポートするのが主な仕事になります。
 
cやdの状態の組織は、アジャイル的な組織と言えると思います。
アジャイル的な組織になると、重厚長大なプロセスに頼らずとも、各々の構成員が高い技術力により適切な判断をするようになります。
この状態になると各々の構成員がリーダーシップを取るようになるので、リーダーの存在感が薄くなるのですが、強いて言えばリーダーの仕事は構成員のモチベーションを高く保つ(リーダーの下で仕事したいと思わせる)ことが主な仕事になるでしょう。
 
-------------------------
 
成熟度毎で求められるリーダー像が変わってくるので、どの成熟度で力を発揮するのかは個人差が出てくると思います。
 
ちなみに、私の場合は、aの混沌とした状態でリーダーに任命され、正常化する(bの状態になる)につれて他のリーダーへバトンタッチするような形になることが多いです。
プロセスやルールが無くとも適切な判断を行えるように鍛えているので、その成果を発揮できるaの状態の組織でのリーダーというのは個人的に楽しかったりします。