Skip to content

Instantly share code, notes, and snippets.

@fResult
Last active January 11, 2026 17:42
Show Gist options
  • Select an option

  • Save fResult/72a81980e52ae28bfa24020925e4ef53 to your computer and use it in GitHub Desktop.

Select an option

Save fResult/72a81980e52ae28bfa24020925e4ef53 to your computer and use it in GitHub Desktop.
This is a part of my post, "When Language Ergonomics Fight Your Architecture (And How to Win)" on LinkedIn (https://www.linkedin.com/feed/update/urn:li:activity:7415629554402111488)
import lombok.val;
public class Java14PlusMain {
public static void main(String... args) {
/*
* ⚠️ DEMO NOTE: Exception Handling Strategy
* We catch exceptions here purely for demonstration simplicity.
*
* 🚨 IN PRODUCTION: Treat them differently.
* 1. IneligibleAgeException (Domain Error):
* - This is a Business Rule (Expected Scenario)
* - STRATEGY: Handle it gracefully (e.g., show UI message)
* - BETTER: Treat Error as Data! Prefer returning `Either<Error, Success>` or `Result`
* instead of throwing exceptions for control flow
*
* 2. InvariantViolationException (Developer Error / Bug):
* - This means the code/data is broken (Contract Violation)
* - STRATEGY: Fail Fast
* - DON'T sweep this under the rug (swallowing the exception)
* - This indicates a BUG in the code. Hiding it creates zombie state
* - Let it CRASH loud and clear, so developers can fix it immediately
*/
try {
val anderson = new EligiblePerson("Thomas", "A.", "Anderson", 30);
System.out.println(anderson);
val constantine = anderson.toBuilder().lastName("Constantine").build();
System.out.println(constantine);
System.out.printf(
"First Name: %s, Middle Name: %s, Last Name: %s, and Age: %d%n",
constantine.firstName(),
constantine.maybeMiddleName().orElse("-"),
constantine.lastName(),
constantine.age());
val invalidPerson = constantine.toBuilder().age(16).build();
System.out.println(invalidPerson);
} catch (InvariantViolationException | IneligibleAgeException ex) {
// In a real app, DON'T catch InvariantViolation (Developer Error), it should fail fast!
System.err.println("Error: " + ex.getMessage());
}
}
}
import lombok.val;
public class Java8Main {
public static void main(String... args) {
/*
* ⚠️ DEMO NOTE: Exception Handling Strategy
* We catch exceptions here purely for demonstration simplicity.
*
* 🚨 IN PRODUCTION: Treat them differently.
* 1. IneligibleAgeException (Domain Error):
* - This is a Business Rule (Expected Scenario)
* - STRATEGY: Handle it gracefully (e.g., show UI message)
* - BETTER: Treat Error as Data! Prefer returning `Either<Error, Success>` or `Result`
* instead of throwing exceptions for control flow
*
* 2. InvariantViolationException (Developer Error / Bug):
* - This means the code/data is broken (Contract Violation)
* - STRATEGY: Fail Fast
* - DON'T sweep this under the rug (swallowing the exception)
* - This indicates a BUG in the code. Hiding it creates zombie state
* - Let it CRASH loud and clear, so developers can fix it immediately
*/
try {
val anderson = new EligibleEmployee("Thomas", "A.", "Anderson", 30);
System.out.println(anderson);
val constantine = anderson.toBuilder().firstName("John").lastName("Constantine").build();
System.out.println(constantine);
System.out.printf(
"First Name: %s, Middle Name: %s, Last Name: %s, and Age: %d%n",
constantine.firstName(),
constantine.maybeMiddleName().orElse("-"),
constantine.lastName(),
constantine.age());
val invalidPerson = constantine.toBuilder().age(16).build();
System.out.println(invalidPerson);
} catch (InvariantViolationException | IneligibleAgeException ex) {
// In a real app, DON'T catch InvariantViolation (Developer Error), it should fail fast!
System.err.println("Error: " + ex.getMessage());
}
}
}
import java.util.Objects;
import java.util.Optional;
import lombok.Builder;
import lombok.Value;
import lombok.experimental.Accessors;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;
@Value // ← Use Lombok's @Value to simulate Java Record
@Accessors(fluent = true) // ← Great choice to mimic Record accessors
public class EligibleEmployee {
String firstName;
/*
* 💡 Advanced Note:
* If your project already uses Vavr, you can use io.vavr.control.Option as a field type
* since it is Serializable and plays well with functional patterns.
* However, standard java.util.Optional should remain a return type only.
*/
@Nullable String middleName;
String lastName;
int age;
/* JSpecify's/Jakarta EE's @Nonnull helps us make nullability explicit */
/*
*🛡️ DEFENSIVE CODING:
* @Builder is intentionally placed on this Constructor, NOT the Class.
*
* Why? If placed on the Class, adding a new field without updating this constructor
* would cause Lombok to silently generate a default constructor, BYPASSING these validations.
*
* Keeping it here forces a COMPILE ERROR if fields mismatch, guaranteeing safety.
*/
@Builder(toBuilder = true)
public EligibleEmployee(
@NonNull String firstName, @Nullable String middleName, @NonNull String lastName, int age) {
Objects.requireNonNull(firstName);
Objects.requireNonNull(lastName);
if (age < 25) {
throw new IneligibleAgeException(
"Eligible Employee must be 18+ years old, but got %s".formatted(age));
}
if (firstName.trim().isEmpty()) {
throw new InvariantViolationException("First Name cannot be empty");
}
if (middleName != null && middleName.trim().isEmpty()) {
throw new InvariantViolationException("Middle Name cannot be empty");
}
if (lastName.trim().isEmpty()) {
throw new InvariantViolationException("Last Name cannot be empty");
}
this.firstName = firstName;
this.middleName = middleName;
this.lastName = lastName;
this.age = age;
}
public EligibleEmployee(@NonNull String firstName, @NonNull String lastName, int age) {
this(firstName, null, lastName, age);
}
/**
* This is our ARCHITECTURE ENFORCEMENT.
* It explicitly disables the static builder entry point to prevent Anemic Object creation.
* This forces clients to use the Constructor (or Factory), ensuring all Invariants are validated.
*/
@SuppressWarnings("unused")
private static EligibleEmployeeBuilder builder() {
throw new UnreachableException(
"The %s#builder is unreachable!".formatted(EligibleEmployee.class.getSimpleName()));
}
public Optional<String> maybeMiddleName() {
return Optional.ofNullable(middleName);
}
}
import java.util.Objects;
import java.util.Optional;
import lombok.Builder;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
// Use JSpecify's @NullMarked to enforce @Nonnull as default for every field
@NullMarked
@Builder(toBuilder = true)
public record EligiblePerson(
String firstName,
/*
* 💡 Advanced Note:
* If your project already uses Vavr, you can use io.vavr.control.Option as a field type
* since it is Serializable and plays well with functional patterns.
* However, standard java.util.Optional should remain a return type only.
*/
@Nullable String middleName,
String lastName,
int age) {
// Compact Constructor: The pride of Java 14+
public EligiblePerson {
Objects.requireNonNull(firstName);
Objects.requireNonNull(lastName);
final int eligibleAge = 25;
if (age < eligibleAge) {
throw new IneligibleAgeException(
"Eligible Person must be %d+ years old, but got %s".formatted(eligibleAge, age));
}
if (firstName.trim().isEmpty()) {
throw new InvariantViolationException("First Name cannot be empty");
}
if (middleName != null && middleName.trim().isEmpty()) {
throw new InvariantViolationException("Middle Name cannot be empty");
}
if (lastName.trim().isEmpty()) {
throw new InvariantViolationException("Last Name cannot be empty");
}
}
public EligiblePerson(String firstName, String lastName, int age) {
this(firstName, null, lastName, age);
}
/**
* This is our ARCHITECTURE ENFORCEMENT.
* It explicitly disables the static builder entry point to prevent Anemic Object creation.
* This forces clients to use the Constructor (or Factory), ensuring all Invariants are validated.
*/
@SuppressWarnings("unused")
private static EligiblePersonBuilder builder() {
throw new UnreachableException(
"The %s#builder is unreachable!".formatted(EligiblePerson.class.getSimpleName()));
}
public Optional<String> maybeMiddleName() {
return Optional.ofNullable(middleName);
}
}
// Assume these Classes/Interfaces are in standalone file for each class/interface
// DeveloperError.java ← to denoted that it is a Developer Error, not Business Error
public sealed interface DeveloperError permits
InvariantViolationException,
UnreachableException {}
// DomainError.java ← to denoted that it is Business Error
public sealed interface DomainError permits IneligibleAgeException {}
// InvariantViolationException.java
public final class InvariantViolationException
extends RuntimeException
implements DeveloperError {
public InvariantViolationException() {
super("Invariant Violation");
}
public InvariantViolationException(String message) {
super(message);
}
}
// UnreachableException.java
public final class UnreachableException
extends RuntimeException
implements DeveloperError {
public UnreachableException() {
super("This code should be unreachable");
}
public UnreachableException(String message) {
super(message);
}
}
// IneligibleAgeException.java
public final class IneligibleAgeException
extends RuntimeException
implements DomainError {
public IneligibleAgeException() {
super("Ineiligible Age");
}
public IneligibleAgeException(String message) {
super(message);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment