Member-only story
Java Records and Shallow Immutability: The Hidden Mutability That Can Break Your Production Code
An in-depth guide to Java Records, defensive copying, mutable collections, hash-based collections, concurrency, and production-grade API design.
Introduction: The Record That Changed Without Being Modified
Imagine you are reviewing a Spring Boot pull request. A developer has introduced a Java Record to represent an order returned by a REST API.
The implementation looks clean:
public record Order(
String orderId,
List<String> items
) {
}There are no setters. The fields are final. The compiler generates the constructor, accessors, equals(), hashCode(), and toString().
Someone reviewing the code says, "Good. We are using immutable DTOs now."
The implementation is merged.
A few weeks later, an integration test fails intermittently. An order response contains an extra item that was never added through the order service. Another test removes an item from the returned list and unexpectedly changes the object used by a subsequent operation.
The original implementation was simple. The failure is not.
Consider the following code:
List<String> items = new ArrayList<>();
items.add("Laptop");
Order order = new Order("ORD-1001", items)…







