「クリックされる」を捨てると、認可の設計が見えてくる
Hatched by Ryusei Nakamura
Aug 09, 2026
1 min read
0 views
91%
「ボタンがクリックされる」と書いた瞬間、その設計書から誰かの意思が消える。では、認可を「許可されているか確認する」とだけ設計したとき、そこでは何が起きているのだろうか。
一見すると、これは文章表現とフレームワークの小さな仕様差に見える。しかし両者の交点には、ソフトウェア設計における重要な問いがある。このシステムで、誰が行為者であり、どの地点でその行為を止めるのか。主語の選び方と認可の扱い方は、実は同じ問題を別の角度から扱っている。
受動態は、責任の所在を隠す
基本設計書に「ユーザーにより削除される」「クリックされる」「登録される」と書くと、文章は一見客観的になる。だが、その客観性には代償がある。ユーザーが何を意図し、システムが何を判断し、結果として何が起きるのかという因果関係が薄れてしまうからだ。
「削除される」という文には、少なくとも三つの情報が欠けている。誰が削除を試みるのか。システムはその試みを許可するのか。許可されない場合、何が起きるのか。これらを曖昧にしたまま実装に進むと、画面仕様、API仕様、認可ロジックがそれぞれ別の解釈を持ち始める。
反対に、「ユーザーが請求書を削除する」と書けば、設計の中心に行為者が現れる。すると次の問いが自然に生まれる。
「すべてのユーザーが削除できるのか」 「削除を試みた時点で確認するのか」 「権限がなければ画面から隠すのか、それとも実行時に拒否するのか」
能動態は、単に読みやすい文章を作るための技法ではない。設計上の責任と判断の流れを可視化する技法である。
ここで重要なのは、ユーザーを主語にすれば十分だという意味ではない。システムの処理も、適切に主語として書く必要がある。「ユーザーが削除を要求する。システムは権限を確認する。権限がある場合、システムは削除する」というように、行為と判断を分けて記述することで、仕様は動詞の連鎖になる。
認可には二つの役割がある
認可処理にも、似ているようで異なる二つの役割がある。一つは、ある能力が許可されているかを調べ、その結果を使って処理を分岐させること。もう一つは、その能力が必須であることを宣言し、許可されていなければ処理を強制的に中断することだ。
前者は、たとえば allows のような確認に対応する。ユーザーが編集権限を持つなら編集ボタンを表示し、持たないなら読み取り専用にする。ここでは、許可の有無は画面や処理を選択するための情報である。
後者は、authorize のような強制に対応する。更新処理の入口で権限を確認し、許可されていなければ例外を発生させて、更新そのものを実行させない。ここで認可は単なる情報ではなく、アクションを成立させる条件になる。
この違いを見落とすと、認可チェックは書かれているのに安全ではないという状態が生まれる。たとえば次のようなコードを考えてみよう。
if ($user->allows('delete', $invoice)) {
// ここに何かを書く
}
$invoice->delete();
確認はしている。しかし、その結果を無視して削除しているため、認可は存在しないのと同じだ。これは技術的なミスであると同時に、設計書の主語が曖昧だった結果でもある。「権限を確認する」とだけ書き、「確認後に何をするか」を書かなかったからだ。
一方で、処理の途中に強制的な認可を置くと、設計は明確になる。
$this->authorize('delete', $invoice);
$invoice->delete();
この二行には、重要な順序がある。ユーザーが削除を試みる。システムは削除能力を確認する。許可された場合だけ、システムは削除する。許可されない場合、削除処理には到達しない。
認可とは、「許可されているか」を知るための質問ではない。場合によっては、「この行為を続けてよいか」をシステム自身に問う境界線である。
仕様書は、システムの倫理を先に決める
認可をどこに置くかは、コードの整理方法だけの問題ではない。ユーザーにどのような失敗を経験させるか、システムがどの程度責任を持って危険な操作を止めるかを決めている。
たとえば、管理画面で「請求書を削除する」機能を考える。権限がないユーザーに削除ボタンを見せない設計は、操作可能性を事前に伝えるという点で親切だ。しかし、ボタンを隠すことは認可の代わりにならない。APIを直接呼び出す利用者や、古い画面を使う利用者が存在するからだ。
したがって、表示上の確認には allows 的な分岐を使い、実行上の防壁には authorize 的な強制を使う必要がある。前者はユーザー体験のため、後者はシステムの安全性のためだ。この二つを同じものとして扱うと、「見えないから安全」という危険な錯覚に陥る。
ここから、認可設計を三層に分けると理解しやすい。
- 発見の層: ユーザーが何をできるかを画面で示す。ボタンやメニューを表示、非表示にする。
- 判断の層: そのユーザーが対象のデータに対して能力を持つかを判定する。
- 強制の層: 許可されていない処理を、必ず実行前に止める。
発見の層は便利さを提供する。判断の層は一貫性を提供する。強制の層は信頼性を提供する。三層は連携しているが、代替関係ではない。
同じことは、設計書の文章にも当てはまる。「削除ボタンを表示する」「ユーザーが削除を試みる」「システムは権限を検証する」「許可されなければエラーを返す」と書けば、三層の関係が読める。一方、「削除される」とだけ書けば、発見も判断も強制も、文章の外に押し出される。
主語、権限、失敗を一つの型で設計する
実務で有効なのは、すべての重要な操作を、次の四要素で記述する方法だ。
行為者、対象、能力、拒否時の結果である。
たとえば、曖昧な仕様はこう書かれている。
「注文がキャンセルされる」
これを次のように変える。
「購入者が発送前の注文をキャンセルする。システムは購入者と注文の関係、および注文状態を確認する。条件を満たさない場合、システムはキャンセルを実行せず、理由を通知する」
この書き換えによって、認可の条件が具体化される。権限はユーザーだけに属するとは限らない。対象の所有者であること、注文が特定の状態であること、操作期限内であることも、能力を成立させる条件になり得る。
さらに、「拒否時の結果」を書くことが特に重要だ。認可に失敗したとき、例外を投げるのか、別の画面に遷移するのか、再認証を要求するのか、単に操作を無効化するのか。失敗は例外的な出来事ではなく、権限を持たない利用者が存在する限り、通常の業務フローの一部である。
この型を使うと、設計レビューでの質問も変わる。「この機能は誰が使うのか」だけでは足りない。「誰が試みるのか」「誰が許可を判断するのか」「どの地点で止めるのか」「拒否された人は何を理解できるのか」と問えるようになる。
ここには一つの実践的な原則がある。**許可を確認する場所は、できるだけ操作の境界に置く。**画面表示だけでなく、コントローラーやサービスの入口、さらに必要ならデータ操作の直前でも保証する。認可の責任を複数箇所に分散しすぎると、漏れが生まれる。反対に、重要な操作の入口に強制的な確認を置けば、呼び出し元が増えても安全性を保ちやすい。
Key Takeaways
- 設計書では受動態を減らし、行為者を主語にする。誰が何を試み、システムが何を判断するのかを動詞で書く。
- 認可の確認と認可の強制を分ける。画面の分岐には確認を使い、実行してはいけない処理には強制的な拒否を使う。
- UIで隠すことを、セキュリティ対策と混同しない。表示されない操作でも、APIや処理の入口で必ず認可する。
- すべての重要操作を四要素でレビューする。行為者、対象、能力、拒否時の結果を確認する。
- 拒否を正常な業務フローとして設計する。権限がない場合に、何を止め、何を伝え、次に何を可能にするかを明記する。
ソフトウェアの設計品質は、成功する経路だけをどれだけ美しく描けるかでは決まらない。誰かが行為を試み、その行為をシステムが認めない瞬間を、どれだけ正確に表現し、確実に制御できるかで決まる。
だから、次に仕様書を書くときは「何がされるか」ではなく、「誰が何をするか」から始めたい。そして、次に認可を書くときは「許可されているか」と尋ねるだけでなく、「許可されなかったら、この処理をどこで止めるか」まで決めたい。
**良い設計とは、システムに能力を与えることではない。誰の意思を、どの条件で、どこまで実行させるかを明示することだ。**主語を取り戻した文章は、そのまま責任の境界を取り戻したシステムになる。
Sources
Hatch New Ideas with Glasp AI 🐣
Glasp AI allows you to hatch new ideas based on your curated content. Let's curate and create with Glasp AI :)
Start Hatching 🐣