対人フィードバックの基礎

人をシステムとして考える

人が意思決定をするプロセスというのは、結局上の図のようにシステム化することができます。Function(Input) = Outputなどと表現してもいいかもしれません。

与えられた情報に対して思考し、それが行動(Output)になるということです。

Outputを決定するのはInputとFunctionだけというのがここから導き出される最も重要な示唆です。

フィードバックに適用する

対人においては、ある人の行動に不満を持つ際にフィードバックを行うことが多いと思います。

このとき、行動(Output)だけにフィードバックを行うのはほぼ無意味です。

なぜなら、フィードバック者であるあなたが持っているInputとFunctionは、その人が持つそれらとは異なるからです。

相手に期待する行動があるならば、フィードバックも常にInputとFunctionを修正することを目的とするべきです。

誤ったフィードバックから生まれる問題

行動(Output)だけにフィードバックが行われる状況が続くと、フィードバックされた側は期待されるOutputからInputとFunctionを自力で修正する必要があります。

これは最適化問題を解かされているようなものです。

要領が良い人は素早くこの問題を解くことができるかもしれませんが、全員が必ずしもそうではなく、さらにフィードバック者の能力が卓越している場合は問題の難易度自体が上がるのでいつまで経っても期待するOutputは得られないことでしょう。

プレイヤーとしては非常に優秀なのに、人を育てられない人の実態はこれだと考えられます。

またこうした状況というのは、フィードバック者が持つ思考を想像する作業になりやすく、ややもすれば「顔色伺い」であり、心理的安全性を著しく低下させます。

これらのことから、フィードバックでは常に自分が持つInputとFunctionを開示することで答えを初めから与えてあげる必要があると考えられます。

大規模トラフィックシステム開発の知見集・あるある集

この領域のソフトウェアエンジニアなら思わず分かる〜と言ってしまうような事柄をまとめてみました。

リクエストが二重に送信される

これは1,000rps以上の規模のシステムであれば間違いなく1度は遭遇し、10,000rps以上になるとほとんど無視できないレベルになります。 原因は多岐にわたるのですが、大きく分けて2つあります。

リトライによるもの

システムの可用性は厳密な100%になることはなく、それが99.9999999%であろうがエラーというのは常に起こり得る課題です。 大規模トラフィックシステムの場合はその問題が特に顕著であることから、backoffリトライの実装が推奨されています。(というかほぼ必須)

厄介なことに、サーバーからInternal Errorが返却されたり、SDKタイムアウトした場合であっても実際にはリクエストが到達して処理されているという問題がよく起こります。 通常であればこうしたエラーはリトライ対象になるのですが、二重三重とリクエストを送信してしまうわけです。

実際にあった問題: Is it possible to implement my own retry strategy? · Issue #1248 · firebase/firebase-admin-node · GitHub

こうしたリトライの問題に対して最も有効な解決策はX-Idempotency-Keyヘッダを利用することです。

POSTリクエストを冪等処理可能にするIdempotency-Keyヘッダの提案仕様 - ASnoKaze blog

簡単に説明すると、X-Idempotency-Keyヘッダに設定された値が同じリクエストは最大1回しか受理しないという機能です。

日本ではLINE APIのX-Line-Retry-Keyヘッダが特に有名です。

失敗したAPIリクエストを再試行する | LINE Developers

サーバー側にこの機能が実装されていれば勝ち確です。されていない場合は個別に対処するしかないので長く険しい旅路が始まります。

リクエストの受け取り側がAt least onceになっている

特に配信を司るシステムにおいて、その品質は3つのカテゴリに分類されます。

  • at-most-once: 0回あるいは1回配信される
  • at-least-once: 1回以上配信される
  • exactly-once: 1回配信される

exactly-onceが最も品質が高いことは間違いないのですが、現実的にこれを採用しているシステムは稀でしょう。 実際、有名なSES, FCM, SendGridの配信品質は全てat-least-onceとなっており、現実的には非常に低確率であるものの配信が2回以上実施されることがあります。

これもまた大規模トラフィック環境においては無視できない問題となり、頭を悩ませることになります。ただ、知識として知っていればユーザーへの説明は十分できるでしょう。

SDKやライブラリのバグ

馴染みがない方にはよく驚かれることですが、大規模トラフィック時にのみ起こるSDKやライブラリのバグは存在します。

先ほど挙げたFCM SDKだけに限らず、スレッドセーフ実装に問題があり負荷がかかった際にのみリクエスト内容が改変されるなども2年に1回は遭遇します。

こうした問題は非常に低確率で発生するため特定が難しく、通常であればSDKやライブラリではなく自分たちの実装から疑うことになるので調査が難航します。

