Band: small, and it is the most valuable small thing in this milestone
Measuring Verdict against the modern field turned up one place where it is genuinely ahead, and the README does not mention it.
A single-error failure is free in Verdict and is not free in the two closest competitors.
| Library |
failure: construct + read |
| Verdict |
0 B |
| CSharpFunctionalExtensions 3.7.0 |
0 B |
| ErrorOr 2.1.1 |
88 B |
| LightResults 10.0.4 |
56 B |
| FluentResults 4.0.0 |
576 B |
The reason is a design decision that is already made and already documented nowhere: Result<T> holds a single Error struct inline (src/Verdict/Result.cs:18-20), so a failure is a field assignment. ErrorOr holds a List<Error>, so constructing a failure allocates the list even when there is exactly one error. Multiple errors in Verdict require the opt-in Verdict.Extensions package, which is exactly the "zero-allocation core, scale through opt-in packages" constraint the project was built on, doing its job.
This matters because the failure path is not the rare path. A service under partial outage runs it on every request, and that is the moment GC pressure is least welcome.
Why it is worth writing down
The README's competitive argument is entirely "189x faster than FluentResults", which measures a struct against a class and is a comparison every modern competitor already wins. It is true, and it is not a reason to switch from ErrorOr. This is.
The honest version of the claim, which is what should replace it:
On the success path Verdict allocates nothing, and so do ErrorOr, CSharpFunctionalExtensions and LightResults. Where Verdict differs is the failure path: a single-error failure is 0 bytes, against 88 for ErrorOr and 56 for LightResults, because the error lives in the struct rather than in a list. Multiple errors are an opt-in package.
Shorter than what is there now, checkable, and it survives a reader going and measuring it.
What should change
- Put the failure-path table in the README, in place of one of the five restatements of 189x. Depends on the README rewrite.
- Add the failure-path comparison to
CompetitiveBenchmarks, so the claim has a benchmark behind it and cannot quietly stop being true. Depends on the benchmark field issue.
- Add a row to the allocation gate pinning
Result<T>.Failure(error) at 0 B. It is already there and already passing, which is the point: this is now a competitive claim and not just an implementation detail, so it needs the gate that says so.
Band: small, and it is the most valuable small thing in this milestone
Measuring Verdict against the modern field turned up one place where it is genuinely ahead, and the README does not mention it.
A single-error failure is free in Verdict and is not free in the two closest competitors.
The reason is a design decision that is already made and already documented nowhere:
Result<T>holds a singleErrorstruct inline (src/Verdict/Result.cs:18-20), so a failure is a field assignment. ErrorOr holds aList<Error>, so constructing a failure allocates the list even when there is exactly one error. Multiple errors in Verdict require the opt-inVerdict.Extensionspackage, which is exactly the "zero-allocation core, scale through opt-in packages" constraint the project was built on, doing its job.This matters because the failure path is not the rare path. A service under partial outage runs it on every request, and that is the moment GC pressure is least welcome.
Why it is worth writing down
The README's competitive argument is entirely "189x faster than FluentResults", which measures a struct against a class and is a comparison every modern competitor already wins. It is true, and it is not a reason to switch from ErrorOr. This is.
The honest version of the claim, which is what should replace it:
Shorter than what is there now, checkable, and it survives a reader going and measuring it.
What should change
CompetitiveBenchmarks, so the claim has a benchmark behind it and cannot quietly stop being true. Depends on the benchmark field issue.Result<T>.Failure(error)at 0 B. It is already there and already passing, which is the point: this is now a competitive claim and not just an implementation detail, so it needs the gate that says so.