<div style="font-family: Arial, sans-serif; font-size: 14px;"><br></div>
<div style="font-family: Arial, sans-serif; font-size: 14px;" class="protonmail_signature_block protonmail_signature_block-empty">
    <div class="protonmail_signature_block-user protonmail_signature_block-empty">
        
            </div>
    
            <div class="protonmail_signature_block-proton protonmail_signature_block-empty">
        
            </div>
</div>
<div style="font-family: Arial, sans-serif; font-size: 14px;">Yes, agreed. What I was hoping to illustrate was that the id (or a class fixing the id) has some precise semantics, relational semantics with and without provenance and that various APIs that already exist expose those in different ways that already leak those semantics. Dealing with those semantics directly isn't necessarily more dangerous than trying to obscure it: the sql in my example being more straightforward than the usual JPA while still safe. <div><br></div><div>Either you store provenance in an id class which only comes from the orm or in an unmodifiable field of a non-data class (which now can only come from the orm) or you let the api user manage provenance. I wouldn't want a record to represent provenace (so the id or the whole object in the second case) because I don't want to expose its constructor and I wouldn't use a carrier for the same reason. JEP 468 doesn't alter this story as far as I can see because if you want true provenance you need to control that behind a private/package protected constructor or some RAII: it isn't data alone, it is history. I also wouldn't really ever want a File or a Socket to be a record or carrier, but 2 out of 3 of these database api approaches lend themself naturally and maybe safely to records and carriers: the first with provenance and the third without. JEP 468 doesn't change this because you already chose the approach when you made the constructirs. </div><br></div><div class="protonmail_quote">
        On Monday, January 26th, 2026 at 4:55 PM, Brian Goetz <brian.goetz@oracle.com> wrote:<br>
        <blockquote class="protonmail_quote" type="cite">
            
    <font size="4" face="monospace">I think the entity-modeling story
      here is more like:<br>
      <br>
       - a regular class that associates (id, PersonInfo), where the ids
      are dispensed exclusively by the ORM, and<br>
       - a record/carrier for PersonInfo, that lets you "mutate" the
      information but not the ID association.  <br>
      <br>
      <br>
    </font><br>
    <div class="moz-cite-prefix">On 1/26/2026 10:52 AM, Aaryn Tonita
      wrote:<br>
    </div>
    <blockquote type="cite">

      This past sprint we had such a case where unconditional
      deconstruction would have helped with database entities. Basically
      a user had created a patient twice over quite some time span and
      operations and graft allocations were associated with both
      patients but the medical and personal details were most accurate
      on the newest and the user desire was to merge them. Our
      deduplication detection didn't trigger because of incompleteness
      of the old record. However because so many downstream systems
      depend on the oldest record that was the id to keep. 
      <div><br>
      </div>
      <div>In the database you would just alter the id of the old with
        cascade, then relink the foreign key constraints of related
        tables to the new patient, then modify the id back to the old
        value again with cascade and delete the old. Now JPA doesn't
        support this approach of altering a primary key... So instead
        you need to fetch the new and old entity and deconstruct the new
        entity before deleting it and then reconstruct the old entity
        with the new data and same old id... Or you reach for the @Query
        approach instead and after modifying the database you change
        just the id (and linked relations) of the newer patient
        representation object. This latter approach is less brittle to
        future changes. </div>
      <div><br>
      </div>
      <div>But the original point that Brian made stands: the
        constructor always allows a nonsense representation. People
        exploit that in unit tests to create unpersisted entities
        or relations to other entities that don't exist. Without
        fetching the entire database all at once you won't really get
        away from that but I also wouldn't want to.</div>
      <div><br>
      </div>
      <div>We have more and more places where withers would help (and
        sad places where a carrier class would have helped but we used a
        class in place of a record). <br>
        <br>
        <br>
        <br>
        <br>
        Sent from <a target="_blank" href="https://urldefense.com/v3/__https://proton.me/mail/home__;!!ACWV5N9M2RV99hQ!M9c8nZS-3Ga3cPLqwU5QkNrUJs_RoN8etK5ZrHbzmmz29wzkUXoSJmTLdIVhe9GYuWhACJi2C0eIRWF10YI$" rel="noreferrer nofollow noopener">Proton Mail</a> for
        Android. </div>
      <br>
      <div class="protonmail_quote"><br>
        <br>
        -------- Original Message --------<br>
        On Monday, 01/26/26 at 16:13 Ethan McCue <a class="moz-txt-link-rfc2396E" href="mailto:ethan@mccue.dev" rel="noreferrer nofollow noopener"><ethan@mccue.dev></a>
        wrote:<br>
        <blockquote class="protonmail_quote">
          <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" rel="noreferrer nofollow noopener" 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 solid #ccc; 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 nofollow noopener" 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 solid #ccc; 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>
      </div>
    </blockquote>
    <br>
  


        </blockquote><br>
    </div>