Murga

個人的に言いたいコト・主張・気持ち。

西村博之「自分は自分、バカはバカ。他人に振り回されない一人勝ちメンタル術」を読んだ

2ちゃんねるを作った人物として知られる「ひろゆき」こと西村博之の「自分は自分、バカはバカ。他人に振り回されない一人勝ちメンタル術」という本を読んだ。

僕は元々他人の目を気にしない方だし、どちらかというと既に一人勝ちメンタルを会得している気がしていて、そういう意味では不満も何もないのだが、「他人に振り回されない」というところが気になってこの本を手に取った。僕はすぐ周りの人の言動にイライラしてしまう。本書の中でも例として挙げられていたような「電車で足を踏まれた時」みたいな些細なことで1日中イライラしてしまったりする。その解決策になる情報が何かないかと思って読んでみたのだ。

…といっても、本屋で見出しとマーカー部分、章末の「Point」のみを流し読みしただけなのだが。

それでも、本書に書かれている要素はいくつか自分の役に立つ新たな情報が載っていた。それを以下に挙げていく。コレ以外の内容は、自分も概ね同意する既知のネタしかなかったので省略。

エグゼクティブ・プレゼンス

テクノロジーが進歩した現代、そして今後において、既存のデータを見て何か分析するような方面は、個人のスキルはあまり通用しなくなってくる。AI がより大量の情報を高速に判断できたりするようになるからだ。

一方、まだデータがない情報だったり、人間の感情的な部分に影響する情報は、個人のスキル次第で次の大きな仕事にできる可能性があったりする。

そういう状況で今後必要になってくるのは、「いかにそれが正しいと伝えられるか」ではなく「いかにもっともらしく聞こえるか」というスキルだという。そのためには、もっともらしいデータや根拠を事前に取り揃えるのは勿論だが、人に伝える際も、堂々とした話しぶりで、説得力をもたせた話し方ができないといけない。

海外では、「上に立つ人にふさわしい存在感・振る舞い」を醸し出すスキルのことを「エグゼクティブ・プレゼンス」というのだそうだ。必ずしも役職として上に立たなくても、何かを伝える際は、他人に影響を与え、アクションを促して人を引っ張っていかなくてはならないので、それを有利に行うために必要なスキルとして、「エグゼクティブ・プレゼンス」という言葉を覚えておきたい。

イライラを避ける方法

本書の目的の一つに、「自分のメンタルを上手くコントロールし、気分良く日々の生活を送る」ことが挙げられている。日常の実社会で起こるイライラもそうだし、SNS を見ていてイライラすることに対しても、ひろゆきなりの対処法が語られていた。

日常の出来事にイライラしやすい、イライラを引きずりやすい人に対する対処法として語られていたのは2つ。

  1. イライラした出来事を何度も思い出してみる
    • 何度も思い返す内に、そのイライラした状況に慣れ、耐性が身につけられる
    • コレが逆効果な人もいるので、そういう人は無理してこの方法を取らず、次の方法を取る
  2. 「電車で足を踏まれてイラッとした時は耳をつねる」「上司の言動にイラッとしたら拳を握る」など、イラッとする状況に対して「決まって行う反応」を決めておく
    • イラッとした瞬間にその肉体的な行動を取ることで、イラッとした事実から意識を反らし、余計なイライラを回避できる

自分の場合は1つ目の方法が合わないので、2つ目の方法を今後試していこうと思う。

また、SNS に関しては、

  • 何か情報を得るためのツールとして捉えてはダメ
  • 文句・愚痴のハキダメでしかないと思うこと
  • 何かを発信したいなら SNS ではなくブログにすべき
  • SNS で何かをやった気になっても何も得られない

といった話だった。確かにそう思う。

「思い出し怒り」が酷い人

巻末の特別付録として、ストレスフリーでいるために僕が「しないこと」リストというモノが掲載されていた。

その中で、「思い出し怒り」をしてしまう人は、そのイラッとする出来事があった瞬間に、怒らずに我慢したせいだ、と言っていた。「あの時こう反応していれば良かった」という後悔から、何度もイライラした瞬間を反芻してしまう、ということだった。

だから、我慢せずその場で怒れ、というワケだ。

これは何も、何かあったらその場で感情に任せて怒鳴り散らしたり、相手をぶん殴ったりすれば良い、という意味ではない。先程挙げたイライラを回避する方法、「耳をつねるなどの予め決めておいた反応」が素直に取れれば、怒りをその場で処理できるだろうから、後に引きずらなくて済むだろう。

感想

