Build1 distinct publisher3 min readUpdated
A JetBrains Kotlin post argues that expected business failures belong in the return type, not the implementation. The payoff is that error handling becomes reviewable.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
JetBrains' Kotlin blog has published a piece arguing that a signature ending in `Unit` discloses nothing, built around one example: `fun signDocument(documentId: UUID, code: String): Unit` [1]. In Kotlin, `Unit` means the function completes without returning a meaningful value, roughly Java's `void` [2], so the line promises that the call may succeed and says nothing else.
The failures are not hypothetical. The post lists them: the code might be invalid, the signing window might have closed, the database might be down, the document might already be signed or expired, or the request might have arrived out of order from a buggy client [3]. None of that is visible in the declaration. To recover it, the author writes, you open the implementation, then the service it calls, then the exception handlers, the route mapping, the tests, the OpenAPI spec, and the client code [4]. That is seven artifacts read in sequence to reconstruct what one line should have stated [5].
The useful part of the argument is not the `Either` type. It is the triage that comes before it. The post splits bad outcomes into three groups [6]. API client errors, such as malformed JSON, a missing header, an unsupported operation, an out-of-sequence request, or access that is not allowed, are things a working client should almost never produce, and no designer has drawn a screen for them; they collapse into coarse HTTP responses, a 400, a 403, a 404, and are not enumerated one by one in the domain model [7]. Unexpected exceptions, such as an unavailable database, a dependency timeout, a dropped network, a `NullPointerException`, or a broken invariant, are not business outcomes at all; they become operational signals, a 500 to the client, a stack trace in the logs, a spike in the error-rate metric, a page to whoever is on call [8].
The third group is the one that has to appear in the type. The signing code was wrong, the window has closed, the document was already signed, approval is missing, the policy rejected it: cases where the client behaved correctly and the operation still cannot succeed, and where designers have a specific screen for each [9]. The test the post proposes is blunt and checkable in review: if a healthy client needs to handle two outcomes differently, those two outcomes must be distinguishable in the type [9]. That produces `fun signDocument(documentId: UUID, code: String): Either<DocumentSignError, Unit>`, with inputs on the left and the expected failure type alongside the success type on the right [10]. Two of the three categories stay out of the domain model entirely [11].
The stated rule is that a failure belonging to the business logic belongs in the signature, the API contract, and the client's handling code, not buried in the implementation [12], with the goal that the signature alone is enough to know how to call the function and how to handle every expected outcome [13]. The author works on authentication and verification at Salmon and says a mishandled failure is rarely cosmetic, because the difference between two error cases can be the difference between letting the right person through and the wrong one [14]. The accompanying material about Salmon's engineering commitments is the least load-bearing part of the argument [15].
Watch where teams draw the boundary, because that is where the work actually is: whoever decides that a timeout is operational and a closed window is domain is deciding what users see. The post says it often sees people mistakenly dragging the second category into the domain model, but the text available breaks off mid-sentence at exactly that point [16], so the guidance on the hardest case is missing. The mechanism is not Kotlin-specific; the author says the concept carries to any language with sealed types [17].
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
Unexpected exceptions include an unavailable database, a dependency timeout, a dropped network, a NullPointerException, or a broken invariant leaving an illegal state. These are not business outcomes and are not modelled as domain errors; they become operational signals: a 500 to the client, a full stack trace in the logs, a spike in the error-rate metric, and a page to whoever is on call.
The author writes that at Salmon he works on authentication and verification, that a mishandled failure is rarely cosmetic, and that the difference between two error cases can be the difference between letting the right person through and the wrong one.
The post says Salmon's engineering culture runs on real ownership from day one, high standards held in the open, and a refusal to ship things that do not actually work, and that a function hiding its failures is at odds with all three.
A JetBrains Kotlin blog post titled "Signatures, be true: domain errors and functional handling in Kotlin" opens with the example function `fun signDocument(documentId: UUID, code: String): Unit` and argues the reader cannot tell what could go wrong from it.
In Kotlin, Unit means the function completes without returning a meaningful value, roughly equivalent to void in Java.
Failures the signDocument example must reckon with include: the code might be invalid, the signing window might have closed, the database might be down, the document might already be signed or expired, or the request might have arrived out of order from a buggy client. None is visible in the signature.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Single self-published essay, argument not measurement
Every claim traces to one article on the language vendor's own blog. The article's internal content is directly verifiable - the signatures, the three-category taxonomy, the Salmon framing - so descriptive claims are solid, but the substantive thesis rests entirely on the author's reasoning and illustrative code, with no benchmarks, defect data, codebase study, or second publisher. One ledger claim about where the text ends is also contradicted by the supplied body, which lowers confidence in the completeness of the captured evidence.
No adoption signal in the supplied material
The cluster contains no release, deployment, usage disclosure, benchmark, or pricing/licensing event. The post's remark that the default in most Java and Kotlin codebases is to validate-then-throw is an unquantified aside, and the author's own use of the pattern at Salmon is asserted without scale, timeline, or results. Nothing measurable to score.
Mildly overstated relative to evidence
The technique argument is deliberately modest - the author disclaims monads and category theory and confines the prescription to domain errors - which keeps the gap small. The overstatement comes from unbacked payoff framing: the claim that a signature is enough to know how to handle every expected outcome, and the surrounding culture rhetoric about refusing to ship things that do not work, are presented as settled benefits with no adoption or defect evidence and no accounting of migration cost. Positive but small.
Vendor channel plus employer branding
The piece runs on JetBrains' own Kotlin blog, where promoting Kotlin idioms such as sealed types and Either-shaped results serves the language vendor's interest. The author additionally advertises his employer Salmon by name, describing its engineering commitments and his authentication and verification work, which reads as recruiting-adjacent framing. These incentives are visible on the page rather than hidden, and no payment, sponsorship, or product-sale relationship is disclosed in the source, so the score reflects clear but transparent alignment.
Descriptively firm, substantively unverifiable
Confidence is moderate-low. What the article says is well captured and internally consistent, so claims about its content are reliable. But there is one publisher, no corroboration, no adoption or outcome data, the captured body is truncated before the mechanism discussion concludes, and one ledger claim about the truncation point is contradicted by the supplied text - all of which cap how much can be concluded about the technique's real-world standing.
build
2,513 tool calls, zero refactorings: what agents actually do when you ask them to refactor1 distinct publisher
build
A Rust veteran's first Zig project: the friction was tooling and layout, not safety1 distinct publisher
product
Hypercubic's $5.3M bet: the hard part of COBOL migration is reading the code, not writing it1 distinct publisher
build
One game, two codebases: where parity belongs when you ship native on iOS and Android1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
1 article · August 19, 2026