色んな事を書く

シンプルさを極めたエンジニアになりたい

【読書メモ】アメリカの高校生が学んでいるお金の教科書

1 章 お金の計画の基本

お金の計画を立てるとは自分が欲しいものを知ること。まぁでも計画自体にあまり価値はないんだろうな。お金の価値って時間ともに変わっていくし、計画を立てた当時では買えたであろうはずのものが買えなくなってしまったってことはあり得るだろうし。計画を立てることによって、自分が何を何のために欲しいと思っているのかを知ることが大切なんだろうな。なんとなく欲しいと持っているものを本当に必要なもののために省いたりとかも。

名目GDP GDPをその時の市場価格で評価したもの。物価変動の影響を受ける。
実質GDP 名目GDPから物価変動の影響を差し引いたもの。純粋な経済成長を前年と比較したければこちらを使う。
GDPデフレータ 名目GDP/実質GDP。1以上であればインフレで未満であればデフレ。

この記事がわかりやすい。

https://www.bank-daiwa.co.jp/column/articles/2016/2016_18.html

まぁでも計画自体にあまり価値はないんだろうな。

って自分では思ったけど、そんな事もないな。人生において比較的お金のかかるイベントっていくつかあって、たとえば結婚とかマイホームの購入とか子供の大学入学とか。そういうイベントを何時頃までに迎えたいかを可視化する事で、いついつまでにいくら必要かが分かりやすくなる。そこにアラインしているのか現実的に可能かどうか判断するにも計画は必要だと思った。

2 章 お金とキャリア設計の基本

4 章 貯金と銀行の基本

貯金とは、支出を先延ばしにするという事でもある。

5 章 予算と支出の基本

半年分の支出を緊急時に備えて貯めておく

これいいかもねぇ。安心のために資産は欲しくて、とりあえず 1,000 万って思ってたけど、漠然としすぎてる。半年分の支出は目的が明確で良い。

8 章 投資の基本

ETF とは

https://nextfunds.jp/semi/article1-1.html

ServiceBusAdministrationClient について

Rule の作成

この記事を読みながら

learn.microsoft.com

CreateRuleAsync というメソッドを使えば、ある topic の subscription に適応するルールを作成できます。

このルールを適応すれば、例えば以下のようなことが出来ます。

  • ApplicationProperty の Shop の値が test となっている時、Message の ReplyTo を hoge にする
  • Message の To が fuga の時に、ApplicationProperty の Shop の値を `` にする

など、Message のプロパティを書き換える事が出来ます。

こんなコードを書きます。

var a = new ServiceBusAdministrationClient(connectionString);
await a.CreateRuleAsync("some-topic", "some-subscriptions", new CreateRuleOptions
{
    Name = "StoreIsTestRule",
    Filter = new SqlRuleFilter("Store = 'test'"),
    Action = new SqlRuleAction("SET sys.ReplyTo = 'hoge'")
});

すると Subscription に対して Rule が出来ます。

Fileter ってのがどの Message に対して Action を実行するかって条件を記述します。サンプルコードのように SQL Like に書くためのクラスが SqlRuleFilter です。

Store='test' と書いてて Store とは何ぞ?となるかもしれませんが、これは ApplicationProperty 内の値を示しています。Shop とか書くと、ApplicationProperty 内の Shop に対して条件をかけます。ネストされたプロパティに対しては、Shop.Name='Tokyo' のように書くことが出来ます (配列は試してない)。

ApplicationProperty だけでなく、システムプロパティにも条件は書けます。システムプロパティとは

  • Subject
  • State
  • ReplyTo

などなどですね。詳しくはこちらの記事を参考にしてください。

これらに対して SQL を書くときは、sys を先頭に付けます。なので sys.To='fuga' のようにする必要があります。

同じく Filter を書くためのクラスには CorrelationRuleFilter というのもあります。こんな感じで書けますね。

var a = new ServiceBusAdministrationClient(connectionString);
var fileter = new CorrelationRuleFilter();
fileter.ApplicationProperties.Add("Store", "Tokyo");
await a.CreateRuleAsync("etl-location-changed", "pub-make-location-content-sources", new CreateRuleOptions
{
    Name = "StoreIsTestRule1",
    Filter = fileter,
    Action = new SqlRuleAction("SET Shop = 'hoge'")
});

こちらの方が書き心地が良いかもしれません。

では実際に message を投げて Rule が適応されているのか見てみます。

await using var client = new ServiceBusClient(connectionString);
await using var sender = client.CreateSender("some-topic");

var message = new ServiceBusMessage();
message.ApplicationProperties.Add("Store", "test");

await sender.SendMessageAsync(message);

await using var receiver = client.CreateReceiver("some-subscriptions");
var messages = await receiver.ReceiveMessagesAsync(100, TimeSpan.FromMilliseconds(5));

foreach (var m in messages)
{
    m.Dump();
    await receiver.CompleteMessageAsync(m);
}

すると受け取った message の ReplyTo にちゃんと hoge と入っていました。面白いのが、ApplicationProperty に適応された Rule が記録されていることです。Rule 名を同じ ApplicationProperty をプログラムで追加してるとどうなるだろう?

後不思議だったのが、同じ MessageId の message が複数受信できたことです。今回の場合だと Rule が適応されていない Message適応されている Message の 2 つが受信出来ました。同じ MessageId って複数存在しないと思ってたけどなんでだろう?

色々使える場面はあるのかもしれんけど、普段 ApplicationProperty に値を積めないから想像がつかんなぁ。そもそも ApplicationProperty を使いたい時とは。

書いてる途中で知ったんだけど、ServiceBusClient に CreateRuleManager ってメソッドがあって、これの戻り値でも同じように Rule の作成が出来そうだった。

【読書メモ】解像度を上げる

1 章 解像度を上げる 4 つの視点

