<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<font size="4" face="monospace">I don't think adding the conditional
deconstruction story (as interesting as it is!) would shed light
on this question. I think the answer lies in "why does the
active-row pattern not obey the requirements for being a
carrier." Whether deconstruction is conditional or unconditional,
the problem is still that if someone can create records/carriers
with<br>
<br>
PersonRow r = new PersonRow(rand.nextInt(), "Bob Smith")<br>
<br>
and then persist them with<br>
<br>
database.persist(r);<br>
<br>
they are bestowing the right to update random rows that were not
dispensed by the ORM. The database API has, by virtue of the fact
that PersonRow has a public constructor that accepts an ID,
essentially exposed a wider API than it intended to. <br>
<br>
The answer has always been "don't use carriers/records for this",
but the interesting sub-question is (a) how to explain this
succinctly to users so they get it and (b) what to tell them to do
instead.<br>
<br>
</font><br>
<div class="moz-cite-prefix">On 1/26/2026 10:12 AM, Ethan McCue
wrote:<br>
</div>
<blockquote type="cite" cite="mid:CA+NR86h60gtt-MwWoPXpGFecMSVTQFYQ1FLNFU3pyac=m2fDwg@mail.gmail.com">
<div dir="auto">
<div>My immediate thought (aside from imagining Brian trapped in
an eternal version of that huffalumps and woozles scene from
Winnie the Pooh, but it's all these emails) is that database
entities aren't actually good candidates for "unconditional
deconstruction" </div>
<div dir="auto"><br>
</div>
<div dir="auto">I think this because the act of getting the data
from the db/persistence context is intrinsically fallible
*and* attached to instance behavior; maybe we need to look
forward to what the conditional deconstruction story would be?</div>
<div dir="auto"><br>
<div class="gmail_quote gmail_quote_container" dir="auto">
<div dir="ltr" class="gmail_attr">On Mon, Jan 26, 2026,
10:04 AM Brian Goetz <<a href="mailto:brian.goetz@oracle.com" moz-do-not-send="true" class="moz-txt-link-freetext">brian.goetz@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div> <br>
<br>
<blockquote type="cite">
<div dir="ltr">
<div>It's interesting that when language designers
make the code easier to write, somebody may
complain that it's too easy :-)</div>
</div>
</blockquote>
<br>
I too had that "you can't win" feeling :)<br>
<br>
I would recast the question here as "Can Java developers
handle carrier classes". Records are restricted enough
to keep developers _mostly_ out of trouble, but the
desire to believe that this is a syntactic and not
semantic feature is a strong one, and given that many
developers education about how the language works is
limited to "what does IntelliJ suggest to me", may not
even _realize_ they are giving into the dark side. <br>
<br>
I think it is worth working through the example here for
"how would we recommend handling the case of a "active"
row like this. <br>
<br>
<blockquote type="cite">
<div dir="ltr">
<div>I think it's a perfect place for static
analysis tooling. One may invent an annotation
like `@NonUpdatable` </div>
<div>with the `RECORD_COMPONENT` target and use it
on such fields, then create an annotation
processor </div>
<div>(ErrorProne plugin, IntelliJ IDEA inspection,
CodeQL rule, etc.), that will check the violations
and fail the build if there are any.</div>
<div>Adding such a special case to the language
specification would be an overcomplication.</div>
<div><br>
</div>
<div>With best regards,<br>
Tagir Valeev.</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Sun, Jan 25,
2026 at 11:48 PM Brian Goetz <<a href="mailto:brian.goetz@oracle.com" target="_blank" rel="noreferrer" moz-do-not-send="true" class="moz-txt-link-freetext">brian.goetz@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div> <font size="4" face="monospace">The
important mental model here is that a
reconstruction (`with`) expression is "just" a
syntactic optimization for:<br>
<br>
- destructure with the canonical
deconstruction pattern<br>
- mutate the components <br>
- reconstruct with the primary constructor<br>
<br>
So the root problem here is not the
reconstruction expression; if you can bork up
your application state with a reconstruction
expression, you can bork it up without one. <br>
<br>
</font><font size="4" face="monospace">Primary
constructors can enforce invariants _on_ or
_between_ components, such as:<br>
<br>
record Rational(int num, int denom) { <br>
Rational { if (denom == 0) throw ... }<br>
}<br>
<br>
or<br>
<br>
record Range(int lo, int hi) { <br>
Range { if (lo > hi) throw... }<br>
}<br>
<br>
What they can't do is express invariants
between the record / carrier state and "the
rest of the system", because they are supposed
to be simple data carriers, not serialized
references to some external system. </font><font size="4" face="monospace">A class that models
a database row in this way is complecting
entity state with an external entity id. By
modeling in this way, you have explicitly
declared that <br>
<br>
rec with { dbId++ }<br>
<br>
*is explicitly OK* in your system; that the
components of the record can be freely
combined in any way (modulo enforced
cross-component invariants). And there are
systems in which this is fine! But you're
imagining (correctly) that this modeling
technique will be used in systems in which
this is not fine. <br>
</font><font size="4" face="monospace"><br>
The main challenge here is that developers
will be so attracted to the syntactic
concision that they will willfully ignore the
semantic inconsistencies they are creating. <br>
<br>
<br>
</font><br>
<br>
<div>On 1/25/2026 1:37 PM, Andy Gegg wrote:<br>
</div>
<blockquote type="cite"> <font face="Times New Roman">Hello,<br>
I apologise for coming late to the party
here - Records have been of limited use to
me but Mr Goetz's email on carrier classes
is something that would be very useful so
I've been thinking about the consequences.<br>
<br>
Since carrier classes and records are for
data, in a database application somewhere or
other you're going to get database ids in
records:<br>
record MyRec(int dbId, String name,...)<br>
<br>
While everything is immutable this is fine
but JEP 468 opens up the possibility of
mutation:<br>
<br>
</font><font face="Times New Roman">MyRec rec</font><font face="Times New Roman"> = readDatabase(...);<br>
rec = rec with {name="...";};<br>
writeDatabase(rec);<br>
<br>
which is absolutely fine and what an
application wants to do. But:<br>
</font><font face="Times New Roman">MyRec </font><font face="Times New Roman">rec =
readDatabase(...);<br>
rec = rec with {dbId++;};<br>
writeDatabase(rec);<br>
<br>
is disastrous. There's no way the canonical
constructor invoked from 'with' can detect
stupidity nor can whatever the database
access layer does.<br>
<br>
In the old days, the lack of a 'setter'
would usually prevent stupid code - the
above could be achieved, obviously, but the
code is devious enough to make people stop
and think (one hopes).<br>
<br>
Here there is nothing to say "do not update
this!!!" except code comments, JavaDoc and
naming conventions.<br>
<br>
It's not always obvious which fields may or
may not be changed in the application.<br>
<br>
record </font><font face="Times New Roman">MyRec(int
dbId, int fatherId,...)<br>
probably doesn't want<br>
rec = rec with { fatherId = ... }<br>
<br>
but a HR application will need to be able to
do:<br>
<br>
record </font><font face="Times New Roman">MyRec(int
dbId, int departmentId, ...);<br>
...<br>
rec = rec with { </font><font face="Times New Roman">departmentId =
newDept; };<br>
<br>
Clearly, people can always write stupid code
(guilty...) and the current state of play
obviously allows the possibility (rec = new
MyRec(rec.dbId++, ...);) which is enough to
stop people using records here but carrier
classes will be very tempting and that
brings derived creation back to the fore.<br>
<br>
It's not just database ids which might need
restricting from update, e.g. timestamps
(which are better done in the database
layer) and no doubt different applications
will have their own business case
restrictions.<br>
<br>
Thank you for your time,<br>
Andy Gegg<br>
</font><br>
</blockquote>
<br>
</div>
</blockquote>
</div>
</blockquote>
<br>
</div>
</blockquote>
</div>
</div>
</div>
</blockquote>
<br>
</body>
</html>