「無料で楽しめるモノに金を払わない」とも書いていたので、金は払わず立ち読みで済ませたのだが、まぁまぁ面白かった。僕は他のひろゆきの書籍を読んだことがないのでそれなりに新鮮に感じたのかもしれない。

世間がどんな状況だろうと、周りがどう評価していようと、「自分は幸せだ」と自分が感じていられる状態を作れていれば「一人勝ち」なワケで、そういうメンタルを持てるように工夫していくのは大事なことだと思った。

参考

自分は自分、バカはバカ。 他人に振り回されない一人勝ちメンタル術

自分は自分、バカはバカ。 他人に振り回されない一人勝ちメンタル術

30歳限界説

プログラマに対する35歳定年説なんて言葉があって、最近までは「な〜にが、そんなの馬鹿げてるぜ、俺は一生プログラマだわ」って思ってたけど、最近の僕の中では30歳限界説すら感じ始めてる。

プログラミングが嫌いになったワケではないが、以前ほどの興味が持てず、関心が薄れてしまった感じがする。

6年以上 SE 業をやってきて、要件定義から設計、コーディング、テストからリリースまで、全ての工程を一人でも出来るようになり、大抵の問題は解決できるようになった。設計や実装といったスキル面でいえば、知らない言語、分野はあれど、仕事としてやることになったらちゃんと勉強してやれるだろうな、というポテンシャルの自負がある。技術は日々進歩していって、色々なものが抽象化されていくが、結局根幹は変わらないので、今持っているスキルをベースに応用してやっていけるだろうなと思ってる。

僕の「人」の扱いには課題があるが、僕はそもそも人に興味がないので、営業とかコミュニケーション的な視点はわざと持たないでいる。最初から上手くやろうとかいう思いがない。他人と話しているとイライラして仕方ないので、仕事に絡めたくない。顧客折衝は誰か得意な奴がやっといてくれ、と思うのだが、やらされたらとりあえずやっている。上手くはないが、経験がないワケではない。

自分の向いていない分野がハッキリして、自分が自信を持てる分野はひとしきり何でもやれるようになってしまって (厳密には「なんでも知った気になっているだけ」なんだろうけどあえてこう表現する)、これ以上特定の分野を掘り下げたり、新しい分野に手を出したりしたいと思えなくなってきた。

僕は一度ある程度の領域にまで到達できると急激に興味を失う性格だ。幼い頃はポケモンカードの収集に始まり、ゲームのやりこみ、ギターやエフェクターいじり。その時々で色々なモノに興味は湧くのだが、ある時急に「もうココまでやったら、もういっか。」となる瞬間が来る。

それでもパソコンに関しては興味を失わないで20年以上やってきたが、ココに来てついに興味がなくなってきたかもしれない。

自宅に帰ってある程度時間があっても、何かを作ったりしたいと思わなくなった。その気になれば大体のモノは作れるし、今必要なツールなんかはもう切った感があり、不自由ではない。解決したい課題も、表現したい事柄も殆どないので、プログラミングする動機も目的もない。

世間ではクラウドとか IoT とか、仮想通貨ブームが落ち着いて次は FaaS だ何だと次から次に色々な波が押し寄せているが、どれにも興味が持てない。俺たちはそんなに細かいこと気にしないといけないモノを作ってるのか?そんな仕事そもそもなくても困りゃしねぇじゃねぇか。なんかやるにしても、みんな Rails で実装して Heroku に載せて終わりで十分じゃないのか?結局のところ実現したいことってそれでいいはずじゃん。新しい設計技法も革新的な抽象化されたプラットフォームも要らねぇよややこしい。

技術面でもウンザリしてきているのに、他人が絡むと急に「バカ向けの説明資料作り」が必要になる。ソイツらは読みもしないくせに設計書や手順書を作らされ、相手の都合で Markdown への移行もしきれずに結局 Excel の操作ばかり。こんなの昔っから同じようなもんばっかり作っとるわ。他の会社の全然知らないシステムの設計書を放り込んどいても分かりゃしねぇだろ誰も読みもしないしメンテもしないんだから。

仮想技術によって高度に抽象化された層がいくつも重なっていて、結局やりたいことは変わらないはずなのに毎回新しいことを覚えさせられて、本質を見失いかけている技術面。一方、人間側の追従が遅れているせいで未だに20年前と同じドキュメントを作らされる資料面。どっちにも飽きた。自分だけ多少できるようになっても、世の中は何も変わりゃしない。