解像度とは...「物事の理解度や、物事を表現する時の繊細さ、思考の明確さ」を表すもの。解像度が高いほど、これらのことも高いことを表す。

解像度の高い人は、対象を以下の 4 つの観点から見ている

  1. 深さ
    • 対象の物事や事象の原因や要因を細かく掘り下げて理解をしていくこと
    • 何か美味しいと感じる食べ物があった時に、「甘い」「辛い」など理解を深めていくこと
  2. 広さ
    • 対象の物事や事象の原因や要因の多様 性を確保する事
    • おいしいものを食べた時に、「調理法」「素材」など多様性を広げていくこと
  3. 構造
    • 「深さ」「広さ」で捉えた要因の関係性やそれぞれの相対的な重要性を理解していくこと
    • 売り上げの減少をテーマに取った時に、その要因として「顧客数の減少」を考える。その顧客のうち来店数に分けたり、性別で分けたり、それら毎の購入額で分けたり、構造を持たせていくこと
  4. 時間
    • 経時変化や因果関係、物事のプロセスや流れを捉える事
    • 売り上げの減少をテーマに取った時に、上記の構造化が時間経過と共にどのように変化していくのかを考える事

基本的に深さの視点が足りていない事が多いらしい。

2 章 あなたの今の解像度を診断しよう

4 つの視点から解像度を診断する

  1. 深さ
    • その話はどこまで具体的か
    • 5W1H」や「なぜなぜ分析」をすることで解像度を深めていける。
  2. 広さ
    • 多面的に話せるのか
    • ある事象の重要な課題を理解しその解決策を提案するだけでなく、その他の解決策も理解できているのか、という事
  3. 構造
    • 簡潔に話せるか、ユニークな洞察があるか
    • 構造化が出来ているということは、事象の最も重要な点が理解できているという事。それが出来ていれば簡潔に話すことも可能。
  4. 時間
    • 道筋は見えているのか
    • いついつまでにどの程度の目標を達成しないといけないのか、また環境の変化にも目を配れているのか

3 章 まず行動する・粘り強く取り組む・型を意識する

解像度を高めていくにはまず行動が大切と。解像度のために必要な「情報」「思考力」「行動」というのがあって、行動からやれば情報も集まるし思考力も鍛われると。

上げるべきは課題と解決策への解像度

まぁそうね。重要でない課題に対してアプローチしても効果薄いし、何が重要かを見極める意味でも課題に対する解像度を上げていかないといけない。それに対してどんな解決策がマッチするのか考えるには、解決策への解像度も必要ね。解決策を講じたはいいが、課題の一部しか解決できませんでした、以前の課題を悪化させました、だと話にならないしなぁ。

4 章 課題の解像度を上げる「深さ」

良い課題の 3 条件

  1. 大きな課題である
    • 大きさ = 課題の強度 × 課題の頻度
      • 課題の強度: 発生した時のペインの大きさ (ex. 金額、痛み...)
      • 課題の頻度: どのくらい頻繁に起きるか
  2. 合理的なコストで現在解決しうる課題である
    • 解決しうるかどうかを判断するには、解決策に対する知識が必要
    • 合理的なコスト内で解決できるものを選ばないと、得られる価値がそれを下回っては意味がない
  3. 実績を作れる小さな課題に分けられる
    • 小さくを取り組むことでフィードバックを得られるし、解決の可能性を高められる

課題への解像度を深めていくには、今見ている課題が「症状」なのか「病因」なのかを見極める事が大切。「病因」を一つ具体的な物事に対する「症状」と捉える事も出来る。そういた感じで具体度を上げていくとよい。この手法として「内化」と「外化」がある。

  • 内化
    • 読む、聞くなどして情報を取り入れる事
  • 外化
    • 書く、話すなどして情報を発信する事

これをイテレーションしていこうね。

解像度を深めていくための型。深さの視点。

  • 言語化する
    • 内化の前に言語化する。まず言語化(外化)しないと何が必要な情報なのかわからないよね。
    • 言語化する際は主語を明確にしたり動詞を入れたりとにかく文章にする。体言止めとかにしない。この辺はユースケース駆動開発に出てくる叙事的にしように似ている。ユースケースに対する解像度を上げるって事だったのかなぁ。
  • サーベイをする
    • 言語化して明らかになった曖昧な部分を埋めていく作業ね。これをする事でわからない部分をわかるようにする、事もできる。集めた情報を元に何がわかりまだ何がわかってないのか、を明確にする為にも小さな外化を繰り返さなきゃいけんのだろうなぁ。
  • インタビューする
    • 意見ではなく事実を聞く事。事実から仮説を導き出す事。
  • 現場に没入する
    • 見るのではなく観察をする。観察とは注意する対象を決めて見ること。
  • 個に集中する
  • Why so? を繰り返し、事実から洞察を導く
    • Why so not を繰り返し、なぜ解決されていないのかにも身を向けて見る
    • 原因を人ではなく組織や仕組みに対して向けていく
  • 習慣的に言語化する
  • 言葉や概念、知識を増やす
  • コミュニティで深掘りを加速する

5 章 課題の解像度を上げる「広さ」「構造」「時間」

広さの視点

  • 前提を疑う
  • 視座を変える
    • 視野を広げるには視座を上げんといかん
    • 立場を変えて見る
  • 人と話す
  • 改めて深める場所を決める

構造の視点

  • 分ける
    • ロジックツリーみたいに集めた情報を分けていく。どういう切り口で分けるかでツリーの形は変わるね。多次元データベースに対するダイスとかドリルダウンのイメージを持った。
    • 分ける粒度は具体的な行動を取れるようになるまで。抽象度高く何くれば良いかわからなければ、深くしていくための情報や知識を集める。
  • 比べる
    • 比較する為には抽象レベルを揃える必要がある。数値化は抽象化の手段。
  • 関連付ける
    • 互いに繋がりあって相互作用する要素の集合体をシステムという
    • あるシステムはより大きなシステムの一部である。対象の切り出す側面を変えると、別のシステムになる。
  • 省く
  • 質問をする

