Tales of an ISV: fear the auto-number field

I recently made a post on twitter post on twitter about how terrible of an idea using the auto-number field type for the “name” field on custom objects is for ISVs, in particular that it prevents you from ever importing legacy numbers into this field, or letting customers change the format. EVER. From the responses I got it </a>seems to be accepted, but not well known.

I had a conversation with another ISV a few days later who asked about why it’s a practice to avoid, and figured it was blog-worthy for any new ISVs who haven’t been stuck in this trap yet.

First and foremost, apex tests consume auto-number values and never give back the values they used. This is no longer true by default, and is controlled by an organization preference that allows enabling the legacy behavior if you really want to for some reason.1

This alone takes the wind out of their sails, they’re not really sequential as every time the customer upgrades your package there’s going to be a nice big gap in them. If you plan to use them in any capacity where it’s required to have sequential numbers you can’t use auto-number fields.

The next hit they take is that the usual tricks for importing auto-number fields aren’t available on managed fields! As a package publisher even the only thing I can change is the label and format:

and as a customer you don’t even get an edit button! That’s right, customer’s can’t even change the display format on these fields, let alone the type in order to do an import.

And lastly, the killer for most people, they aren’t guaranteed unique. From the SObject clone method’s documentation:

The optional opt_preserve_autonumber argument determines whether auto number fields of the original object are preserved or cleared in the duplicate. If set to true, auto number fields are copied to the cloned object. The default is false, that is, auto number fields are cleared.

That means anybody with apex access can duplicate autonumber fields on an sObject (in memory, it gets assigned a new one when an insert is actually performed). Ouch.2

But if your packaged custom object wants to keep the same numbering as a object in your legacy system? Well too bad, not possible.

The best solution I’ve found so far is to make it a text field, and have a custom setting store a “last issued” number value. Have an apex trigger that 1) erases any user-provided input, and 2) issues it the next number in sequence on insert, and another trigger preventing the field from being modified post-insertion. Make sure you have a boolean you can check somewhere to allow legacy imports.

However this does kill the usability of native salesforce record creation pages as the name field is marked as required on the UI (but not through the API).

Now that is an example of some terrible hackery and pain that only ISVs are subjected to.

  1. Spring ‘14 changes to Apex test autonumber handling 

  2. In hindsight, this is likely not a meaningful issue, givne the ability do do the same thing via JSON.deserialize. In-memory SObject instances that try and enforce read-only fields should be taken as a convenience and not a “security” feature.