技術とか戦略とか

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

生成AIは相手を見て回答のレベルを変えるので学びを続けるべき

はじめに

生成AIは、投げられたプロンプトから知識のレベルを推測し、それに応じた回答を返します。
(少なくとも、そう見える動きをします)

知識のレベルが浅ければ一般的なことしか返しませんし、深ければ専門的な回答を返します。
そのため、生成AIを使いこなすには、プロンプトを投げる側にも知識が要求されますし、「生成AIで専門知識を補える」というのは学びを止めて良い理由にはなりません。

ここでは、プロンプトから推測される知識のレベルに応じて、どのように回答が変わるのかを、グリコじゃんけんを例に挙げて説明します。

例題のグリコじゃんけんの説明

念のため、グリコじゃんけんの説明をします。

グリコじゃんけんは、子どもの遊びとして広く知られるじゃんけんゲームのレパートリーの一種です。
じゃんけんで勝った時に、勝った時に出した手に応じて、決められた言葉を言います。
この時に言う言葉の文字数の分だけ先に進み、先にゴールまで辿り着いた人が勝ち、というルールです。

ゲームの流れは以下のようになります。

  1. 参加者はスタートラインに並ぶ。
  2. 普通のじゃんけんをする。
  3. 勝った人だけが進む。勝った時の手によって進む歩数が変わる。
  4. ゴールに到着すればその人の勝ち。しなければ2に戻る。

勝った時の手に応じて何歩進むかは所説ありますが、ここでは以下の歩数分だけ進むとします。

  • グーで勝ち → 3歩
  • チョキで勝ち → 6歩
  • パーで勝ち → 6歩

自分の手と相手の手の組み合わせにより、得られる利得がどのように変わるのかを表にして現すと、以下のようになります。

知識レベルが低いプロンプトの例

まず、知識レベルが低いプロンプトを投げた時の応答例を出してみます。
今回は、ただ漠然と「勝つ方法を教えてください」と回答を丸投げするようなプロンプトを投げてみます。
これは、2026/07/10のCopilotに投げた例です。

プロンプト


友達とグリコじゃんけんをすることになりました。友達と初めてやるのですが、勝つ方法をおしえてください!
  

応答


結論として、「初めての相手に確実に勝つ方法」は存在しませんが、勝ちやすくするための“実戦的なコツ”はあります。  
グリコじゃんけんは運だけのゲームではなく、心理と習慣がかなり影響します。

🎯 まず押さえるべきポイント
グリコじゃんけんは通常のじゃんけんと同じく三すくみですが、
勝ったときに進める歩数が違う(グー3・チョキ6・パー6)ため、
「勝つ=歩数を稼ぐ」ことが目的になります。

そのため、以下のような“人間の癖”を利用すると勝率が上がります。

🧠 初対面の相手に効く心理的な戦略
1. 初手はパーが出やすい
多くの人は初手で「手を大きく広げる」パーを出しやすいと言われています。
理由は単純で、緊張していると手が開きやすいから。

→ 初手はチョキが有利になりやすい

2. 勝った後の手は変えにくい
人は勝つと「同じ手を続ける」傾向があります。
負けた側は「手を変えよう」としがち。

→ 相手が勝った直後はその手を続ける可能性が高い
→ 読みやすいタイミング

3. 負けた後は“逆の手”を出しやすい
例えば負けてグーを出していた人は、次にパーを出しやすい。
「さっき負けた手は避けたい」という心理が働くため。

🚶‍♂️ グリコじゃんけん特有のコツ
1. 歩数の大きいチョキ・パーを多めに使う
勝ったときのリターンが大きいので、
「勝てそうなタイミング」でチョキ・パーを使うと差がつきやすい。

2. 相手が“焦っているとき”はチョキが強い
歩数で負けている相手は「大きく進みたい」と思うため、
チョキやパーを出しやすくなる。

→ そのタイミングではグーが刺さりやすい
  

この応答は一見正しそうに見えますが、正しい確率が高い一般論を集めただけのようにも見えます。
全ての状況に当てはまるわけではないばかりか、反例となる一般論が存在する箇所や、回答の中に矛盾が見られる箇所もあります。
鵜呑みにするのは少々危険でしょう。

以下は、生成AIの回答に対する私の批判です。

>全体的に
これらは全て一般的な人間の癖について述べたものであり、ゲームを進める中で相手が学習すると癖を変えてくる可能性がある。

>1. 初手はパーが出やすい
一般的に、じゃんけんで出てくる確率は
グー>パー>チョキ
であり、パーが一番有利になりやすい。
(芳沢光雄教授(桜美林大学)の大規模実験(2009)より)

>1. 歩数の大きいチョキ・パーを多めに使う
>勝ったときのリターンが大きいので、
>「勝てそうなタイミング」でチョキ・パーを使うと差がつきやすい。
グーで勝てそうなタイミングならグーを出すべき。
相手のそれぞれの手を出す確率が等しいと仮定するなら、チョキやパーを多めに使うのは理解できる。

