はじめに
現役トラックドライバーが独学でPythonを学び、
物流現場の知識を武器に
Webエンジニアへの転身を目指しています。
※2026年5月12日開始〜・学習122時間時点の記録です。
先日、自作の運転性格診断アプリ 「🚛DPT(Driver Personality Test)」 にクリック時のボタンの リップルアニメーション(波紋エフェクト) を実装しました。見た目を良くするための、ちょっとした改善のつもりでした。
ところがこの過程で、家のトイレの 「点滅ランプ」 と、コードの中の isProcessing というフラグが、実は全く同じ役割を果たしていることに気づいてしまったので、その話をまとめておきます。
きっかけは「連打対策」だった
DPTでは、選択肢のボタンを押すと次の質問に切り替わる作りになっています。当初はボタンを押した瞬間に即座に次の質問へ切り替わっていました。
しかしこの仕様だと、ボタンを連打するとその速度のままどんどん次の質問に進んでしまい、極端な話「50問を数秒で終わらせる」ことができてしまいました。診断アプリとしての信頼性にも関わる問題です。
これを防ぐために導入したのが、以下のような 「処理中フラグ」 でした。
let isProcessing = false;
testContainer.addEventListener('click', (event) => {
if (isProcessing) return; // 処理中なら何もしない
const button = event.target.closest('.option-btn');
if (!button) return;
isProcessing = true; // 「今から処理中だよ」に切り替える
createRipple({ currentTarget: button, clientX: event.clientX, clientY: event.clientY });
const qIdx = Number(button.dataset.qIndex);
const oIdx = Number(button.dataset.oIndex);
setTimeout(() => {
if (oIdx === -1) {
handleAnswer(qIdx, -1, '回答なし', 0);
} else {
const opt = shuffledQuestions[qIdx].shuffledOptions[oIdx];
handleAnswer(qIdx, oIdx, opt.text, opt.score);
}
isProcessing = false; // 処理が終わったら「処理中じゃない」に戻す
}, 300);
});
isProcessingがtrueの間はクリックを一切受け付けず、falseに戻るまで待つ。 たったこれだけの仕組みですが、これによって連打による多重処理や不正な回答記録を未然に防ぐことができました。
「あれ、これ家のトイレ🚽と同じじゃないか❓」
このコードを実装しながらふと思い出したのが、自宅の トイレの操作パネル でした。
ボタンを押して水を流すと、しばらくランプが点滅します。この点滅中は、ボタンを何度押しても一切反応しません。15秒ほど待つとランプが点灯に変わり、ようやく 「もう一度流していいですよ」 という状態になります。
整理すると、対応関係はこうなります。
| 🚽トイレ | 🚛DPTのコード |
|---|---|
| ボタンを押す | ボタンをクリックする |
| 点滅中(処理中) | isProcessing = true |
| 点滅中にボタンを押しても無反応 |
if (isProcessing) return; で弾かれる |
| 15秒待つ |
setTimeout(..., 300) で待つ |
| 「流していいよ」の点灯に戻る |
isProcessing = false に戻る |
トイレの設計者は、当然 「みんな一回ボタンを押したら大人しく待ってくれる」 なんて期待していません。 よくわからず連打してしまう子供 や、 せっかちな大人が何度もボタンを押すこと を、あらかじめ想定しているはずです。連打されても水量が足りず流れないだけならまだしも、 下手をすればシステムが誤作動を起こす可能性 だってあります。だからこそ 「点滅中は無反応にする」 という仕組みが必要になるわけです。
コードの isProcessingフラグ も、まったく同じ発想でした。「ユーザーは一回だけクリックする」という性善説では設計せず、 「連打されるかもしれない」 という前提で作る。これが世の中でいう 防御的プログラミング(defensive programming) という考え方だと、後から知りました。
🚛トラックの車間距離も、実は同じ発想
さらに話を広げると、これはトラックの 車間距離の取り方 とも同じ構造をしていると感じました。
停止距離 = 空走距離 + 制動距離
私たちトラックドライバーは、自分の車がブレーキを踏んでからどれくらいの距離で安全に止まれるか、いわゆる 「制動距離・停止距離」 を常に考えながら走っています。なので、普通車の時よりも 多くの車間距離が必要 になります。
「停止距離」 とは、危険を感じてから車が完全に止まるまでに進む 全体の距離 のことです。これは、 ドライバーが危険を察知してブレーキを踏むまでの「空走距離」 と、 ブレーキが効き始めてから車が停止するまでの「制動距離」 を合わせたものになります。
☔雨の日は路面とタイヤの間に水が入り込み、ハンドルやブレーキが利きにくくなる 「ハイドロプレーニング現象」 が起きることもあります。天候が悪化すると摩擦係数が下がり、同じ速度でも制動距離が伸びてしまうため、普段より 多くの車間距離が必要 になります。
車間距離を詰めて走っても、前の車が完璧なブレーキ操作をしてくれるなら何も問題は起きません。しかし実際の道路には 「急に割り込んでくる車」「予告なく急ブレーキを踏む車」 が一定数存在します。だからこそ、相手が予期しない動きをする前提でマージン(車間距離)を確保しておく。これは7年半のトラック運転の中で、意識せずとも体に染み付いていた感覚でした。
🚽トイレの設計者は 「利用者が連打するかもしれない」 ことを想定している
💻コードを書く自分は 「ユーザーが連打するかもしれない」 ことを想定する
🚛トラックドライバーは 「周りの車が予期しない動きをするかもしれない」 ことを想定している
現場の違いはあれど、根っこにある考え方は 「相手(利用者・他車)は思い通りに動かない前提で、マージンや防御策をあらかじめ組み込んでおく」 という一点に集約されるのだと思います。
※「停止距離」についてはアシストライン株式会社さんのこちらの記事をご覧ください。
おわりに
見た目のためのアニメーション実装のつもりが、結果的に「入力の信頼性を守る」という副産物を生み、さらにそれが日常生活やこれまでの職業経験とも地続きになっていることに気づけたのは、想定外の収穫でした。
教科書を使わず、AIとの対話だけでプログラミングを学んでいる身としては、こうして身の回りの 「なぜこうなっているんだろう❓」 を分解しながら理解を深めていくスタイルが、性に合っているのかもしれません。
同じように独学で学んでいる方の参考に、少しでもなれば幸いです。