「想定外」はユーザーの悲鳴?海外で学んだ、人間臭さに寄り添う「Architecture for Human Friction」のススメ

海外でC# / WPFエンジニアとして設計・開発に明け暮れていると、技術的な進歩以上に「人間という不確定な存在」とどう向き合うか、という哲学的な壁にぶつかることがよくあります。

特にデスクトップアプリの世界では、ミリ秒単位の精度やガチガチの業務ロジックが求められます。つい「ロジックの正しさ」だけを正義だと思いがちですが、数々の修羅場をくぐり抜けてたどり着いた一つの答えがあります。それが、**「Architecture for Human Friction(人間摩擦のためのアーキテクチャ)」**という考え方です。

エッジケースは「エラー」ではない。人間の脆さが露わになる瞬間だ

「それはユーザーの操作ミスだから、バリデーションで弾けばいいよね」

数年前の僕なら、設計会議でドヤ顔でそう言っていたはずです。C#には強力な型システムがあり、WPFには洗練されたデータバインディングや INotifyDataErrorInfo といった仕組みが備わっています。コードが綺麗でロジックが堅牢なら、システムは完璧だ。そう信じて疑いませんでした。

しかし、海外の多種多様なバックグラウンドを持つ人間が、猛烈なプレッシャーの中で使う現場に立ち会ったとき、僕のプライドは音を立てて崩れ去りました。

現場で見た「想定外」の正体

ある物流大手の倉庫管理システム(WMS)刷新プロジェクトでのことです。テスト環境では完璧でした。異常な数値が入ればエラーボーダーが表示され、二重クリック防止も RelayCommand.CanExecute で完璧に制御していました。

ところが、稼働初日の現場は地獄でした。 1分1秒を争う出荷作業の中、作業員たちは疲れ果て、焦っていました。入力欄にデタラメな値を打ち込み、バーコードリーダーを連打し、ネットワークが不安定になった隙に画面を狂ったようにクリックする。システムは仕様通りに「それは不正な操作です」というエラーメッセージを冷酷に吐き出し、処理を中断させました。

僕の書いたコードは、ロジックとしては「正しかった」。でも、システムとしては「最悪」だったのです。

「脆弱な瞬間」を設計する

現地のシニアアーキテクトに言われた言葉が、今でも忘れられません。

「いいか。君が『エッジケース』と呼んでいるものは、単なる統計的な外れ値じゃない。それは、ユーザーが最も無防備で、最も助けを必要としている**『脆弱な瞬間』**なんだ」

僕たちは「全ユーザーの0.1%しかやらない操作」を切り捨てがちです。しかし、その瞬間にユーザーはトラブルに巻き込まれ、パニックになり、システムに裏切られたと感じています。深夜3時にトラブル対応をしているエンジニア、納品期限直前に手が震えている事務員。彼らが操作を誤るのは、無能だからではなく「人間」だからです。

Empathy-First Design Patterns:バックエンドに「心のセーフティネット」を張る技術

海外の現場で「最高にクールだ」と感銘を受けるコードには、共通点があります。それは、正常系の美しさ以上に**「ユーザーがパニックになったときの逃げ道」**がエレガントに用意されていることです。これを実現するための「共感第一」のデザインパターンを紹介します。

1. 「Undo」は機能ではなく「生存戦略」である

「あ、間違えた!」と思った瞬間、ユーザーが探すのはCtrl+Zです。海外のスピード感あふれる現場では、システムが操作を「なかったこと」にできる能力を持つのは、慈悲ではなく義務です。

僕は単なる ICommand を一段階抽象化し、**「Undoable Command Pattern」**を基盤に据えます。 Memento パターンで操作前の状態をスナップショットとして保存し、バックエンドのサービスレイヤーですべての破壊的な操作をスタックに積みます。この「いつでもやり直せる」という感覚が、ユーザーの心理的摩擦(Friction)を劇的に減らすのです。

2. 「不完全な状態」を許容する:The Draft State Pattern

エンジニアはDBに不整合なデータが入るのを嫌いますが、現実の人間は「入力の途中で同僚に話しかけられる」生き物です。