特にミッションクリティカルなシステムの場合、SDKやライブラリを使わずに自前で実装するという選択肢も当たり前に出てきます。

こうした理由があって、自分はSDKやライブラリをすぐに切り離せるように実装する癖をつけています。

また、テストコードではたとえばSDKが最終的に送信するリクエストの内容(ボディやヘッダ)まで検証するようにしておき、自前実装に切り替えた場合に挙動に変化が無いようにする工夫もしています。

無視していたコーナーケースが問題になる

こんなのは起こるはずがないだろう、と考えていたエラーやバグがある日を境に次々と噴水のように湧き出てきます。

これは先ほどの可用性の話と同様で、1,000rpsでたまーに見るなあと思っていた問題は、10,000rpsでは毎日発生するようになります。

確率は同じでも、試行回数が増えたことで期待値が増加しているということです。

特に急成長しているシステムでは「たまーに見るなあ」は早急に対処すべき問題、というわけです。

他にもいっぱいあるので暇なときに書く。

terraformの微妙なところ5選

※ 専門家ではなく一利用者の意見であることに注意してください

実装から間もないリソースや設定が管理できない

terraformはクラウドプロバイダのAPIと.tfの繋ぎ込みをするツールに過ぎないため、繋ぎ込み部が実装されていない場合は当然terraformで管理することができない。 新しく追加されたリソースはもちろん、既存のリソースに対して新しく設定が追加された場合などもterraformで管理することができない。

terraformで管理している設定と管理されていない設定が混在しうる

IaCに求められる重要な役割として「網羅性」が挙げられると思う。 terraformにおいては当然、全てのインフラが.tfに記述されていることが期待されると思うが、実際の運用では必ずしもそうならない。 GUIからぽちぽちしないと作れない類のリソースや設定というのは割とあって、それらはterraformで管理できず、網羅性が担保できない。

ドキュメントを隅々まで読む必要がある

terraformにリソースを追加、あるいは変更しようと思った場合にドキュメントを隅々まで読む必要がある。 例えばbigquery_dataset_iamを見て欲しい。

ドキュメントにはWarnような形でさまざまなNoteが記載されているのだが、このルールに則らない場合、apply(反映)と同時に全てのBQデータセットに対する権限が剥奪されるという挙動が起きる。 こうした罠のような仕様というのはぼちぼちあって、terraformの真っ当な利用者はドキュメントを隅々まで読むことが常に求められる。

ドキュメントを読むことは該当のリソースに対する仕様を全て理解することと等しいため、terraformの勉強というのは基本的に終わりがなく、ちゃんと使おうと思えば思うほど辛い。 当然キャッチアップもしんどい。

また、バグもそれなりにあるため挙動がおかしければ実装を読みにいく必要がある。

github.com

よくあるユースケースでローカルからtfstateファイルに変更を加えなければならない

terraform importやterraform state rmといったコマンドは、tfstateに変更を加える際に利用される。 tfstateはterraformにおいてSSoTのような役割を果たしており、基本的にはこの内容に従ってクラウドプロバイダに変更が反映される。 リソースは同一だがAPIのバージョンが上がってマイグレーションしなければいけなくなった場合などに、これらのコマンドを使ってtfstateをいじらなければならない。 慣れていないと事故に繋がりやすく、さながら本番DBにALTER TABLEをかけているような緊張感がある。

権限が足りない場合、applyするまで分からない

.tfファイルをGitHubで管理し、applyはCircleCIやGitHub ActionsなどのCI/CDツールを用いて行うのがterraformの一般的な使い方である。 権限が足りているかどうかは実際にapplyしないと分からないので、引っかかると割とげんなりする。 planの段階で教えてくれたらいいのに、と常々思っている。

(beatmaniaの話です) ガン下げをついに辞められたので考察した

簡単な自己紹介

十段合格→冥ハード挑戦

くらいまで白数字550(ガン下げ)でした。

ガン下げが良くない(と思われる)理由

一般的にガン下げは良くないと言われていますね、目線を簡単に固定出来るメリットはあると思います。

SUD+(白数字)について:サドプラ下げ過ぎは良くないと思う理由。 | サファリドットコム

が、低速は如何ともしがたい部分があって、自分の場合は卑弥呼の低速はなんとか耐えられるけど、冥の低速はどうしても無理という感じでした。

500回はやったと思う。

どう変化したか

変化前 変化後 差分
緑数字 320 305 -15
白数字 550 260 -290

ここに画像

実際にやってみるとこの差が本当に大きいんですよ。

計算してみると、これだけの変化でガン下げ時よりノーツのスピードは1.7倍ほど速くなります。

HS・緑値・SUD+ - 音ゲー上達の理屈 Wiki - アットウィキ

意外と耐えられるぞ