>2. 相手が“焦っているとき”はチョキが強い
>歩数で負けている相手は「大きく進みたい」と思うため、
>チョキやパーを出しやすくなる。
>→ そのタイミングではグーが刺さりやすい
人間には損失を感じている場面でリスクを取りたがる(プロスペクト理論)ので、そのようなタイミングでチョキやパーを多く出したがるのは理解できる。
しかし、「チョキが強い」というタイトルと、「グーが刺さりやすい」という結論が矛盾しているように見える。
チョキやパーを多く出すならチョキを出すべき。

知識レベルが高いプロンプトの例

次に、知識レベルが高いプロンプトを投げた時の応答例を出してみます。
今度は、ゲーム理論の知識に基づいたプロンプトを投げてみます。
これも、2026/07/10のCopilotに投げた例です。

プロンプト


友達とグリコじゃんけんをすることになりました。
友達の癖を知らないので、癖を発見するまでは、イコライジング戦略になるような確率(相手の各選択肢の選択確率に寄らずに一定の期待利得を得るための各選択肢の選択確率)でそれぞれの手を選び、様子見したいと思います。
この場合、それぞれの手をどの程度の確率で選べば良いでしょうか?また、その場合の期待利得を教えてください。

前提条件について、以下の通り補足します。
・グーで勝った場合は3歩、パーやチョキで勝った場合は6歩先に進むとする。
・利得は、自分が進む歩数-相手が進む歩数、と定義する。
・ゴールまでの距離はわからないため、一旦考えないものとする。
  

応答


結論
イコライジング戦略になる選び方

グー: 
2/5(40%)
チョキ: 2/5(40%)
パー: 1/5(20%)

そのときの期待利得
相手がどの手をどの確率で出しても、
期待利得は常に 
0
(公平なゲーム)になります。

つまり、「相手の癖がわからない間、損もしないし得もしないように構える安全な様子見戦略」は
グー 2/5、チョキ 2/5、パー 1/5 で手をランダムに出す、という形になります。

この戦略でしばらく様子を見て、相手の偏りが見えてきたら、そこから先は「癖を突く戦略」に切り替えるのが面白そうですね。
  

知識レベルが低い場合のプロンプトの投げ方と比べると、「問題の大枠を自分で決めてから、回答が一意になるようにクローズドなプロンプトを投げている」という点が異なります。
今回の例では、「相手の癖が見えるまではイコライジング戦略で様子見したい」と、問題の大枠を自分で決めています。
そして、前提条件を明確にし、一般的な数学の問題として定義することで、回答が一意に返ってくるようにしています。
このようなプロンプトを投げるためには、投げる側に一定の知識や経験が必要であり、つまり、学びを止めないようにする姿勢が重要になります。

人間と比較して、生成AIの計算能力や言語能力は凄まじいものがあります。
しかし、その能力を正しい方向に使われるように導くのは人間の仕事です。
一般論を上手い感じにまとめたランダムな回答を見るのも楽しいものですが、ビジネス、とりわけIT業界の実務の用途では、そのような使い方は適さないことが多いです。

WindowsでPythonをダブルクリックで動かす方法を紹介!

【はじめに】
プログラム実行には環境構築がつきものです。
しかし、これは非IT人材にとってはハードルが高く、手順書があったとしても自力ではできないことが多々あります。
IT人材にとっても、都度手作業で環境構築を行うのは手間がかかります。

実行ファイルだけ渡すことも可能ですが、その場合は挙動を変えたい場合に不便です。
環境構築の手間は依然として高い印象ですが、既存のプログラムを改変したいだけであれば生成AIの普及によりハードルが下がっていますし、ソースコードの可読性が高ければ直感的にある程度の改変なら自力で行うこともできます。

そこで、今回は、Windows 11上で、Pythonのプログラムをダブルクリックだけで実行する方法を紹介します。
Pythonのプログラムを動かすには環境構築が必要ですが、この環境構築の手順をバッチファイルで自動化しています。
Pythonはライブラリが豊富であり拡張性が高いため、複雑なプログラムの実行にも適しています。
 
【前提作業】
任意のフォルダをエクスプローラで開き、画面上部のメニューから「表示」→「表示」→「ファイル名拡張子」にチェックを入れてください。


 
【最も簡単な例(Hello World)】
まず、Pythonを動かすためのバッチファイルを作成します。
任意のフォルダで右クリックし、「新規作成」→「テキスト ドキュメント」を選択してください。
これで空のファイルが作られますので、ファイル名を「PythonRunner.bat」にしてください。
(「.bat」より前の部分の名前は任意です)

その後、「PythonRunner.bat」を右クリックし、「その他のオプションを確認」→「メモ帳で編集」を選択してください。
これでソースコードを入力できるようになりますので、下記をコピペしてください。

@echo off

REM --- Pythonがインストールされていない場合はインターネット接続が前提になりますのでご注意ください ---

REM --- Pythonがインストール済みか確認(インストール済みなら:RUNまでスキップして即プログラム実行) ---
python --version >nul 2>&1
IF %ERRORLEVEL% EQU 0 (
    echo Pythonがインストール済みなので、Pythonのインストールはスキップします。
    goto RUN
)

REM --- Pythonインストーラをダウンロード ---
echo Pythonがインストールされていないので、Pythonをインストールします。
powershell -Command "Invoke-WebRequest -Uri https://www.python.org/ftp/python/3.12.0/python-3.12.0-amd64.exe -OutFile python-installer.exe"