時間の視点で解像度を上げる

  • 変化を見る
  • プロセスやステップを見る
  • 流れを見る
  • 歴史を振り返る
    • 課題や問題点のルータを知る

6 章 解決策の解像度を上げる

良い解決策の3条件

  • 課題を十分に解決できる
    • オーバースペックになってもダメ。その為にも課題の解像度を上げる。
  • 合理的なコストで現在実現しうる解決策である
  • 他の解決策に比べて優れている

深さの視点

  • プレスリリース
  • 行動可能な単位までHowを問う
  • 専門性を磨いて新たな解決策に気づく
  • 手で考える
  • 体で考える

広さの視点

  • 使える道具を増やす
    • 全く関係のない分野の解決策がはまる場合もある。コミュニティやサーベイが大切。
  • 外部資源を獲得する前提で広げる
    • 今自分たちにできるかどうかは一旦考えない
  • 探索に資源を割り当てる
  • 解決策の真の意味を考える
    • この解決策がどのような意味を持つのか、課題が解決されると何が起こるのか、解決策が生み出す本当の価値は何か、を自問する

構造の視点

意識してなくとも、解決策には必ず構造が存在する。解決策の目的を意識し、どんな構造にするのか考えるのが大切。

  • 解決策の構造はシステム
  • 解決する範囲を決める
    • システムには必ず強みと弱みがあり、それらを言語化できる必要がある
  • 構造のパターンに当てはめる
  • 新しい組み合わせを生み出す
  • 要素間の相性を考える
  • 捨てる事で独自性を出す
  • 制約を意識する
  • 他システムとの連携を考える
  • 意図してなかったシステムの振る舞いに対処する
  • ストーリーを描く
  • 雑な構造から描き始める

時間の視点

  • 最適なステップを見出す
    • 計画に意味はないが、計画を立てることに意味はある。解決策の解像度が上がるから。
    • なぜ今そのステップかという問いに答える。
  • シミュレーションする
    • レベル K 思考
  • 好循環を作り出す
  • 長期の視点で考えて、時間を味方につける
  • アジリティと学ぶ力を高める

7 章 実践して検証する

検証の方法には実際に使ってもらうってのがある。顧客が欲しいと言ってくれることと、身銭を切ることには大きな差がある。うちの社長も常に自分がスタートアップの社長だった時にそのプロダクトを買いたいと思うかを問うているって言ってたなぁ。

8 章 未来の解像度を上げる

課題とは理想とのギャップの中で初めて見つかる。なので理想の置き方は重要。理想に対する解像度を上げるのことも本書の手法が使える。ベースにあるのは、情報・思考・行動の組み合わせ。

【読書メモ】世界一流エンジニアの思考法

1 章 世界一流のエンジニアは何が違うのだろう

「早く出来るように頑張る」ということが最終的な生産性をむしろ下げていた

 

コピペをしたほうが「成果」は出る。

 

「理解」して「実践可能」にしていればそんなことすらする必要はない。

自分の場合はなるべく一つ一つの作業を理解して取り組むようにしていたけど、その作業をこなすための理解に留まっているように思える。なぜ今そのコードを書く必要があるのかを説明する事は出来ても、その学びを別の何かに応用するって力が弱いと感じてしまう。本質的な部分への理解に達していないのかもしれない。正直そういう学び損ねはもったいないと思うから、業務後や休みの日にでも時間を取ってじっくり学ぶようにする。

・その構造をつかんで、人に説明できること

・いつでも即座に取り出して使える事

・知見を踏まて応用が効くこと

理解の三要素ね。これでいうと、説明できることにとどまっている気がする。即座に取り出せるようにするにはどうすればよいか。どうにも本質を理解しておけばいつでも使えるようになるって話だから、まずはそこをやる。とはいえ本質とは何かなぁ。「どんな課題を解決しようとし、なぜこういう事をしたのか」、みたいな背景を理解する事か?

後は基礎・基本への理解なんだろうね。C# の文法だったり言語仕様、ソフトウェア工学への知識とか、そういうベースを固めて目の前の問題を説明できるようになろう。C# の言語仕様とか目の前の問題を解く最低限しか得ないから、応用は効きづらいんだろうな。時間かけてもいいから、あぁすればどうなるのか、みたいな好奇心を大切にしてインプットをしてみる。

メンタルモデルをつくるとそれが出来るようになる

 

メンタルモデルとは、人々が世界を理解し、予測し、解釈し、新しい状況に適応するための、自己の心の中のイメージや理論

これはわかる。複雑なフローをシンプルにしてみんなが理解しやすい形に落とし込むのとか割と得意。メンタルモデルを定義して作ることが設計だとも思っている。ユースケース駆動開発の本に載っている ICONIX プロセスや DDD のアプローチってそういう側面を持っているから好きだったりする。

2 章 アメリカで見つけたマインドセット

より少ない時間で価値を最大化するための考え方。

  • 望んでいる結果を達成するために、最低限の努力をする
  • 不必要なものや付加価値のない仕事を無くす
  • 簡潔さを目指す
  • 優先順位をつける
  • 時間や費やした努力より、アウトプットと生産性に重点を置く
  • 長時間労働しないように推奨する
  • 会議や会議の時間内で効率的かつ生産的に価値を提供する

優先順位の捉え方が全然違っているのは驚いた。よくあるのは複数の事柄に対して優先度をつけて、より高いものから着手していこうって考え方。こうではなくて、最も優先度の高いもの以外はやらない、ってのがアメリカのスタイルらしい。

