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