表面的な技術は日々新しいモノが出てくるけど、結局は同じモノをこねくりまわしているだけ。進歩を感じないし職業的にも諦観が凄い。僕個人の体力低下も相まって、プライベートな趣味としても興味が薄れつつある。

完全に飽きてしまった時に、それでも仕事と割り切ってこれからも同じようなことをし続けるのか。それとも、三十路を過ぎてから別の業界にでも飛び込むのか。以前なら「仕事と割り切って続けよう」なんて考えていたが、最近は割り切れないくらいに興味が持てなくて苦痛だ。

多分、良い意味でも悪い意味でも、世界はあんまり変わっていなくて、自分が世界をどう感じるようになったか、という変化だけだと思ってはいるのだが、自分が認知する世界がこのままどんどん詰まらなくなっていくのは嫌だなぁ、本当はもうちょっと楽しいはずなんだろうに、って思ってる。

情熱プログラマー ソフトウェア開発者の幸せな生き方

情熱プログラマー ソフトウェア開発者の幸せな生き方

実例に見る、バグの原因を見つけるアイデア:catch 句の中で例外が発生している

何かバグが起きた時、どうしてそれが起こっているのかすぐには特定できない場合がある。

今回は、僕が実際に遭遇した「一見しただけでは特定しにくかったバグ」を紹介することで、似たようなバグに出会った時の参考にしていただければと思う。

サンプルコード

僕が実際に遭遇したバグとサンプルコードを紹介する。JavaScript で書いているが、Java など似たような構文の言語でも同様だろう。

// 呼び出し元の関数
function main() {
  logger.trace('main() 開始');
  try {
    logger.trace('main() 実処理呼び出し');
    const result = exec();
    logger.trace('main() 呼び出し終了・結果 = [' + result + ']');
    return result;
  }
  catch(error) {
    logger.error('main() 例外を検知・異常終了');
    process.exit(1);
  }
}

// 実処理
function exec() {
  logger.trace('exec() 開始');
  try {
    // ユーザ情報を取得するような処理 (ココはサンプルのためテキトーに)
    const userId = myStorage.getData('userData').id;
    logger.trace('exec() ユーザ ID 取得成功 = [' + userId + ']');
    return userId;
  }
  catch(error) {
    // myStorage がスローする例外には詳細情報が含まれているのでその内容を出力する
    logger.error('exec() 例外を検知', error.details.statusCode);
    // 例外発生時は空値を返す実装とする
    return '';
  }
}

こんなコードがあったとして。

main() 関数を実行すると、内部で exec() 関数が呼び出され、正常に動けば以下のようにログが出力される。

2019-01-01 00:00:00 [TRACE] main() 開始
2019-01-01 00:00:01 [TRACE] main() 実処理呼び出し
2019-01-01 00:00:02 [TRACE] exec() 開始
2019-01-01 00:00:10 [TRACE] exec() ユーザ ID 取得成功 = [U0011]
2019-01-01 00:00:11 [TRACE] main() 呼び出し終了・結果 = [U0011]

大抵はこのように正常系が動作するのだが、ある時コレが上手く動かなくなったとする。大本のエラーは myStorage.getData() 関数で発生するので、以下のようなログが出力されることを期待していた。

2019-01-01 00:00:00 [TRACE] main() 開始
2019-01-01 00:00:01 [TRACE] main() 実処理呼び出し
2019-01-01 00:00:02 [TRACE] exec() 開始
2019-01-01 00:00:10 [ERROR] exec() 例外を検知 500
2019-01-01 00:00:11 [TRACE] main() 呼び出し終了・結果 = []

exec() 関数内の catch 句が例外をキャッチし、エラーコードをログ出力した上で、空の文字列を返却する。main() 関数から見るとエラーは握り潰されているので、正常な結果ということで、空の文字列を受け取って終了するはずだたt.

が、実際は次のようなログが出力された。

2019-01-01 00:00:00 [TRACE] main() 開始
2019-01-01 00:00:01 [TRACE] main() 実処理呼び出し
2019-01-01 00:00:02 [TRACE] exec() 開始
2019-01-01 00:00:12 [ERROR] main() 例外を検知・異常終了

今回は「一見しただけでは見つけにくかったバグを紹介する」と前置きして、順に説明しているので、原因になりそうなコード行がもうお分かりかと思う。

しかし、いきなりこのログの並びだけを見ると、exec() 関数は try / catch で囲んでいるのに、どうして catch 句をすり抜けて main() 関数に戻ってるの?!」と思うワケだ。また実際のコードはもっと処理 (行数) が多いので、何が起こっているのか余計に分かりにくくなるのだ。