結論から言うと、目線を極限まで上げればこれだけ白数字を上げても何とかなりました。

それどころか高密度が前よりも見切れるようになった・・・冥もハード出来たし最高。

でも、目線を中間ぐらいにもっていくと相変わらず見切れない。なぜなんでしょうか?

結局重要だったのは目線の固定

ガン下げになっていくプロセスって基本的に

「高密度見切れない!」→「サドン下げたらなんか見やすいぞ!」

という成功体験を積み重ねていく事だと思うんですよ。

しかも一回ガン下げに慣れてしまうと

「さすがにガン下げは今後まずそうだしサドン上げてみるか」→「ノーツ早すぎナニコレ、やっぱり戻そう」

って感じにどんどん後戻りできなくなるじゃないですか。

これがいけなかったんですね。

結局のところ、ガン下げじゃないと見切れなかったのは身体能力的な問題では無かったということですね。

目線を極限に上げて見切れるようになったのは、無理やりにでも目線が固定出来たからだと思います。

つまり

  • 正しい目線固定を練習すれば、ガン下げはいつでも解消できる(と思われる)
  • スピードも上がるので低速が見切りやすくなって嬉しいよ

ということでした。

DMM.comに一ヵ月おじゃましてました(^^)v

https://upload.wikimedia.org/wikipedia/commons/e/e7/DMM.com_logo.gif

1か月間インターンしてました。

受け入れ先は動画配信の基盤を作ってる配信基盤チームっていうイケてるチームです。

↓このチームが何やってるか気になる人はこれを読んで🙏

連載、始めます ~進化する動画配信基盤~ - DMM inside

DMM.comのエンジニアってどんな感じなんですか???というモチベーションで行ったら普通に凄くてびっくりしてきた話です!

良いところ👍

エンジニア的に攻めも守りもつよつよな人が多い

新しいサービスをガンガン立ち上げる攻めのプロと、 既存サービスの売り上げを着実に伸ばしていく守りのプロ。

両方がバッチリ揃っていて、しかも高次元でバランスしている・・・。

これは本当にすごいことで、多様性を重んじる風潮があってとても居心地が良い。

いわゆる「ザッソウ」がすごく浸透している

↓ザッソウとはこれのことですね

職場の「ホウレンソウ」は時代遅れ、会社は「ザッソウ」で強くなる(倉貫 義人) | 現代ビジネス | 講談社(1/4)

人と一緒に仕事するんだし、雑談と相談はどんどんしていった方が良いよね!的な風潮が社内全体にあって良い。

インターンでお邪魔してるだけなのにどんどん話振ってもらえて嬉しかったし、 そういう仲間のために良い仕事をしたい!とモチベーションも上がるのでマジで悪いことが無い。

新しい物好きが多い

ただキャッチアップするだけじゃなく、

どうやったら最大限活用できるか、 どうしたらその魅力を伝えられるか、

この部分をしっかり考えられる人が多くて驚いた。素直にめっちゃ見習いたい。

↓このことが良く分かる記事

働き方に変化はあったのか? フレックスタイム制導入背景と効果検証結果! - DMM inside

昼飯

うまい。

無料(ただしインターンに限る)。

弁当もうまいよ。

AWS, GCPが月1万まで使い放題

神すぎる。限界まで使ってやった。

エンジニアサポート制度「AWS実弾演習場」とは?実際の利用者に話を聞いてみました! - DMM inside

良くないところ👎

トイレのUXが低い

個室が明らかに足りてない。

センサー式の蛇口の割に温水設定でも初手冷水。つめたい。

入口が狭いかつ死角が多いので3回のうち1回は人とぶつかりそうになる。良くない。

帰宅の通勤ラッシュがえげつない

フレックスを使っても混んでるものは混んでる、立地上仕方ないやつですね。

社内で迷子になる

建物を真上から見たときにすべてが点対称になっているので人間には無理です。

まとめ

良いエンジニアいっぱいいます!

仕事もいっぱいあります!

ぜひインターン来てね!

diverta 2019 Programming Contest A~E

コンテストに遅刻したので戒めに解きました。

問題文 Tasks - diverta 2019 Programming Contest

A

N-K+1を出力すればいいです.

B

問題

整数R,G,B,Nが与えられたとき, 以下を満たす(r,g,b)の組はいくつあるか?

R r + G g + B b = N

ただし, R,G,B,Nはそれぞれ1以上3000以下.

解法

rとgが決まると上式よりbが一意に定まる.

このことから, rとgに関して二重forを回すとO(N2)となって間に合う.

C

問題

2文字以上10文字以下の文字列がN個与えられます.

全ての文字列を任意の順番で結合し, 1つの文字列を作ろうとしたとき, 'AB'という部分文字列は最大で何個含まれますか?