Refinement も同じ発想なのかな?プロダクトやユーザにとって最も価値のあるもののみを提供し、その仮説検証のサイクルのスピードは高めたい。今の開発組織でやっているのって、この PBI とついでにこれも見たいな感じでユーザにとって価値のあるリリースが先延ばしになりがち。負債解消とかそういうことに対するモチベーションもあるから、一概にどちらが正しいとは言えないけど。本書を読むと目の前の成果だけでなく将来的な生産性も考えたほうが良いスタンスだったので、この場合は負債も計画的に返済して以降になるか。ただし、最もインパクトの大きいもの一つ、になるのだろうが。

本当に必要な機能は何か、不要な機能は何かを見極め、プロセスの改善を実施していかに「楽」をしてより高い価値を生み出せるか。なぜ今のそのコードを書く必要があるのか、を説明する事は出来ても、その学びを他の場面に応用していく力がまだ弱いと感じてる。もちろん時間をかけてじっくりやれば解消はするんだけど、どうも「目先の成果」を出すために本質の理解を怠っているかもしれない。正直そういう学び損ねはもったいないと思うから、仕事終わりや休みの日にでも復習する時間を取ろう。

「楽をしたい」「楽できるように頑張る」みたいなマインドは持ってたけど、まだまだ甘いんだろうな。思い返すと運用作業を楽にするため、とか実装を楽にするため、みたいな発想はあっても、プロダクト開発を楽にするため、っていう発想はあんまなり。「本当にこの機能は必要か」を徹底的に問うているわけではない。

チームでやってる Refinement だと「こんな機能本当にいるかね?」って批判的な人格をインストールして臨んでみて入るんだけど、まだどこかで作ってみればいいやんがあるんだろうなぁ。ユーザへの理解が足りないってのもあるんだけどね。

時間は固定して、その中で価値を最大化させる

これは耳が痛いね~。結論が出ていないとついついそれを追って時間が長くなりがち。その時間の中で最大の価値を出すようにコントロールしないといけない。価値を出せていないと感じてしまうと、なんかしないといけないと感じてついつい時間を延長してしまいがち。たぶんここも限られた時間の中で何に論点を置いて議論しつくすのかってが大事なんじゃないだろうか。参加者の脳みそをそこに向けさせるってファシリテーション的なスキルも必要になってくるんじゃなかろうか。

成果物に対してもだらだら時間を延ばして完成されるってのもあるよね。まだ見せられないから、完成していないから、って理由で本来与えられて時間を超えたり、残業したりとかね。こういうのがはびこると生産的な組織にはならないんだろうなぁ。この程度で見せるのは恥ずかしいって感情もあると思うだけど、まず見せてフィードバックをもらって作っていくってほうが大事。そういうことが出来る文化にしていかないといけないってのもある。

プロジェクトが中止になったら成功した時と同じようにパーティをするような会社もある。なぜなら、その「失敗」から学ぶことが出来たから

「学ぶ」というスタンスが根本から違う気がする。僕も学ぶのは大好きで常に学び続けたいと思うが、思い返すと本や情報、人との会話の中での学びが多いように感じた。もちろん何かしらの経験から学ぶこともたくさんあるんだけど、この本での主張は「挑戦し失敗から学ぶ」という意味での経験になっているように思う。僕は一つの仕事をこなすことを経験として捉えていて、どでかい失敗を学びと捉える事はあんまないなぁ。だから失敗したいなぁとぼんやり考えていたのかもしれない。

成功したら称賛、失敗したら感謝って習慣が良いね。

「無理を承知で」のお願いの連鎖はみんなの疲弊を生み、チームや組織の業務改善に全くつながらない

これは本当にそう思う。何かしらのイノベーションやアイディアって一定の制約があるからなんだよね。立体駐車場は車を止められる土地が限られてたから、縦方向に面積を拡大すればいいじゃんってアイディアになったんだと思うし。何でもかんでも残業でカバーはせっかくの業務改善や組織課題の発見のチャンスを潰してしまうことに繋がりかねない。

結果を重視するのではなくバリューを重視する。やってみてどうだったか、改善点はどこにあるのかを常に考えるマインドを持つ。

3 章 脳に余裕を生む情報整理・記憶術

技術を徹底的に理解し、理解した情報の整理をして、すぐに取り出せるレベル 1 の状態にしてこそ、長い目で見た時の生産性は上がる

僕はどちらかというと広く浅くのタイプの人間だと思っていて、何かしらの問題でもググるキーワードを覚えていて、情報集めてすぐに解く。んで、まだ自分の知らない領域の学びに対してモチベーションを感じるんだけど、そうじゃないって事ね。一歩ずつ使える武器を増やしていくことが大切ですと。だからブログとかで丁寧に言語化をするのも良いんだろうね。

記憶力とか知識の定着には自分の言葉で説明する事が大切。ブログでのアウトプットも該当するだろう。日々の仕事の中で得た業務知識や作業内容を誰かに説明するような形で残しておくことも意味がある。こういう時に使えるのがコーネルメソッド。

究極の情報整理ノート『コーネルメソッドノート』! | 株式会社 学研ホールディングスのプレスリリース

後は何かをインプットする時には常に誰かに説明するつもりになるってのも確かに大切。

4 章 コミュニケーションの極意

「その場で吸収できることを最大化したい」というスタンス。複雑なものを一気に提供されてもその場で理解しきれないので、単純にリアルタイムに理解できる適切な情報量が好まれる

学び。

テキストコミュニケーションに慣れていなかったりすると、本質的な情報以外の何かを付与してしまうこともあるのだろうか。伝わり方を気にして、とか。それも文化的な問題の気もするけど。

ディスカッションは「どちらが正しいのか」はどうでもよく、「自分の考えを自分なりに深めるための行為」なので、初心者こそやったほうが良い。

この考え方はなかったな~。「どちらが正しいか」を決めようと思ったことはないけど、「自分なりに深める」というのもなかった。意見交換って意味に近いのだと思うけど、そういう場面があんまりなかったかもしれない。僕はこう思うってなって、でも私はこう思うってなった時に、お互いの考えを深める事が出来ているかな~と言われると微妙。確かにそういう考え方もあるよね、程度で終わってしまっている。