REM --- Pythonをサイレントインストール ---
python-installer.exe /quiet InstallAllUsers=0 PrependPath=1 Include_test=0
del python-installer.exe
echo Pythonのインストールが完了しました。

REM --- サンプルスクリプトを実行 ---
:RUN
echo Pythonのサンプルスクリプトを実行します。
python HelloWorld.py
pause

このファイルを保存する際は、メモ帳の上部の「ファイル」→「名前をつけて保存」から保存します。
エンコードに「ANSI」を選択し、「保存」を押下して保存します。


次に、Pythonのソースコード本体を作成します。
同じフォルダで右クリックし、「新規作成」→「テキスト ドキュメント」を選択してください。
今度は、ファイル名を「HelloWorld.py」にしてください。
(「PythonRunner.bat」内で指定しているファイル名です)

その後、「HelloWorld.py」を右クリックし、「その他のオプションを確認」→「メモ帳で編集」を選択してください。
これでソースコードを入力できるようになりますので、下記をコピペしてください。

print("Hello World")

このファイルを保存する際は、メモ帳の上部の「ファイル」→「名前をつけて保存」から保存します。
エンコードに「UTF-8」を選択し、「保存」を押下して保存します。

ここまでで、フォルダ内に以下のようにファイルが2つ存在していればOKです。


実行は、「PythonRunner.bat」をダブルクリックすることで行います。
Pythonがインストールされていない場合はPythonのインストールを自動で行うので、時間がかかります。
インストールされている場合はすぐに実行されます。
以下のように、コンソール上に「HelloWorld」が表示されればOKです。
(以下の実行例はPythonがインストールされている場合のものです)

 
【ライブラリを使う例】
ライブラリを使用することで、Pythonで複雑なことができます。
今回の例では、学術計算を行った上でその結果をグラフで表示するサンプルを作成します。

まず、「PythonRunner.bat」をメモ帳で開いて編集します。
以下のソースコードをコピペしてください。

下記をコピペしてください。
ライブラリのインストールの処理を新たに追加しています。
今回は、「numpy」というライブラリと「matplotlib」というライブラリをインストールします。
(インストール済みの場合はスキップされます)

@echo off

REM --- Pythonがインストールされていない場合はインターネット接続が前提になりますのでご注意ください ---

REM --- Pythonがインストール済みか確認(インストール済みなら:RUNへスキップ) ---
python --version >nul 2>&1
IF %ERRORLEVEL% EQU 0 (
    echo Pythonがインストール済みなので、Pythonのインストールはスキップします。
    goto RUN
)

REM --- Pythonインストーラをダウンロード ---
echo Pythonがインストールされていないので、Pythonをインストールします。
powershell -Command "Invoke-WebRequest -Uri https://www.python.org/ftp/python/3.12.0/python-3.12.0-amd64.exe -OutFile python-installer.exe"

REM --- Pythonをサイレントインストール ---
python-installer.exe /quiet InstallAllUsers=0 PrependPath=1 Include_test=0
del python-installer.exe
echo Pythonのインストールが完了しました。

:RUN
echo 必要なライブラリをインストールしてからPythonのサンプルスクリプトを実行します。

REM --- ライブラリのインストールで使用するpipを有効化 ---
python -m pip --version >nul 2>&1
IF %ERRORLEVEL% NEQ 0 (
    python -m ensurepip --default-pip
)

REM --- 各ライブラリをインストール(変数にセットするライブラリ名はスペース区切りで指定) ---
set LIBS=numpy matplotlib
for %%L in (%LIBS%) do (
    python -c "import %%L" >nul 2>&1
    IF %ERRORLEVEL% EQU 0 (
        echo %%L はインストール済みです。
    ) ELSE (
        echo %%L をインストールします...
        python -m pip install %%L >nul
    )
)

REM --- サンプルスクリプトを実行 ---
python HelloWorld.py
pause

その後、「HelloWorld.py」をメモ帳で開いて編集します。
以下のソースコードをコピペしてください。

import numpy as np
import matplotlib.pyplot as plt

# 数値計算(sin波)
x = np.linspace(0, 10, 100) # 0(第一引数)~10(第二引数)の数値を100(第三引数)個に分割
y = np.sin(x) # 分割したそれぞれ値についてsinの値を求める

# sin波のグラフ表示
plt.plot(x, y)
plt.title("Sin Wave")
plt.xlabel("x")
plt.ylabel("sin(x)")
plt.show()

「PythonRunner.bat」をダブルクリックして実行すると、以下のように、学術計算(sin波)の結果がグラフで表示されます。
ライブラリがインストールされていない場合はライブラリのインストールを自動で行うので、時間がかかります。
インストールされている場合はすぐに実行されます。

 
【終わりに】
今回の例では、バッチファイルをダブルクリックするだけで、必要な環境構築も含めてPythonのプログラムを自動実行しました。
Pythonの中身を書き換えれば、異なる計算を行うことが可能です。
また、バッチファイルを書き換えて異なるライブラリを取り込むことで、全く別の処理をPythonのソースコードに記述することもできます。

今回のサンプルは、Pythonのプログラムをお手軽に実行するための雛形として役に立つと思います。

 

