30歳からのプログラミング

30歳無職から独学でプログラミングを開始した人間の記録。

『熊とワルツを - リスクを愉しむプロジェクト管理』を読んだ

「リスク」をどのように捉え、どのように向き合っていくべきなのか説いた一冊。
用語や概念の整理をしつつ、具体的にどのように取り組むべきかを論じていく。
2003 年頃に出版されたということもあってか、ソフトウェアの受託開発を念頭に置いた説明が多いが、基本的な考え方はそれ以外のプロジェクトにも適用できるはず。

bookplus.nikkei.com

リスクを取らずに済むのであれば、そうすればよい。わざわざ危ない橋を渡る必要はない。
だがリスクと利益は切っても切れない関係にあり、成功を掴むためにはリスクを避けて通ることはできない。
著者らは、「リスクのないプロジェクトに手を出してはいけない」とまで言う。
しかし同時に、リスクを無視するのも愚かな行為であると主張する。

不確定性を数量化し可視化すること、過去のデータを活用すること、あたりがリスク管理の中心的な考え方なのかなと読んでいて思った。

未来は不確実であり、誰にも分かりはしない。「このシステムはいつになったら完成するんだ、正確な納品日を教えろ」なんて聞かれても、分かるわけない。
だが、分からないなりに分かることだってある。「どんなに早くても来年の 3 月までに完成する可能性はゼロだろう。だがさすがに 12 月には完成しているはず」。
こうすると、依然として未来は不確実ではあるが、不確定性の範囲を示すことができる。

また、未来は分からないが過去に何が起こったのかは分かる。そして過去の実績は未来を予測するための貴重なデータになる。これまでのプロジェクトはどのようなリスクを抱えていて、それはどの程度実現し、プロジェクトの結果はどうだったのか。
それを記録しておくことで、より詳細に不確定性を数量化できる。

そして不確定性を可視化することで、どの程度の不確定性があるのかを正確に表現し伝えることができる。
本書自身がそれを実践・証明しており、図を使って説明してくれることで内容がすんなり入ってくるし、齟齬も生まれない。
視覚的に表現することで分かりやすくなるんだということを実感すると同時に、同じデータであってもそれをどう表現するかで得られる示唆は変わるんだなということも感じた。
本書では不確定性を漸増図と累積図で表現している。どちらも同じデータを違う形で表現しているだけだが、どちらを使うかで得られる示唆が変わってくる。知りたい内容に応じて適切な可視化方法を選ぶ必要がある。

可視化した不確定性のなかから特定の日付を選んでコミットメントするのは望ましくなく、可視化された不確定性そのものをスケジュールの約束にすべき、というのも尤もだなと思った。
確かにそれが望ましいとは思う。

とはいえ、そう簡単にはいかない。
本書を読んで一番強く感じたのが、リスク管理を行うためにはそのための土壌が必要だということ。著者らによれば「リスク管理は大人のプロジェクト管理であり成熟の証」とのことだが、まさに成熟した組織でないと不可能な取り組みだと思う。
本書でも何度も言及されているが、リスクを直視できない組織は多い。

リスク管理を行うためにはまず、不確定性やリスクの存在を認めることが重要なのだが、それがそもそも難しい。
不確定性やリスクの存在は「怠け」や「弱気」の印である、と見做す価値観や文化は確かに存在する。魅力のない結果ではなく魅力のない予測を罰する文化、失敗することは許されるが「失敗するかも」と口に出すことは許されない文化。
そういう文化においては、致命的なリスクからは目を背けるようになる。そのようなことについて考えるのが恐ろしいので、存在しないものとして扱ってしまう。そして、約束を守ることより、とにかく大きな約束をすることが大切になってしまう。
そのような環境では、リスク管理を実践するのは不可能だろう。

結局は文化や組織風土の話であり、「リスク管理」に限らず何事もそうだと思う。文化は組織の土台であり、それが腐っていては、その上にどんな立派な制度や仕組みを載せたところで上手くいかない。

MySQL の SQL Mode について

MySQL には SQL Mode という設定があり、この内容によって、許容される構文やデータの妥当性チェックのルールが変化する。
この記事では SQL Mode の確認方法や設定方法の他、設定内容によって挙動が変化する例を見ていく。
動作確認は MySQL のバージョン8.0.28で行った。

環境構築

まずは動作確認用の MySQL コンテナを用意し起動する。

% docker run --name test_db -dit -e MYSQL_ROOT_PASSWORD=password mysql:8