ただ決めるのは君だし、僕の意見なんて全然無視していいよ。

僕もこのスタンスなんだけ、どうなんだろうな~と思うときがある。根底にはさ、僕の意見なんて相手の成果物のクオリティを今よりも高いものにするためのものでしかなくて、自分の思い通りの成果物にしてほしいなんて思っていないんだよな。だから究極言うと、PR で 100 個コメント付けても一つも対応しないとかは全然 OK。ただし、意見は欲しいんだけどね。

もっと成果物に対するリスペクトを表していった方が良いのかな。まずは作ってくれてありがとう、から会話を始めるとか。思ってるだけじゃ一生伝わらないし。言葉だけじゃなく態度でも示していこう。

6 章 仕事と人生の質を高める生活習慣術

本当に生産性を上げたければ長時間労働をやめなければいけない

長時間労働をやめて、生産性を上げるための学びの時間を確保してねって事。時間を区切ってその中で成果を出すように工夫を凝らすような力学を発生させるって意味も含まれているんだろうが、ここでは「自己学習」をしていくことが大切だとのこと。

「整理され、検索せずにすぐに取り出せる状態になっている事」そこまでやって「完了」と捉える

これいいね。やってみよう。確かに自分の人生をコントロール出来ている感覚はない。調べたいなや試してみたい、やってみたい事が山済みでそれが気を散らす原因になっている可能背はある。一つ一つ処していけば今と何か変わるのかも。

5S の話に近いよね。

5Sとは何か | 5S|ナビゲート

【読書メモ】プログラマー脳

動機

  • 「良いコードとは認知不可の低いコードである」という漠然とした持論は持っていたが、「プログラミング中に発生する認知不可とは」が曖昧なので突き詰めたい
  • 理解し難い物事に対するストレスが半端ないから、極力そんなコードを後世に残したくない

プログラマー脳 ~優れたプログラマーになるための認知科学に基づくアプローチ | Felienne Hermans, 水野貴明, 水野いずみ |本 | 通販 | Amazon

1 章 コーディング中の混乱を紐解く

  1. 知識の不足 → 長期記憶
  2. 情報の不足 → 短期記憶
  3. 処理能力の不足 → ワーキングメモリ

コーディング時における混乱の原因と、それらがどの認知プロセスと関係があるかという話。

長期記憶はプログラミングの文法とか背景のソフトウェア工学の知識に関するものがあるだろう。C# だとこういう書き方をするとか、スタックってこういうものだよね~、みたいな話。

短期記憶はあるプログラムにおける変数の中身とかクラスの責務とかそういうのかな。やっぱこういう時に理解のしやすさ、ってのは記憶の残りやすさに一定関わってくると思っていて。変数名がそのコンテキストにおける役割を正しく表現できているかとか、責務を正しく表現されたクラス名とかだと覚えやすさは全然違うと思う。例えばだけど Service って Suffix がついたクラス名なのに状態を持っている、みたいなクラスがあるとそれだけで「なんで?」となってしまう。直感に反したことを行って、変な疑問が生まれるとそれだけで思考体力を使ってしまう。嫌だ。やっぱ DDD みたいに解決対象の問題から抽象度を下げてコードに落としていくアプローチは理解しやすさ高いと思うんだ。

もちろん、何が長期記憶にあって何が短期記憶にあるかは人によって違う。C# 歴が 5 年の人の長期記憶にあるものが 1 年の人は短期記憶にある、なんてことは珍しくないと思う。相手の脳内記憶装置の中身を完全に知る術はないんだけど、これまでにこなしたタスクとかどういう知識をインプットしているのかとか、その辺りから推測するしかない。1 on 1 とかセットして。でないと退屈なタスクを与えてしまったり、負荷の高すぎるタスクを与えてしまったりと不幸な感情になってしまうと思う。その人の成長に寄与しない。

ワーキングメモリってのは目の前の問題を解決するために何をするのか具体的に考えるために必要なスペースって感じかな。bit 演算とかリフレクションを使ってるコードを読むと特に負荷がかかってるなぁと思う。

って考えると処理能力の不足は短期記憶・長期記憶で補完できると思う。初めて読むプログラムなんて文法から調べていかなきゃいけないし (それってつまり短期記憶・長期記憶にもその情報がない)、全てをワーキングメモリで処理しなきゃいけない。だから意図が明確なインターフェースにするとかの工夫は読み手に取ってハッピーなものだと思う。何をしているのかわからないってなると実装を見に行かないといけないし、そうなってしまうと呼び出し元の情報はいったんどこかに退避させないといけない。実装を理解して戻ってきたときには一度退避させた情報を再度取り出さないといけない。きつい。ワーキングメモリは節約させてあげたい。

ある人にとっては意図が明確でも、そのほかの人にってはそうじゃないってのはどういう時だろう。ドメイン知識、みたいな前提知識が抜けている時はそういう状況になりうるよねと思う。逆に言うと意図が明確なコードはドメイン知識を学ぶ道具になるんだよなぁ。「読み手の短期記憶・長期記憶に意味のある情報を理解しやすい状態で表しつつ、ワーキングメモリの負荷を減らすコード」を書いていけると良さそう?なんでこのコードはこうなっているんだっけ?ってなってしまうと、その後改修を行うたびに余計なメモリを使うよね。そんなことなくスムーズに手を動かせる状態にしたい。

2 章 コードを速読する

なぜコードを記憶するのは難しいのでしょうか。その最大の理由は、短期記憶の容量が限られていることです。

馴染みのないコードを読むのが辛いのは短期記憶を使うしかないから。一つ一つ何をしているのか理解して脳内メモリに保存して読み進めていかなきゃいけない。短期記憶は容量も限られており、かつその容量は想像以上に小さいことも要因の一つ。