SES・受託エンジニアの新人教育の必要性とは?2つの理由を紹介!

人材の流動化が進み転職のハードルが下がっているため、また生成AIにより若手向けの仕事が減っているため、社員教育、特に新人教育に力を入れるのをやめる傾向が強まっています。
しかし、そのような傾向を踏まえても、SES・受託開発の企業において、組織力を高めて顧客からの信頼を得て継続的な獲得に繋げるために、体系的な新人教育の必要性は高いと考えています。

もちろん、育てた人材が他社へ流出してしまう可能性はありますが、だからためらうというものではなく、それでも必要だからやるべき、というものだと考えています。

この記事では、新人教育が必要であると考えている理由を2つ挙げます。
 
1.組織としてのスキルの平準化・底上げ
SES・受託企業にとって、技術力や業務知識は他社との差別化要因になります。
特定領域に強みを持つ企業は、他の企業と比較して質が良い仕事ができるようになり、顧客からの信頼を得やすくなります。

しかし、その強みは、組織として再現できる状態である必要があります。
個々人な学習や経験に依存してしまうと、社員の異動によりその強みが失われる可能性が出てきてしまいます。

だからこそ、会社としての強みを再現可能にする仕組みとして、新人教育は重要です。
特定分野の技術や業務知識を体系化し、誰が入社しても一定水準まで到達できるようにする、これを自己学習に任せていては、再現性は失われます。
個人の努力はもちろん大切ですが、企業として、学ぶべき内容を明確に提示し、習得を支援することが必要です。

教育によってスキルの底上げが進むと、プロジェクトの品質が安定し、顧客からの評価も高まります。
結果として、組織全体の競争力が強化されます。
 
2.ギバーマインドの体現によるチームビルディング
協業のしやすさを高める上でも、新人教育は重要です。

開発の現場では、派遣エンジニアやフリーランスを含む混成チームで開発することが珍しくありません。
その中で、メンバーが非協力的だったり、コミュニケーションが取りづらかったりすると、プロジェクトの円滑な進行に悪影響が出ます。
最悪の場合はシステム設計にも歪みが生じます。コミュニケーションが難しいメンバーの担当範囲を避けるように導線が引かれたり、仕様調整が滞ったりすることもあります。
(人間関係の難しさがシステム設計に悪い形で反映されるという現象は、「コンウェイの法則」として知られています)

協業を成立させるためには「ギバーマインド」、つまり自分から胸襟を開き献身的に動く姿勢を根付かせることが有効です。
新人教育は、この姿勢を組織として体現するための手段の一つとなり得ます。
技術だけでなく、コミュニケーション、報連相、チームワークの価値を伝え、実践できるように育てることで、協業しやすい文化が形成されます。
また、新人のためにコストを割いて教育をするという姿勢そのものがギバーマインドの姿勢の体現となり、背中を通してその重要性を伝えることができます。

unmodifiableListで要素が書き変わる事象への対応法

Javaで、要素が不変の配列を定義したくなる場合があります。
例えば、配列操作が可能な定数を定義したい場合です。
 
■ケース1:final修飾子だけでは対応不可
そのような場合、まず初めに「配列にfinal修飾子を付ければ良いのでは?」と考えるかもしれません。
しかし、final修飾子では配列から参照される要素までは変更不可にできません。
配列を元に作成したArrayListにfinal修飾子を付けても同じことです。
 
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // 配列を定義
        final Integer[] array = new Integer[] { 100, 200, 300 };

        // 配列 → List
        final List<Integer> list = Arrays.asList(array);

        // Listの要素の変更ができてしまう
        list.set(1,150);
        
        // 元の配列の変更の影響も受ける(元の配列も変更できる)
        array[0] = 50;

        for (int i = 0; i < list.size(); i++) {
            System.out.println(i);          // 1回目は0、2回目は1、3回目は2
            System.out.println(list.get(i)); // 1回目は50、2回目は150、3回目は300
        }
    }
}
 
■ケース2:Collection.unmodifiableListでも参照元の配列の変更の影響を受ける
要素を不変にするために用意された標準メソッドとして、Collection.unmodifiableListが存在します。
これを用いてListを定義することで、ListなどのコレクションAPIによる操作を不可とすることができます。
 
しかし、これでも完全ではありません。
Collection.unmodifiableListはディープコピーまでは行わないので、参照元となる配列に変更が生じると、その変更がCollection.unmodifiableListを用いて作成したListにも伝播します。
 
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // 配列を定義
        final Integer[] array = new Integer[] { 100, 200, 300 };

        // 配列 → 変更不可List
        final List<Integer> list = Collections.unmodifiableList(Arrays.asList(array));

        // UnmodifiableListは要素の変更ができない(コンパイルエラー)
        // list.set(1,150);
        
        // 元の配列の変更の影響は受ける
        array[0] = 50;

        for (int i = 0; i < list.size(); i++) {
            System.out.println(i);          // 1回目は0、2回目は1、3回目は2
            System.out.println(list.get(i)); // 1回目は50、2回目は200、3回目は300
        }
    }
}
 
