Clear and honest code
Every software developer has encountered this scenario: you’re working in a large codebase, and you need to use a function. The IDE shows you its signature, it looks straightforward, but when you use it, surprises await. This is where the importance of honest and clear code becomes evident.
Consider this example:
1
2
3
4
5
// (account: Account, amount: BigDecimal) -> Account
fun credit(account: Account, amount: BigDecimal): Account {
if (amount < ZERO) throw NegativeAmountError(amount)
return account.copy(balance = account.balance + amount)
}
The function signature appears simple, but it hides a critical detail - it can throw an exception. This lack of transparency creates two significant problems:
- Cognitive Overhead: Developers must dive into the implementation to understand the complete behavior
- Trust Issues: When signatures don’t tell the whole story, developers become wary of using any code without thorough investigation
Here are two alternative approaches that make the function’s behavior explicit:
1
2
3
(account: Account, amount: NonNegativeCurrency) -> Account
// or
(account: Account, amount: BigDecimal) -> Either<NegativeAmountError, Account>
Both signatures are more honest, but they use different strategies:
- The first version uses type constraints to prevent invalid states entirely
- The second version explicitly communicates possible outcomes through the return type
By making our code’s behavior explicit in its signature, we create more maintainable and trustworthy systems.
References
- Treat Type as set (Types as Sets by Mark Seemann and Elm Guide: Types as Sets)
- Algebraic data type
This post is licensed under CC BY 4.0 by the author.