上手くやっていくにはインプットした内容は脳以外のどこかに出力するなりするしかないんだろう。一行一行処理の流れを理解しても仕方ないから、大枠を捉えて読んでいかないといけない。

コード読むのが早い人は長期記憶を使っている。ある程度読んだだけで何をしているのか把握でき、記憶容量も節約できるため速い。

これまで本章では、特定のトピックについて記憶している情報が多いほど、情報を効果的に分割できるということを見てきました。

長期記憶の中に過去に見聞きしたものやある種のパターンを置いていて、目の前のこととそれらを当てはめながら理解している。このまとまった単位をチャンクっていう。コードを書くときは処理を適切な単位で区切っていこう。それがメソッドだったりクラスだったりするんだろう。

ただ区切れば良いんじゃなくて、特定のトピックってのがポイントだろうなぁ。別々のトピックの事柄を同じメソッドにまとめてしまっては逆に読みづらい。Get の接頭辞が付いたメソッドの中で副作用が起きてたら「なんで?」となってしまうように。

デザインパターンとか特定のドメイン知識に左右されない良いチャンクだと思う。ただその知識そのものを得るのにコストがかかったりするし、そもそも気付かない人もいるから難しい。パターン名をクラス名に含めるとかはドメインが汚れる可能性もあるしあまりやりたくない。

上手くチャンク化された部分を振り返ってみる。自分のプログラムとか短時間で記憶して、何もない状態から書き直してみる。それで一致している部分は適切なチャンクがなされている可能性が高い。なぜなら短期記憶に残っているから。

ざっと読んだけど、実験結果を使って説明してくれるので、自分の価値観に根拠を持たせることができる。変数名の大切さとか。

3 章 プログラミング言語の文法を素早く習得する

何を記憶しているかということは、コードをどれだけ効果的に処理できるかということに影響を与えます。

ググれば文法なんてすぐにわかるけど、それを長期記憶に入れておくだけでコードを読む時間は大幅に変わる。またググると広告とかどのページを見るとか実際のコーディングやコードリーディングから意識が削がれる要因は多々あり、そこからやりたかった作業に戻る為には時間を要してしまう。自分の長期記憶だけで目の前のことを解決する方が生産性は高い。

IDE 使ってると呼び出そうとしているメソッドの目的やパラメータの説明を表示してくれるからググる手間は省ける。ってのがあるからやっぱコメントは大事だね。

記憶を強化するための 2 つのテクニック、「想起練習(積極的に何かを思い出そうとする)」と「推敲(新しい知識を既存の記憶と積極的に結びつける)」を取り上げます。

長期記憶にとどめておく為には想起と推敲が必要。ある瞬間に思い起こすよう脳を叩き上げたり、既存の記憶と絡めて新しい事柄をインプットしていく事で知識の価値が高まっていく。既存知識との紐づけのために自分なりの言語化をしていくのも重要とも言えるよね。

4 章 複雑なコードの読み方

脳内デバックをしていて一番しんどいなって思うのが戻り値の実際の値がが想像つきにくい時。HttpResponse みたいな複雑なオブジェクトになると、実行時にデバックしてみないとどんな値になりうるのか想像つきづらい時がある。

そういうフレームワーク固有の複雑なオブジェクトを緩和するために独自モデルとか作るのはありだと思ってる。ドメイン固有の知識を表現して、課題外在性負荷を減らしていく作戦。ただ、何を抽象度高く捉えたものなのか曖昧だと把握するのが困難な場合もある。例えば色んな外部 API へのリクエストとプロダクトのやり取りを共通するために ApiClient みたいなのを作っても、それが API ごとに別々の型を持ってたりすると途端につらい。複数 Repository でコード管理してるとこういうの起きやすい。

認知負荷とかワーキングメモリにかかる負荷のこと。コードの工夫だけでどうにかするのは無理だね。ドメイン知識に関してはある程度どこかにまとめたものが欲しいけど、実装とか言語機能に関しては各自で身につけて欲しいってのはある。

負荷のタイプ 概要
課題内在性負荷 その問題自体がどのくらい複雑化
課題外在性負荷 その問題の妨げとなる外的要因
学習関連負荷 考えたことを長期記憶に保持する際に引き起こされる認知的負荷

ソフトウェアで言うと、課題内在性負荷ってのは解決しようとしている問題そのものの複雑さのこと。課題外在性ってのは問題を解決するために書いたプログラムの複雑性の事かな。解こうとしている対象の問題の知識や制約をいかに表現出来るかが大事だと思う。「課題内在性負荷≒課題外在性負荷」みたいなイメージになるコードを書くようにしてる。

処理が分散しているコードは、読み進めるためにはあちこちに定義された関数をスクロールや検索で見つけ出す必要があるため、ワーキングメモリには負担がかかる可能性があります。

スクロールとかするだけで脳内の負荷高まるしね。ショートカット機能があるといえど、べた書きほど読みやすさに勝るものはないのかな。でも複雑性の高いものに名前をつけるってのはメリットだし、それこそチャンキングだしね。過度な構造化程認知負荷を高めるものはないとは思っている。

テストコードでも、あるケースで使うテストデータはケースごとに準備しておくことが望ましいのだと思う。テストコードを読んで仕様把握したい時にあっちこっちに飛んでしまうと辛いからね。とはいえ何でもかんでもケースごとに定義しているとコード数が爆増してそれはそれでつらいので、あるテストケースで検証したい処理に必要なデータだけケースで定義して、それ以外のセットアップに必要なデータは共通化させたい。Builder Pattern を使ってテストデータの生成の表現力を高めてもよい。

5 章 コードの深い理解に到達する

コード内でどんな処理が行われているのかがよくわかったら、次はそのコードについてより深く考えてみましょう。そのコードは、どのようにして書かれたコードなのでしょうか。