そこで、ドメインモデルとは別に**「Draft State Pattern(下書き状態パターン)」**を導入します。ユーザーが何かを入力するたびに、バックエンドに「不完全な状態」を自動保存(Autosave)します。次にユーザーが戻ってきたとき、「前回の続きから再開しますか?」と声をかけてあげる。これが「共感」のコード化です。

3. CancellationTokenは「ユーザーの拒絶」を受け止める器

重い処理の実行中にユーザーが「やっぱりやめた!」と思う瞬間。 CancellationToken が適切に伝搬されていなければ、アプリはフリーズし、ユーザーの意思は無視されます。これは「システムの拒絶」です。

C#

public async Task ProcessDataAsync(Data data, CancellationToken ct) 
{
    // ユーザーの「やめたい」という意思を、1ミリ秒でも早く検知する
    await _repository.SaveAsync(data, ct); 
}

これはリソース解放のテクニック以上に、**「ユーザーの心変わりを尊重する」**という設計思想なのです。

カオスを許容するリファクタリング:ロジックの美しさと現実の泥臭さ

レガシーコードの多くは「ポステルの法則」——送信するものに関しては厳密に、受信するものに関しては寛大に——が欠けています。入力が1文字違えば Exception を投げて沈黙する。これをどう作り替えるか。

腐敗防止層(Anticorruption Layer)の活用

レガシーなシステムを刷新するとき、中心にある繊細なロジックをいきなりいじるのは眠れる龍の尻尾を踏むようなものです。まずは「人間の支離滅裂な操作」と「古いロジック」の間にクッションを挟みます。

このアダプターは、ユーザーのノイズだらけの入力をレガシー側へ送る前に「翻訳」し、無理な場合は優しくユーザーにフィードバックを返します。中心部の整合性を守りつつ、表面の柔軟性だけを上げる戦略です。

「例外」を「対話」に変換する

C# 9.0以降の recordOneOf ライブラリなどを使って、メソッドの戻り値を「成功/失敗」の二択ではなく、「ユーザーにこれを確認してほしい」という複数の選択肢として定義し直します。

C#

// 戻り値には「在庫切れ」「入力ミス」「後で再試行してね」などが含まれる
public ProcessResult ProcessOrder(Order order);

「コードが例外を投げる」のを防ぐのではなく、**「例外が起きるような状況を、ユーザーと一緒に解決するプロセス」**へとリファクタリングするのです。

結:エンジニアリングの極致は、コードの先にある「体温」を感じること

海外で働いていると、日本とは全く違う文化や価値観に圧倒されます。そんなカオスな環境で、僕たちはつい「ロジック」というシェルターに逃げ込みたくなります。しかし、シェルターの中から外を眺めているだけでは、人を動かすシステムは作れません。

「仕様」の向こう側にある震える手

あるトラブルの原因が、ユーザーによる「戻る」ボタンと「確定」ボタンの同時押しだったとき、それを「誤操作」で片付けるのは簡単です。でも、そのログの裏側には、締め切りに追われ、焦りで手が震えている一人がいます。

僕が書く CancellationTokenUndo のロジック、そしてカオスを許容するリファクタリングは、すべて**その震える手を優しく包み込むための「体温」**なのだと思うようになりました。

コードは冷たい0と1の塊ですが、そこに「体温」を宿らせることはできます。「このシステムは、僕が間違えることを知っていて、それでも守ってくれている」。ユーザーがそう感じた瞬間、僕たちの仕事はただの作業から「エンジニアリング」へと昇華されます。

共感(Empathy)は世界共通の言語です。英語が完璧でなくても、コードに共感が溢れていれば、それは必ず伝わります。

次は君の番だ

明日、コードを書くとき、ほんの10秒だけでいい。画面の向こう側にいる「最高に調子が悪くて、焦っていて、ミスを連発しているユーザー」の姿を想像してみてください。

そのとき、君が書く if 文や例外メッセージはどう変わるでしょうか。その小さな変化こそが、君を「Architecture for Human Friction」を体現するエンジニアへと変える第一歩になるはずです。

いつか、どこかのプロジェクトで一緒に「体温のあるコード」を書ける日を楽しみにしています。

コメント

タイトルとURLをコピーしました