■ケース3:ディープコピーした上でCollection.unmodifiableListを用いることで要素を不変にできる
今回のように、プリミティブ型のラッパークラスのような不変クラスで配列の要素を定義している場合、Arrays.copyOfで配列をコピーすることで対応できます。
Arrays.copyOfは配列そのものはコピーできるものの、配列の要素の参照をそのままコピーするため、厳密にはディープコピーではありません。しかし、その要素が不変クラスで定義されている場合は値が書き換わる心配がない(片方の配列の要素に変更が発生した場合に参照先が変更となり、もう片方の配列の要素の参照先への影響を避けられる)ので、ディープコピーと同じように扱えます。

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        // 配列を定義
        final Integer[] array = new Integer[] { 100, 200, 300 };

        // 配列 → 配列コピー → 変更不可List
        final List<Integer> list = Collections.unmodifiableList(Arrays.asList(Arrays.copyOf(array, array.length)));

        // UnmodifiableListは要素の変更ができない(コンパイルエラー)
        // list.set(1,150);
        
        // 元の配列の変更の影響も受けない
        array[0] = 50;

        for (int i = 0; i < list.size(); i++) {
            System.out.println(i);          // 1回目は0、2回目は1、3回目は2
            System.out.println(list.get(i)); // 1回目は100、2回目は200、3回目は300
        }
    }
}

 

生成AI時代だからこそ役立つ簡易タスク一覧のススメ

■はじめに
2026年現在、ITシステムの開発・運用の場で生成AIが導入されることが増えています。
生成AIで技術調査やコーディングといった作業が効率化され、技術者の人数を減らされる傾向にあります。
元々アジャイル開発をしていた現場だと、人員を減らされた結果、生成AIを片手に一人で開発・運用を回す、言うなれば「一人アジャイル開発」とも言うべき状態になることも少なくないと思っています。

私自身がそのような職場での業務を経験しています。
このような職場では、情報共有や管理のためのドキュメントが省略されがちですが、その中で簡易的なタスク一覧が効果的でしたので、この記事では私が導入したタスク一覧について紹介します。

■簡易タスク一覧の効果
簡易タスク一覧を導入することで、以下のような効果が期待できます。
・自分が抱えているタスクを意思決定者にいつでも見せることができる。タスクの優先度の会話をしやすくなる。
・過去に対応したタスクを意思決定者にいつでも見せることができる。要望がどこまで対応されているのかをすぐに知ることができる。
・引き継ぎや増員の際に、それぞれの技術者が抱えているタスクを俯瞰的に見ることができる。将来的にWBS管理やチケット管理の必要が発生した際に、その足掛かりにできる。

■簡易タスク一覧のイメージと項目


簡易タスク一覧の項目には、以下のようなものを設けると便利です。

・#(連番)
資料を見ながらの会話やGitのコメント等で、連番があると、発言や文章を短くできて便利です。

・タイトル
各タスクの内容をすぐにイメージできるタイトルを付けると、情報共有がスムーズに進みます。

・対象サブシステム
一人で複数のサブシステムのタスクを抱えている場合は、この項目があると便利です。

・対象資材
どのソースを修正するのかを書いておくと、ソース管理や開発者間の情報共有が楽になります。

・出元
後からタスクが発生した経緯を思い出せるように、タスクが発生元となるミーティングやチャットのURLがあると便利です。

・対応内容
どのようなシステム対応を行うのか記載しておくと、意思決定者や開発者間との情報共有がスムーズに進みますし、自分自身もどのような対応を行うタスクなのかすぐに思い出すことができるようになります。
設計書のようなものが必要な場合は、別紙を設けてそこに詳細を記載すると良いでしょう。

・工数
後述の優先度と合わせて対応工数を書いておくと、いつどの対応がリリースされるのかを知ることができるようになります。

・担当者
一人で開発・運用する場合は不要な項目ですが、後に引き継ぎや増員が行われることを予期して、あらかじめこの項目を設けておくと良いでしょう。

・対応完了予定日
工数と優先度から求めることができます。意思決定者にはこの項目を見せると良いでしょう。

・ステータス
「対応内容未定」「開発未着手」「開発中」「対応完了」「対応保留」「要望取り下げ」といったステータスを設けておくと、それぞれのタスクがどのような状態なのか一目瞭然となり、管理しやすくなります。

・進捗メモ
ステータスの詳細に相当する項目です。それぞれのタスクがどのような状態なのか詳細に知る必要がある場合に、この項目に書いてある文章を読むことになります。

・優先度
今抱えているタスクの優先度を明確にしておくと、意思決定者との優先度の会話がしやすくなります。数字でランク付けするのではなく、「高」「中」「低」といったざっくりとした分類分けにしても良いでしょう。

・優先度詳細
それぞれのタスクの優先度がなぜ高いのか/低いのかを記載しておくと、優先度の会話の内容を思い出しやすくなります。

■タスク分割の方針
アジャイル的に動いている職場では、優先度が高いタスクが横入りで入ってきたり、タスク(要望)が取り下げになったりすることが頻繁に行われます。
開発が中途半端になることでソース管理が煩雑になることを避けるために、大きなタスクを分割し、ユーザー影響のないリファクタリングのタスクや、ユーザーに解放しない機能のリリースのタスクを設けることで、開発が中途半端になることを避けやすくすることを推奨します。
1週間の間に複数回リリースできるぐらいまでタスク分割を進めておくと理想的だと思います。