なぜそのコードになったのかっていう意図を汲み取っていくって話だと思った。例えばだけどアプリケーション側で結果整合とか冪等性とか意識しないといけないと、コードが複雑になりやすい。なぜその処理順序なのかとかいったコードの意図は考えてもわかんない部分もある。その順序を間違えてしまうと事故になる可能性があるとか。こういうのはコメントで残していくしかないよねぇ。こういうミドルウェアなどの要因で書かれたコードの理解をするのって大変。

 

コードの背景を推論するにあたり、変数が中心的な役割を果たすことは間違いないでしょう。

変数名も大事だけど型も大事だと思う。immutable なものってその時点で変数の振る舞い方の可能性を減らせるのがいいよね。可変ではないってのは読みやすさの一つだと思う。ReadOnlyList とか使っておくと、どんな状況でも List の要素に変化がないって事がわかるし。

コードの意図を汲み取る為には変数の役割を知り、コードの中でどんな役割を担っているのかいるのか見定めることが大切。でもそれだけで意図まで汲み取れるかというと難しそうではある。

 

ほとんどすべての変数は、たった11個の役割に分類できると主張しています。

あるプログラムの変数の役割をどう決めたのか、それを決めるのに参考にした情報は何か、これらがあればコードの読み方は変わって来るのか?どの変数に注力してコードを読めば良いのかは明確になるはあるかもしれない。ただそれをするくらいならコード読んだ方が早いんじゃないかという気もする。初めて読む部分だけ意識するとかでも良さそう。

 

ソースコードを理解するための 2 つの異なるレベル、すなわちテキスト構造の理解と計画の理解というモデルを作成しました。

 

テキスト構造の理解は、キーワードが何をすることを意味するのか、変数の役割は何かと言ったプログラムの一部分に関数r表面的な理解に関連するものです。一方、計画の理解は、プログラマーがそのプログラムを作る時に何を計画していたのか、何を目指していたのかといったことを理解することを表しています。

確かにな~。ライブラリのコードを読んでいて、それ単体だったら何をしているのかは理解できるんだけど、それが全体のうちのどこに位置するもので、どうやって使われるのかってのはコードを読んだだけじゃ理解できない。Web フレームワークの認証機能とかそんな感じがする。フレームワーク系の計画はドキュメント読めばわかりそうだけど。

 

コード内の注目すべきポイントが、すなわちフォーカルポイントと呼ばれる場所を探すことから始めることに気付きました。

ソースコードリーディングのスタートはフォーカルポイントを見つけること。例外が発生したポイント、あるクラスの public method、Azure Function のエントリポイント、などなど。そこから周辺の知識を集めて読んでいくよね。これはすごい分かる。目的もなく漠然とコードを読んでも頭に入ってこない。

DI とかテキスト的な読みやすさは向上できても計画的な読みやすさの向上には繋がりにくいものもある。どの段階で初期化されているのかわかんないしね。

6 章 プログラミングに関する問題をよりうまく扱えるようにする

問題を考えるときに頭の中で利用し、実際に手を動かして何らかの作業をすることを必要としないモデルもあります。これをメンタルモデルと呼びます。

メンタルモデルを使ってコードを読んだりプログラミングを理解すると効率が良いよって話。メモリ上でのデータの持ち方を木に例えて理解しやすくするやつ。

考えてみたら木構造かどうかなんてコンピュータには関係ないしね。コンピュータではあくまで 0 と 1 でしか表現されていないわけで、それを最も効率的に保持しておける構造を考えただけ。それを人間に説明する為に図に起こして、それが木のような形をしていたから木構造と呼ばれているだけだし。解決したいのは高速な検索を行えるデータ構造だし。

そもそもよく開発とかで使われているモデルって単語は何だろう。問題領域を抽象化して注目すべきことのみを抽出したもの、と理解してる。図とかモデルの一種。自分の理解を他人に伝えたりする場合に使うのもモデルだよね。そういった意味では HttpClient とかもモデルではあるんだよね。

問題解決における問題の表現方法を変えることで問題の解法がシンプルになるって事だよな?整数を 2 でわるみたいな話があった時に、整数を 2 進数で表現しておくと 1 bit 右シフトすれば良いだけになる。ドメイン駆動における問題領域と解決領域の考え方に近いな。いかに複雑な物事をシンプルに捉えて解決していけるのかを極めたいエンジニアになりたいわけで。

良くコードを書くのが速いって言われているけど、多分そんなことない。ある問題があったときに、それを解くコードを書き始めるんじゃなくて最もシンプルな解法を見つける事から始める。シンプルであればあるほどコードも少なくて済むしね。解決したい問題への理解とその問題を解く方法を考えるのが速いのだと思う。

 

より詳しいメンタルモデルが作れるようになればなるほど、システムを正確に理解できるようになり、それに関する質問に正しく答えられるようになるわけです。

メンタルモデルが具体的であるほどそれで説明できる事象への正答率が上がる。つまりメンタルモデルを深く理解した上でコードを読んだ方が誤った理解になりにくい。バイナリサーチとかエンコーディングとか読むの苦手だけど、それはメンタルモデルへの理解が足りていないからかな?

7 章 思考に潜むバグ

特に引用して残しておきたいものはなかった。長期記憶の中の理解が、新しいことへの理解を妨げてしまうことがある、って話。そういうのを誤認識と言ったりする。例えばあるプログラミング言語ではファイルなどのリソースの管理をプログラマが行う必要がないが、別の言語では必要がある、みたいな。必要ないと思い込んでいたためにファイルを確保しっぱなしにしてエラーになってしまったり、とか。

ドメインに対する理解も同じことが言えそう。ヒューリスティックな判断と化してしまっていると、いつかそれが暗黙知になって予期せぬバグを生むはず。そもそもこういうのは起こさないように本質捉えて設計しないといけない。

