Mixing some uniqueness into production programming is not at all unusual.
Most production programming software has method for “serialization”
Often it’s useful to combine it with a degree of automated functional testing, too.
A python script is a great way to tie it all together. And probably a pogo-pin fixture with lever action that can quickly clamp each board - or even automated probing before the boards have been individually cut from the assembly panel.
Ideally the unique electronic information gets tied into some visual identifier on the board. Since print-on-demand solutions tend not to be very permanent, I’ve often felt it made sense to go the other way - stick on permanently pre-printed QR code labels, scan them, and make the electronic identify stored reflect that. Generate a random key, and store the combination of identity, key, and test result in a database.
Depending on the environment where it happens, you could also have a gateway setup and do some over-the-air testing.
In terms of specific storage mechanism, if you aren’t using a dedicated memory you may need to be careful about what parts of memory you do write to. Most flashing tools can be told to erase or write to less than everything. If you understand the technology of the flash memory you can typically even leave something at an unprogrammed state and come back and overwrite it later, but for ECC flash you may have to do that at the granularity of fairly large words. Sometimes flashing tools cannot get at on-chip EEPROM very easily, but you can write a stub to run from RAM which does. Keep in mind that a fully spec-compliant LoRaWAN node basically has to have non-volatile memory used in operation to track either the frame count or the join session and previously utilized join nonces. Sometimes an off-chip memory is used; in that case you can have the simplicity of having the on-chip flash image identical for all examples, and only have uniqueness in the initial block of the off-chip memory.