catch 句の中で例外が発生している

さて、このような奇妙なログの並び方をする理由は、exec() 関数の catch 句内にある、以下のコードが原因だ。

catch(error) {
  // myStorage がスローする例外には詳細情報が含まれているのでその内容を出力する
  logger.error('exec() 例外を検知', error.details.statusCode);

error オブジェクトの中には details というオブジェクト・プロパティがあり、その中に statusCode というプロパティがある、という前提で、このようにコーディングしていた。

しかし、myStorage.getData() 関数がスローした error オブジェクトが details オブジェクトを持っていなかった場合は、以下のような例外が発生する。

TypeError: Cannot read property 'statusCode' of undefined

catch 句の中で発生した例外は、呼び出し元に伝搬する。エラーログを出力するためのこの行で例外が発生した場合は、「exec() 例外を検知」というエラーログが出力されないまま、呼び出し元である main() 関数の catch 句が例外を拾い、突然「main() 例外を検知・異常終了」と出力されたように見える、というワケだ。

例外を捕捉するための catch 句の中で例外が発生している、という可能性は、最初はなかなか思い当たらないだろう。しかし、おかしな動きをしている時は、「ココでキャッチしているはずだから…」といった思い込みを捨て、catch 句の中に問題はないかと調べる癖を付けたい。

「ログ出力処理」そのものが悪影響を及ぼす

また、この例で厄介なのは、エラーの内容を把握するために仕込んだはずのログ出力処理自体に問題があって、エラーの詳細が表に現れなかったところだ。

「正常ログが出るはず」「異常ログが出るはず」と思っているのに、想定していたどちらのログも出なかった場合は、ログ出力処理部分に問題はないかをチェックするようにしたい。

ログ出力処理で例外を出さないようにする

ロギング処理は、システムの動作に影響を与えてはならない。パフォーマンスが明らかに低下するほど大量のログを出力したり、今回のようにログ出力処理が悪さをして、システム異常を引き起こしたりしてはならない。

catch 句の中では、ログ出力も含めて余計な操作や処理をしないようにしたい。どうしても何か処理する必要があるのであれば、極端な話、catch 句の中に try / catch を書いてでも、ログ出力による問題を引き起こさないようにしたいところだ。

function exec() {
  logger.trace('exec() 開始');
  try {
    const userId = myStorage.getData('userData').id;
    logger.trace('exec() ユーザ ID 取得成功 = [' + userId + ']');
    return userId;
  }
  catch(error) {
    logger.error('exec() 例外を検知', error);
    
    // 極端ではあるが同様の例外は防げる例
    try {
      logger.error('exec() 例外の詳細コード', error.details.statusCode);
    }
    catch(innerError) {
      logger.error('exec() 例外の詳細コードなし・未知のエラー');
    }
    
    return '';
  }
}

JavaScript の場合であれば、ログ出力したい値を直接ロガーに渡すのではなく、ログ出力したい値を返す「関数」を渡すようなラッパーを実装すれば、ロギングによる例外発生を防げるだろう。

// INFO ログを出力するロガーのラッパー関数
function infoLogWrap(fn) {
  try {
    const results = fn();  // 引数で受け取った関数を実行して値の配列を得る
    logger.info(...results);  // 配列を展開してログ出力する
  }
  catch(error) {
    logger.error('ログ出力に失敗', error);
  }
}

// ↓ 呼び出し元となるコード

// ログ出力したい値を配列で返す関数を作り、それをラッパー関数の引数に与える
infoLogWrap( () => [errorObj.details.statusCode, convertValues(myValues)] );

// 分かりやすく書くなら以下と同義
const myFn = () => {
  return [
    errorObj.details.statusCode,
    convertValues(myValues)
  ];
};
infoLogWrap(myFn);

infoLogWrap() 関数の呼び出し元は、関数を定義したまでで、まだ実行していない。実際に関数を実行して値を得るのは infoLogWrap() 関数の中、try / catch で囲まれている部分なので、errorObjdetails プロパティがなかろうと、convertValues() 関数で問題が起ころうと、呼び出し元では例外が発生しない。代わりに「ログ出力に失敗」という用意しておいたエラーログが出ることになるが、コレを検知したらロギング処理をコード修正していけば良いだろう。

まとめ

  • catch 句の中で例外が発生している可能性を疑う
  • ロギングのための処理が例外を発生させている可能性を疑う

バグを見つける際の参考になれば。

ライト、ついてますか―問題発見の人間学

ライト、ついてますか―問題発見の人間学