The general principle here is idempotency of pre-processing actions: anything that runs whenever a user opens a view should be side-effect free. The “easy button” is to just default in favor of not using pre-processing data transforms.
To elaborate…
This is a common principle in React front-ends, and it’s mostly obvious and intuitive: if I open up an assignment, I’m seeing a form that invites me to provide some input and click “submit” to take some action. It would be confusing if the mere act of opening the assignment caused the case to change — that’s why we wait for the click of the submit button to persist changes. I can open the assignment once, twice, or a hundred times and it will be the same every time until I submit it.
The mental model I use for what the submit button does is:
- The pre-processing data transform runs on the case.
- The form submission data is applied to the case.
- The post-processing data transform runs on the case.
When I open an assignment, I think of that screen as a preview of what the case will look like after step 1 has run. But step 1 will only run “for real” after I click submit.
Where things get less intuitive is in scenarios like the one in the OP. Generating an ID doesn’t really feel like a side-effect (maybe technically it isn’t?). Hence the awkward term “idempotence” exists to define this very specific concept:
Idempotence is the property of certain operations in mathematics and computer science whereby they can be applied multiple times without changing the result beyond the initial application.
This all supports an intuitive experience for end users: “nothing is permanent until I click submit.” But builders have to think carefully. For instance, ID generation is not theoretically required to be non-idempotent, and the routine could be modified to produce deterministic output given case-invariant input…
…or we can just default in favor of not using pre-processing data transforms. As I hinted above: this is my preferred solution. Speaking from personal experience, I mostly used pre-processing actions to do “utility data stuff” that didn’t really express business intent, and so I didn’t want to clutter the workflow diagram. But Live Data has pretty much categorically eliminated my need for that sort of thing. This ID generation scenario feels like a gray area to me, I could go either way on serving it through a Data Page vs. having an explicit utility workflow step to invoke it.
EDIT: I should clarify that “serving it through a Data Page” would require making the ID generation routine idempotent, which is the salient detail. I’d be more inclined to ensure idempotency if I was going to re-use the routine a lot, since the increased complexity would be offset by the gains in usage flexibility. But for a one-off I would stick to the utility shape.