教えるとか理解をするのは別問題。既存で別のメンタルモデルを持っている人間にそれを置き換える新しいものを教えたとしてすぐに置き換わるわけでないない。きちんとモデルを理解してもらわないと、正しく使いこなせない。

人間が何かを完全に忘れる (長期記憶から消し去る) ことはほとんどなくて、大半の場合は思い出すのが難しくなっているだけ。だからあるきっかけで古い概念を思い出したりそれが目の前の問題を解くための障壁となったりもする。

ある概念に対して複数の理解をしている場合もある。例えばセーターは「体を温めるもの」「体の熱を逃さないもの」みたいな理解をしていたとする。こういった理解が競合してしまい複雑な問題を解くときの足枷になる事もある。例えば「雪だるまにセーターを巻くとはやく溶けるのか」みたいな問題に。普通に考えたら温めるものではないので溶けはしない。むしろ熱を逃さないから溶けにくいだろう。そこに「体を温めるもの」という誤った認識があると誤った解を導いてしまう。間違った認識を押さえ込んで正しい理解を採用することを抑制とかいったりする。メタ認知をしたりアンラーニングすると良さそう

こういう負の転移を防ぐにはペアプロとかで誤認識をしていないのか拾っていくといいと思う。あとはテストを書いて動かしてみるとか。結合試験もそうだね。実際に何がどう動いてその結果どうなるのかは自分で試してみるのが一番早い。まずは自分の認識が間違っていることに気付くのが一歩目だと思う。

IDisposable を実装したクラスを DI Container で使うとき Dispose はいつ呼ばれるのか

備忘用のメモです。

結論、インスタンスが破棄される時です。DI Container で生成されたインスタンスのライフサイクルは DI Container で管理されるので、破棄のタイミングで Dispose を呼び出してくれるみたいです。

雑にサンプルコード。DI Container に登録した Service の有効期限が異なり、IDspoable を実装したクラスを 3 つ用意します。

public class SingletonSample: IDisposable
{
    public void Dispose()
    {
        Console.WriteLine($"Disposing {nameof(SingletonSample)}");
        GC.SuppressFinalize(this);
    }
}

public class ScopedSample: IDisposable
{
    public void Dispose()
    {
        Console.WriteLine($"Disposing {nameof(ScopedSample)}");
        GC.SuppressFinalize(this);
    }
}

public class TransientSample: IDisposable
{
    public void Dispose()
    {
        Console.WriteLine($"Disposing {nameof(TransientSample)}");
        GC.SuppressFinalize(this);
    }
}

んで、これらを使う側のコード

public class DisposeController: ControllerBase
{
    private readonly SingletonSample _singletonSample;
    private readonly ScopedSample _scopedSample;
    private readonly TransientSample _transientSample;
    private readonly DependentObject _dependentObject;
    
    public DisposeController(
        SingletonSample singletonSample,
        ScopedSample scopedSample,
        TransientSample transientSample,
        DependentObject dependentObject)
    {
        _singletonSample = singletonSample;
        _scopedSample = scopedSample;
        _transientSample = transientSample;
        _dependentObject = dependentObject;
    }
    
    [HttpGet]
    public IActionResult Get()
    {
        Console.WriteLine("Finish processing request");
        return Ok();
    }
}

Transient の挙動を見るためのオブジェクト。

public class DependentObject
{
    private readonly SingletonSample _singletonSample;
    private readonly ScopedSample _scopedSample;
    private readonly TransientSample _transientSample;
    
    public DependentObject(
        SingletonSample singletonSample,
        ScopedSample scopedSample,
        TransientSample transientSample)
    {
        _singletonSample = singletonSample;
        _scopedSample = scopedSample;
        _transientSample = transientSample;
    }
}

これで (DI Container の登録は別途やってる) Controller のエンドポイントを叩くと以下のようになります (検証のため 2 回叩いています)。

Finish processing request
Disposing TransientSample
Disposing TransientSample
Disposing ScopedSample
Finish processing request
Disposing TransientSample
Disposing TransientSample
Disposing ScopedSample 

きちんと Controller の処理が終わって Dispose が呼び出されているようにみえますね。Scoped, Transient の有効期限も加味されていそうです。

ではこの状態でプロセスを終了させてみます。

info: Microsoft.Hosting.Lifetime[0]  
      Application is shutting down...
Disposing SingletonSample

Singleton で注入したサービスも Dispose が呼び出されてますね。

learn.microsoft.com

【読書メモ】オブザーバビリティエンジニアリング

  • 動機
  • 前書き
  • 1 章 オブザーバビリティとは
  • 2 章 オブザーバビリティとモニタリングにおけるデバッグの違い
  • 3 章 オブザーバビリティを用いないスケーリングからの教訓
  • 4 章 オブザーバビリティとDevOps、SRE、クラウドネイティブとの関連性
  • 5 章 構造化イベントはオブザーバビリティの構成要素
  • 6 章 イベントをトレースに繋ぐ
  • 7 章 OpenTelemetry を使った計装
  • 8 章 オブザーバビリティを実現するためのイベント解析
  • 9 章 オブザーバビリティとモニタリングの関係
  • 10 章 オブザーバビリティへの取り組みをチームへ適用する
  • 11 章 オブザーバビリティ駆動開発
  • 12 章 サービスレベル目標の信頼性向上への活用

動機

  • 関わっているプロダクトのオブザーバビリティが低いなぁと思うが、オブザーバビリティって何だっけとなったため言語化したい
  • プロダクトのオブザーバビリティを高めるためには何をすれば良いのか言語化したい
  • プロダクトのオブザーバビリティというコンテキストで建設的な議論を行えるようになりたい

 

オブザーバビリティ・エンジニアリング | Charity Majors, Liz Fong-Jones, George Miranda, 大谷 和紀, 山口 能迪 |本 | 通販 | Amazon

続きを読む