<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">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"><u></u>

  
  <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">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>