Some lessons learned delivering an enterprise application on

The app this is based on uses dozens of related custom objects. And pretty much always customers want to add further custom fields and customization logic. The app is also constantly having new features added. These characteristics obviously influence the lessons learned…

  1. Managed released packages work well and so do patch releases. At this point we have created hundreds of versions as we develop incrementally. (Managed beta packages are fairly useless as they don’t support upgrading.) The version compatibility constraints leave junk in the data model and don’t protect against all breaking changes. Customers expect it to be able to back out an upgrade but unfortunately that isn’t supported. There is a major pain point in that the upgrade process does not propagate new versions of things like layouts.
  2. Customer-specific extensions can be delivered using a further managed released package that builds on the core app one or via the Ant deploy task. Given that the former approach does not propagate many common changes on update, the latter approach is usually the best one to use.
  3. Version management just about works (for small teams). The managed packaged org is the definitive version: your Git or SVN copy is your best attempt to track it and doesn’t include information such as the exact set of components in the package. (We use the convention that everything in the org should be in the package.)
  4. The Eclipse based IDE is poor (as it has had only basic maintenance investment over the last few years) but necessary. It allows the code and other artifacts to be seen and searched as a whole and is the best place to see change and version information.
  5. Unit tests are the safety net that let your managed package change and grow. Use continuous integration server to make sure they are run frequently (and to make sure what you have in Git or SVN is complete).
  6. Governor limit violations mostly but not always result from a design error such as non-linear algorithms (hitting the code statements limit) or non-bulkified queries (hitting the number of SOQL queries limit). But they do come up in other situations. For example, your managed package controller starts some processing that consumes some of its governor limits directly and in its triggers; a trigger added by your customer also runs and updates some of your objects consuming from its own set of governor limits; this then causes triggers back in the managed package to consume more of the managed package governor limits exceeding a limit. Whatever the cause, hitting these limits is a brutal problem: where on other platforms performance might become poor, on your app is broken and your customer is left unable to work.
  7. There are two eras in how open for extension a app is. The boundary between these eras is the availability in Summer ’12 of Type.forName and Type.newInstance. These allows extension points to be built into an app and extension code to be added by supplying the names of classes that implement the required interfaces. JSON strings in static resources or directly in custom settings are also a help. Before this it was very hard to add external code.
  8. Embrace the use of Contact and Account. As well as helping your app share this information with other apps, some API calls require the Id value passed to be a Contact Id.
  9. Dynamic SOQL is inelegant (as you lose compile time checks) but necessary in some cases. An example is where you want to do a deep clone of part of your object graph: use describe calls to get all the field names for each object so that custom fields that your managed package does not know about – ones added for specific customers – are included.
  10. While the execution order of before triggers compared to after triggers is defined and important to know and make use of, the execution order of separate triggers for the same event e.g. “before update” on a Contact is not defined. Within one code base discipline and patterns must be applied to define the order. But this problem is hard to address across multiple code bases e.g. your app and extending code triggers.
  11. While it is tempting to write an Apex classes that deals with a single object on the grounds that the code will be cleaner and “it’ll never be used in a bulk situation”, sometime in the future it probably will be used in a bulk situation and result on a governor limit error that stops some important data migration or change from being possible. Always deal in sets of objects.
  12. Client-side JavaScript using e.g. jQuery is a must-have these days. It is easy to introduce into Visualforce pages but has to be hacked into standard layout based UI – see TehNrd’s Show and Hide Buttons on Page Layouts. See Using JavaScript with for a good overview of other JavaScript techniques.
  13. Sometimes you have the difficult choice between delivering poor usability or instead depending on platform implementation that may change e.g. hack to find field ids.
  14. Profiles are hugely painful to keep updated over time and are rarely tested well enough. Do everything you can to keep the number of them in use small.
  15. On custom settings, and particularly if you are using profile or user-specific hierarchical ones, you will eventually need to use code to keep them updated consistently.