「簡易タスク一覧のイメージと項目」で述べた#1~4は、まさにその例になっています。
これらのタスクのイメージとしては、以下のように、商品リスト作成用の既存フローを流用し、新発売リスト作成用の新規フローを作成する、というものになっています。


「案件」という捉え方をするなら#4のタスクだけで良いのですが、ユーザー影響のない前提タスクとして#1~3のタスクを設けることで、開発が中途半端になるリスクを避けています。





Java:Mockitoのチュートリアル

【はじめに】
Mockitoとは、Javaユニットテストをサポートするためのライブラリです。
 
テスト対象のクラスから呼び出されるクラス(オブジェクト)の代わりとなるオブジェクトを生成する機能を持ちます。
代わりとなるオブジェクトは、「モックオブジェクト」と呼ばれます。
 
図で説明すると以下の通りです。

 
テスト対象のクラスから呼び出されるクラスをそのまま用いるとユニットテストが困難になる場合にMockitoが役立ちます。
例えば、そのクラスが開発中の場合に、開発完了前にユニットテストを行うことができるようになります。
また、そのクラスの出力が不定の場合(データベースや外部システムの状態に依存する、ランダム性がある、等)にも役立ちます。
 
実際に動かすと、Mockitoがどのようなライブラリなのか理解しやすいです。
この記事では、Eclipse上でのJavaプロジェクトの構築、JUnitを用いたテスト環境構築、Mockitoの導入、Mockitoを用いたテストの実施、という順を追って、Mockitoを実際に動かしてみます。
Javaのバージョンは11です)
 
