문제
제한된 입력을 읽는 함수가 정규 파일이 아닌 경우, 크기 제한 초과, 읽은 바이트 초과, ENOENT, 그 밖의 예외를 모두 같은 undefined로 접으면 호출자는 파일이 없는 것과 읽을 수 없는 것을 구별할 수 없다. 빈 값은 편리하지만 의미가 너무 넓다.
계약을 쪼개기
부재는 ‘확인할 대상이 없음’이라는 하나의 결과로만 표현한다. 그 외에는 너무 큼, 예상과 다른 대상, 읽기 중단, 손상되었거나 판독할 수 없음처럼 각각을 보존하는 결과 또는 오류 경계를 둔다. 호출자는 이 분류를 보고 다음 행동을 결정할 수 있어야 한다.
불확실성을 펜스 안에 두기
판독 불가를 부재로 바꾸면 보호 장치가 조용히 열리고, 상위 단계는 없는 입력이라고 믿고 진행한다. 확실히 읽지 못한 경우에는 성공으로 승격하지 않고 확인이 필요한 상태로 남긴다. 불확실한 상태는 누락시키는 대신 다음 단계로 전파하되, 완료 판정에서는 울타리 밖으로 내보내지 않는다.
구현 전후 확인
각 분기에서 호출자가 받는 값이 다른지 작은 표로 적는다. 실제 ENOENT 같은 부재는 부재 판정으로, 정규 파일 아님·크기 초과·읽은 바이트 초과·그 밖의 판독 예외는 각자의 실패 또는 불확실성으로 도착하는지 확인한다. 한 분기만 확인했다고 전체 읽기 계약이 증명되는 것은 아니다.
완료 기준
부재만 안전한 미존재 판정을 만들고, 읽을 수 없음·손상·제한 초과는 각각의 fence를 유지하며, 호출자가 빈 값 하나로 성공을 추정하지 않을 때 완료다. 모르는 것을 없는 것으로 바꾸지 않는 것이 bounded read의 최소 계약이다.
The trap
A bounded reader becomes unsafe when a non-regular input, an oversized input, a read-budget overrun, an absent input, and an unexpected exception all collapse into the same undefined. The caller can no longer tell that nothing exists from the fact that something exists but could not be read.
Split the contract
Reserve the missing result for absence only. Preserve the other outcomes behind their own result or error boundaries: the input may be too large, the object may be the wrong kind, the read may have crossed its budget, or the content may be unreadable or corrupt. The caller needs this classification to choose a safe next step.
Fence uncertainty
Turning an unreadable result into ‘not found’ quietly removes a safety fence. An upper layer can then skip work that still needs attention because it believes there was nothing to inspect. An uncertain read must remain uncertain and must not be promoted to success.
Check every branch
Write down the result expected from each branch before relying on the helper. Confirm that actual absence produces the missing verdict, while a non-regular object, a limit violation, a read-budget violation, and another read error retain their own failure or uncertainty. Checking one branch does not prove the whole read contract.
Completion bar
The contract is sound only when absence has one safe verdict, unreadable or damaged input and limit failures keep their fences, and the caller cannot infer success from one empty sentinel. Do not turn ‘I could not read it’ into ‘it is not there.’
陷阱
有界读取器把非普通文件、超过大小限制、读入字节超过预算、目标不存在以及意外异常都折叠成同一个 undefined 时,就失去了安全性。调用方再也无法判断是没有目标,还是目标存在却无法读取。
拆开契约
只有确实不存在时才返回“缺失”这一种结果。其他情况必须保留各自的结果或错误边界:输入可能过大、对象类型不对、读取超过预算,或者内容已经损坏、不可读取。调用方需要这些分类,才能选择不会误报的下一步。
把不确定性留在围栏内
如果把无法读取改写成“未找到”,安全围栏就会被悄悄拆掉。上层可能因为以为没有内容可查而跳过仍然需要处理的工作。无法确认的读取必须继续保持不确定,不能被提升为成功。
检查每条分支
在依赖这个辅助函数之前,先写清每条分支应该给出的结果。确认真实缺失得到缺失判定,而非普通文件、限制超出、读取预算超出和其他读取错误仍然保留自己的失败或不确定性。只检查一条分支,不能证明整个读取契约正确。
完成标准
只有在缺失拥有唯一且安全的判定、无法读取或损坏的内容以及限制错误都保留围栏,并且调用方不能从一个空哨兵值推断成功时,契约才算稳固。不要把“我读不了”改写成“它不存在”。
罠
bounded reader が、通常のファイルではない入力、サイズ上限超過、読み取りバイト数の超過、不在、予期しない例外をすべて同じ undefined に折りたたむと、安全性が失われる。呼び出し側は、対象がないのか、対象はあるが読めないのかを判別できなくなる。
契約を分ける
不在の結果は、本当に対象がない場合だけに予約する。それ以外は独自の結果またはエラー境界に残す。入力が大きすぎる、対象の種類が違う、読み取り予算を超えた、内容が壊れている・読めない、といった違いを保つことで、呼び出し側は安全な次の処理を選べる。
不確実性を囲う
読めない結果を「見つからない」に変えると、安全の囲いが静かに外れる。上位の処理は調べるべき対象がないと思い込み、必要な作業を飛ばしてしまう。不確実な読み取りは不確実なまま残し、成功へ昇格させない。
すべての分岐を確認する
この helper に依存する前に、各分岐が返すべき結果を書き出す。本当の不在だけが不在判定になり、通常ファイルでない入力、制限超過、読み取り予算超過、その他の読み取りエラーがそれぞれの失敗または不確実性を保つことを確認する。一つの分岐を確認しただけでは、読み取り契約全体の証明にならない。
完了基準
不在に一つの安全な判定があり、読めない・壊れた内容と制限エラーがそれぞれの fence を保ち、呼び出し側が一つの空 sentinel から成功を推測できないときに契約は成立する。「読めなかった」を「存在しない」に変えてはいけない。