解法

与えられる文字列の, 最初の文字と最後の文字のみに着目して, 4つのパターンに分類すると, 貪欲法により解くことが出来ます.

具体的には, 以下のように定めるとうまくいきます.

  • B~A
  • x~A
  • B~x
  • それ以外

この場合分けでうまくいく理屈は, 紙で実験しないと理解し辛いのですが, 以下のように説明できます.

2つの文字列を結合した際に'AB'の数を増やすためには, 1つ目の文字列の最後が'A'で 2つ目の文字列の最初が'B'である必要があります.

そして, それ以外の条件の時で, 'AB'の数が増えることは無いです.

そのため, 最後が'A', 最初が'B'であるパターンを列挙すると答えに寄与するすべての場合を列挙したことになります.

この場合分けが出来れば, 以下の貪欲法で結合することでACがもらえます.

  int ans; //自明な'AB'を既に数え上げているとする
    if(ba != 0){//「B~A」が存在するとき
        ans += (ba-1);//「B~A」は置けるだけ置く
        if(bx!=0){ //その後ろに「B~x」を繋ぐ
            ans++;
            bx--;
        }
        if(xa!=0){//手前に「x~A」を繋ぐ
            ans++;
            xa--;
        }
    }
    ans += min(xa,bx);//好きな位置に「x~AB~x」を置けるだけ置く
    cout << ans << endl;

4つのパターンの優先順位を考えた時, 「B~A」は優先的につないだ方が良さそうなので, 繋ぎます.

このとき, 「B~A」の後ろに「B~x」をつなぐとスコアが1稼げるので, 繋ぎます.

また, 「B~A」の手前に「x~A」をつないでもスコアが1稼げるので, 繋ぎます.

あとは好きな位置に「x~A」と「B~x」を繋いだ文字列を設置します.

以上です.

D

問題

正の整数Nが与えられます.

Nについて以下の条件を満たす全ての正の整数mの総和を求めてください

  • Nをmで割った商とあまりが等しい

解法

N=8の時を実験してみましょう.

商が1の時, 2の時, 3の時を考えると, mに関して以下の方程式が成り立つことが分かります.

  • m_1 \cdot 1 + 1 = 8
  • m_2 \cdot 2 + 2 = 8
  • m_3 \cdot 3 + 3 = 8

以上の観察から, 商がxの時, Nとmに関して以下の方程式が成り立ちます.

  • m_x \cdot x + x = N

上式をmについて解くと, 以下を得ます.

  • m_x = \frac{N}{x} - 1

この結果から, mが整数となるためには, xがNの約数でないといけないことが分かります.

Nの約数列挙はO(\sqrt{N})で出来るので, すべての約数の列挙が時間内に可能です.

E

問題

E - XOR Partitioning

解法

考察1

与えられた数列に対してi番目の要素まで累積XOR和を取ったときの値を, b_iとして定義します.

ここで, b_nがある値Xを取ったとすると, 最適に分割された数列のXOR和はX, 0, X, 0, ... , 0,  Xとなっています.

(そうでないと, 全ての要素のXORの値がXになりません!) (入力例2で試すと良いと思います)

よって, b_iについて, Xを決めたときX, 0, X, 0, ... , 0,  Xと交互に取るような方法が何通りあるか求めれば良いです.

これはDPを使うとO(N)で解けます.

考察2

一方, b_nが0の場合では, Xの決め方が最大でn通りもあるため, 先ほどのDPを適用するとO(n2)となってしまい, うまくいきません.

そこで, Xを決め打った時, 最大でどれだけ高速に計算が行えるか考えます.

今, 以下のようなn=25の数列に対する累積和列bが存在するとします(これは入力例4です).

  • 1 3 6 5 6 0 1 0 8 0 0 3 0 4 2 4 0 0 7 5 0 4 2 0

X=4と決め打って実験していきます.

まず, bに現れる4と0以外の要素には興味が無いため, 除去します.

  • 0 0 0 0 0 4 4 0 0 0 4 0

また, 4や0が連続する部分はDPで計算を省略できるので, 圧縮します.

  • (0,5) (4,2) (0,3) (4,1) (0,1)

以上の操作により, 5要素に圧縮することが出来ました.

また, このような圧縮を行った時の要素数は, b中のXの出現数に依存します.

よって, この5要素に対して先ほどのDPを適用するとXを決め打ったとき, O(b中のXの出現数)で計算が可能です.

さらに, 先ほど示した圧縮は0が出現する位置についての累積和を用いると, 同様にO(b中のXの出現数)で計算が可能です.

あとはXを全ての場合について計算してやると,

  • O(b中のX_1の出現数 + b中のX_2の出現数 + ... ) = O(N)

となり, 解けます.