以下のコマンドを入力するとパスワードを求められるので、先程MYSQL_ROOT_PASSWORDとして設定したpasswordを入力する。

% docker exec -it test_db mysql -p

確認と設定

現在の SQL Mode の設定内容はsql_modeというシステム変数に保持されている。
グローバルとセッションそれぞれ、SELECT @@GLOBAL.sql_mode;SELECT @@SESSION.sql_mode;で確認できる。

mysql> SELECT @@GLOBAL.sql_mode;
+-----------------------------------------------------------------------------------------------------------------------+
| @@GLOBAL.sql_mode                                                                                                     |
+-----------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+-----------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> SELECT @@SESSION.sql_mode;
+-----------------------------------------------------------------------------------------------------------------------+
| @@SESSION.sql_mode                                                                                                    |
+-----------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+-----------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

デフォルトの設定内容はバージョンによって異なる。

設定を更新する方法はいくつかあるが、セッションの場合は例えば、SET SESSION sql_mode = '設定したいモード';で設定できる。
以下は、STRICT_TRANS_TABLESモードを有効にしている様子。

mysql> SET SESSION sql_mode = 'STRICT_TRANS_TABLES';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> SELECT @@SESSION.sql_mode;
+---------------------+
| @@SESSION.sql_mode  |
+---------------------+
| STRICT_TRANS_TABLES |
+---------------------+
1 row in set (0.00 sec)

カンマ区切りで複数の SQL Mode を設定することもできる。

mysql> SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_DATE';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> SELECT @@SESSION.sql_mode;
+----------------------------------+
| @@SESSION.sql_mode               |
+----------------------------------+
| STRICT_TRANS_TABLES,NO_ZERO_DATE |
+----------------------------------+
1 row in set (0.00 sec)

SQL Mode によって挙動が変わる例

冒頭に書いた通り、 SQL Mode の設定内容によってクエリ実行時の挙動が変化する。
一例として、IGNORE_SPACEという SQL Mode の有無によって挙動がどのように変わるのか見てみる。

まずは動作確認用にデータを用意する。

mysql> CREATE DATABASE sample_db;
Query OK, 1 row affected (0.01 sec)

mysql> USE sample_db;
Database changed
mysql> CREATE TABLE users (id INT AUTO_INCREMENT, name TEXT NOT NULL, PRIMARY KEY (id));
Query OK, 0 rows affected (0.02 sec)

mysql> INSERT INTO users(name) VALUES('Alice');
Query OK, 1 row affected (0.02 sec)

sample_dbというデータベースを作成し、そのなかにusersというテーブルを作成、そしてそこに 1 件のレコードを作成した。

mysql> SELECT * FROM users;
+----+-------+
| id | name  |
+----+-------+
|  1 | Alice |
+----+-------+
1 row in set (0.00 sec)

まずは SQL Mode が何も設定されていない状態にしておく。

mysql> SET SESSION sql_mode = '';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@SESSION.sql_mode;
+--------------------+
| @@SESSION.sql_mode |
+--------------------+
|                    |
+--------------------+
1 row in set (0.00 sec)

この状態でまずSELECT COUNT( * ) FROM users;を実行する。
そうすると、レコード数を取得できる。

mysql> SELECT COUNT( * ) FROM users;
+------------+
| COUNT( * ) |
+------------+
|          1 |
+------------+
1 row in set (0.00 sec)

だがSELECT COUNT ( * ) FROM users;は、エラーになる。

mysql> SELECT COUNT ( * ) FROM users;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '* ) FROM users' at line 1

これは、COUNT(の間にスペースがあることで構文エラーになるためである。

IGNORE_SPACEを有効にして再度SELECT COUNT ( * ) FROM users;を実行してみる。

mysql> SET SESSION sql_mode = 'IGNORE_SPACE';
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@SESSION.sql_mode;
+--------------------+
| @@SESSION.sql_mode |
+--------------------+
| IGNORE_SPACE       |
+--------------------+
1 row in set (0.00 sec)

mysql> SELECT COUNT ( * ) FROM users;
+-------------+
| COUNT ( * ) |
+-------------+
|           1 |
+-------------+
1 row in set (0.00 sec)

今度はエラーにならない。
これは、IGNORE_SPACEによって関数名と(の間にスペースが入ることが許可されるようになったためである。

このように、同じクエリでも SQL Mode の設定内容によって挙動が変化することがある。

参考資料