なお、JUnitを用いたテスト環境を構築する所までは、株式会社サイゼントが発行している「絶対にJavaプログラマーになりたい人へ(https://www.amazon.co.jp/%E7%B5%B6%E5%AF%BE%E3%81%ABJava%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%9F%E3%81%84%E4%BA%BA%E3%81%B8%E3%80%82-%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E9%96%8B%E7%99%BA%E4%BC%9A%E7%A4%BE%E3%81%AE%E6%9C%AA%E7%B5%8C%E9%A8%93%E5%90%91%E3%81%91%E7%A4%BE%E5%86%85%E7%A0%94%E4%BF%AE%E3%82%92%E5%85%AC%E9%96%8B%E3%80%82%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%82%B9%E3%82%AF%E3%83%BC%E3%83%AB%E3%82%92%E9%81%8B%E5%96%B6%E4%B8%AD-%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BE%E3%82%B5%E3%82%A4%E3%82%BC%E3%83%B3%E3%83%88-ebook/dp/B0D2VJ3GF2)」の第一章~第三章に詳しく書かせていただいています。
環境構築に躓いてしまう方は、こちらも是非ご覧になってください!
 
Eclipse上でのJavaプロジェクトの構築】
Eclipseを起動したら、画面上部のメニューバーから、「ファイル > 新規 > Java プロジェクト」を選択します。

 
その後、プロジェクト名に任意のプロジェクト名を入力し、JREのバージョンにJava 11を選択したら、「完了」を押下します。

 
この手順で新規のJavaプロジェクトが作成されます。
 
その後、以下のクラスを作成します。
(クラスの作成は、新規で作成されたJavaプロジェクトを画面左部のプロジェクト・エクスプローラー上で「右クリック > 新規 > クラス」を選択することで作成できます)
 
・MyLogic.java
package mylogic;

public class MyLogic {

    /* 商品コードから在庫量を取得 */
    public int getStockAmount(String productCode) {
        
        int stockAmount;
        
        // 本当はDBアクセスを含む複雑な処理を行うが、今回はシンプルな処理とする
        try {
            stockAmount = Integer.parseInt(productCode);
        } catch (Exception e) {
            stockAmount = 0;
        }
        if (stockAmount < 0) {
            stockAmount = 0;
        }
        
        return stockAmount;
    }
}
 
・MyMain.java
package mymain;

import mylogic.MyLogic;

public class MyMain {

    private static MyLogic myLogic = new MyLogic();
    
    public static void main(String args) {
        
        System.out.println("商品コードから在庫量を取得。");
        String productCode = "0020";
        System.out.println("商品コード:" + productCode);
        System.out.println("在庫量:" + myLogic.getStockAmount(productCode));
        
    }

}
 
その後、確認のため、プロジェクト・エクスプローラー上でMyMain.javaを「右クリック > 実行 > Java アプリケーション」で実行します。

 
以下のように出力されれば、ここまでの環境構築はOKです。
 
商品コードから在庫量を取得。
商品コード:0020
在庫量:20
 
【JUnitを用いたテスト環境構築】
次に、MyLogicクラスをテストするためのテストコードを作成します。
テストコードの内容は以下の通りです。
 
・MyJUnit.java
package mytest;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

import mylogic.MyLogic;

class MyJUnit {

    private MyLogic myLogic;
    
    @Test
    void test() {
        myLogic = new MyLogic();
        String productCode = "0020";
        assertEquals(20, myLogic.getStockAmount(productCode));

    }

}
 
ここまでで、以下のようなフォルダ構成になっているはずです。

 
その後、確認のため、プロジェクト・エクスプローラー上でMyJUnit.javaを「右クリック > 実行 > JUnit テスト」で実行します。

 
テストが成功すれば(緑色のバーが表示されれば)、ここまでの環境構築はOKです。
 
【より実践的なコードへの修正】
ここで、Mockitoの使用に適した実践的なソースコードに修正します。
ここでは、「MyLogic.java」を、商品コードに対応した在庫量を取得する「MyLogicSub.java」と、その結果を修正して返す「MyLogic.java」に分割します。
また、在庫量の取得ロジックに乱数を用いるようにします。
(実際はデータベース等の外部のリソースにアクセスすることで取得するのが一般的ですが、ここでは、「外部の状態に依存するために結果が不定になる」というのを表現するために乱数を用います)
 
MyLogicSub.javaを新たに作成します。
 
・MyLogicSub.java
package mylogic.sub;
import java.util.Random;

public class MyLogicSub {

    /* 商品コードから在庫量を取得 */
    public int searchStockAmount(String productCode) {
    
        int stockAmount = 0;
        
        // 本当はDBアクセスを行うが、今回はシンプルな処理とする
        try {
            stockAmount = Integer.parseInt(productCode);
            // 取得結果が不定(外部に依存する)というのを乱数で表現
            Random random = new Random();
            stockAmount = stockAmount + random.nextInt(11) - 5;
        } catch (Exception e) {
            stockAmount = 0;
        }
        
        return stockAmount;
        
    }
    
}
 
また、これまで作成してきたクラスも、クラス分割の方針に従って修正します。
 
・MyLogic.java

package mylogic;

import mylogic.sub.MyLogicSub;

public class MyLogic {

    /* 商品コードから在庫量を取得 */
    public int getStockAmount(String productCode, MyLogicSub myLogicSub) {
        
        int stockAmount;
        
        // DBアクセスにより在庫量を取得
        stockAmount = myLogicSub.searchStockAmount(productCode);
                
        // DBアクセスして得た結果を補正。今回は単純な処理とする。
        if (stockAmount < 0) {
            stockAmount = 0;
        }
        
        return stockAmount;
    }
}

 
・MyMain.java
package mymain;

import mylogic.MyLogic;
import mylogic.sub.MyLogicSub;

public class MyMain {

    private static MyLogic myLogic = new MyLogic();
    
    public static void main(String args) {
        
        System.out.println("商品コードから在庫量を取得。");
        String productCode = "0020";
        System.out.println("商品コード:" + productCode);
        MyLogicSub myLogicSub = new MyLogicSub();
        System.out.println("在庫量:" + myLogic.getStockAmount(productCode, myLogicSub));
        
    }

}
 
・MyJUnit.java
package mytest;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

import mylogic.MyLogic;
import mylogic.sub.MyLogicSub;

class MyJUnit {

    private MyLogic myLogic = new MyLogic();
    
    @Test
    void test() {
        String productCode = "0020";
        MyLogicSub myLogicSub = new MyLogicSub();
        assertEquals(20, myLogic.getStockAmount(productCode, myLogicSub));

    }

}
 
ここまでで、以下のようなフォルダ構成になっているはずです。

 
この状態でテストを実行すると、10回に9回以上の確率で失敗するように(赤色のバーが表示されるように)なります。
これは、在庫量の取得に乱数を用いるようにしたことで、期待結果の在庫量と一致しないケースが出てくるようになったためです。
 
このままではMyLogicクラスのユニットテストが難しくなるので、今後の手順でMockitoを用いることで問題を解決していきます。
 
【Mavenプロジェクトへの変換】
ライブラリを取り込む上では、ビルドツールを用いると便利です。
ここでは、ビルドツールにMavenを使用します。
 
まず、プロジェクト・エクスプローラー上で「プロジェクト名を右クリック > 構成 > Maven プロジェクトへ変換」を選択します。

 
その後のポップアップでは完了を押下します。

 
すると、JavaプロジェクトがMavenプロジェクトに変換され、Mavenを使用したライブラリ取り込みが可能になります。
以下のようにpom.xmlが出力されていれば成功です。

 
【Mockitoの導入】
pom.xmlを修正し、Mockitoを取り込むdependencyタグの設定を記述します。
 
・pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>MochitoTest</groupId>
  <artifactId>MochitoTest</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-core</artifactId>
      <version>4.0.0</version>
    </dependency>
  </dependencies>
  <build>
    <sourceDirectory>src</sourceDirectory>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <release>11</release>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
 
pom.xmlを保存すると、Mavenにより、Mockitoが自動で取り込まれます。
以下のように依存関係にMockitoが表示されれば成功です。

 
【Mockitoを用いたテストの実施】
いよいよ、テストクラスであるMyJUnitクラスでMockitoを使用します。
 
モックオブジェクトに置き換えたいオブジェクトは、MyLogicSubクラスのオブジェクトです。
それを示すために、MyLogicSubクラスを定義する箇所で@Mockアノテーションを定義します。
また、それを使用するMyLogicクラスを定義する箇所では@InjectMocksアノテーションを定義します。
 
テストメソッドを実行する前に実行されるメソッドとして、initMocksメソッドを定義します。
ここでは、MockitoAnnotationsクラスのopenMocksメソッドを、テストクラス(this)を引数にして実行します。
これにより、前述のアノテーションが使用可能になります。
 
そして、モックオブジェクトの処理内容は、テストメソッド中に記述します。
今回は以下のような記述を行っています。

String productCode = "0020";
Mockito.doReturn(20).when(myLogicSub).searchStockAmount(productCode);

これは、「myLogicSubクラスのsearchStockAmountメソッドが呼ばれた時、引数に"0020"(変数「productCode」の値)が与えられていれば、戻り値として20を返す」という意味です。
つまり、myLogicSubクラスを以下のような処理内容に置き換えたのと同じ意味です。

public class MyLogicSub {

    /* 商品コードから在庫量を取得 */
    public int searchStockAmount(String productCode) {
    
        int stockAmount;
        
        if (productCode = "0020") {
            stockAmount = 20;
        }
        
        return stockAmount;
        
    }
    
}
 
このmyLogicSubクラスのモックオブジェクトを利用することで、処理結果が意図した一定の値になるようになり、テストが成功するようになります。
 
ここまで書いた内容をソースコードとしてまとめると以下の通りです。
 
・MyJUnit.java
package mytest;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import mylogic.MyLogic;
import mylogic.sub.MyLogicSub;

class MyJUnit {

    @InjectMocks
    private MyLogic myLogic;
    
    @Mock
    private MyLogicSub myLogicSub;

    @BeforeEach
    public void initMocks() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void test() {
        String productCode = "0020";
        Mockito.doReturn(20).when(myLogicSub).searchStockAmount(productCode);
        assertEquals(20, myLogic.getStockAmount(productCode, myLogicSub));

    }

}
 
テストが必ず成功するようになれば、Mockitoの利用に成功しています。
 
このように、Mockitoを用いれば、テスト対象のクラスから呼び出されるクラスの処理を意図通りの仮の値とすることができるようになります。
テスト対象のクラスから呼び出されるクラスの処理が意図しない値を返すことによるテスト失敗がなくなるため、テスト対象のクラスの処理内容のテストに集中することができるようになります。

乱数の解析行為とその対策

私がプレイしているオンラインゲームで話題になったので、乱数の解析行為とその対策について、簡単にまとめます。
 
【コンピューターにおける乱数とは】
「乱数」とは、その名の通り、ランダムな数値のことを指します。
しかし、コンピューター上で乱数を生成する場合、特定のアルゴリズムにより疑似的に生成しており、正確に言うと完全にランダムではないため、専門的には「疑似乱数」と呼ばれます。
 
例えば、平易なアルゴリズムの例として「線形合同法」が挙げられます。
このアルゴリズムでは、「初期値(シード)」と呼ばれるXn(n=0)の値を与え、Xn(n=1)の値をXn(n=0)を元に算出し、Xn(n=2)の値をXn(n=1)を元に算出し…といった形で、前回の乱数の生成値から今回の乱数の生成値を求めています。


 
他にも疑似乱数を生成するアルゴリズムの種類はいくつか存在しますが、前回の乱数の生成値から今回の乱数の生成値を求めている所は変わりありません。
 
【疑似乱数の解析行為】
疑似乱数では、アルゴリズムを知っている状態で実際に出力される生成値を観察することで、初期値を解析できる場合があります。
そして、この解析に成功した場合、初期値から順番に計算を行うことで、Xnの値を知ることができます。
 
例えば、先ほどの「線形合同法」がアルゴリズムとして適用されている場合において、Xn(n=1)から順番に
2, 5, 1, 4, 0, 3...
といった値が観察される場合、初期値は6であると解析することができます。
(M = 7, A = 1, B = 3)
 
通常、実際に使用される乱数のアルゴリズムはより複雑なものですが、同じ要領で、複数の生成値から初期値を解析することができる場合があります。
 
【解析行為への対策】
疑似乱数は、暗号鍵の生成のようなセキュリティ分野や、オンラインカジノのようなギャンブル等の分野でも用いられます。
これらの分野で疑似乱数を解析され、乱数の生成値が推測された場合、何かしらの犯罪行為が行われて不利益を受ける可能性あるため、そのような分野で疑似乱数を用いる場合は解析対策を行う必要があります。
 
対策を行う上では、「暗号論的疑似乱数」と呼ばれる、学術的に解析が困難なアルゴリズムを用いて乱数を生成することが有効です。 
(ただし、「暗号論的疑似乱数」は計算量が多いため、シミュレーションや一人向けゲームのような解析対策を行う必要がない分野においては、通常の疑似乱数の方が適しています)
 
【疑似乱数の実装方法】
各言語で、疑似乱数や暗号論的疑似乱数を生成するためのライブラリが用意されています。
例えば、Javaの場合は以下のようにライブラリを使用して実装することができます。
 
・疑似乱数
public class Main {
    public static void main(String[] args) throws Exception {
        double randomNumber = Math.random();
        System.out.println(randomNumber);
    }
}
 
・暗号論的疑似乱数
import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;

public class Main {
    public static void main(String[] args) throws Exception {
        try {
            SecureRandom randomNumber = SecureRandom.getInstance("SHA1PRNG");
            System.out.println(randomNumber.nextDouble());
        } catch (NoSuchAlgorithmException e) { 
            // 例外処理
        }
    }
}