Post Featured Image

Learn About Our Best Practices for SuiteCommerce Extensions

This blog is a follow-up post to Coding for Commerce webinar we gave on the best practices for working with commerce extensions. It is appropriate for all SuiteCommerce sites, and SuiteCommerce Advanced sites running Aconcagua or newer.

In our breakout session at SuiteWorld, Joaquín Gatica laid out some best practices for working with the extension framework. He talked about dependency management, versioning, handling of templates and Sass, upgrading and migrating, and more. But there are things that are specific to the extensibility API itself that we haven't covered extensively but I've mentioned at various stages throughout my blog posts. To help you when you write your extensions, I want to collate this information and help you understand what we think you should be doing.

Approaching Customizations

In preparation for this blog post and webinar, I went and talked to a number of people who have been using the framework (including our professional services teams and partners) and the people who designed and built the framework. For a number of these sections I'm getting to set them out like questions: many of the questions have been ones that I have either been asked or I have asked.

But, before we dive in, I want to have a quick look at what has necessitated this discussion and provide some simple principles that you can use to guide you through this.

SuiteCommerce is NetSuite's commerce offering that is truly software-as-a-service (SaaS). The principle idea is that a site can be customized but still migrated to a new version automatically, whenever an update becomes available. For SCA this was problematic because of the nature of the customizations, how they were written, and how they were integrated into the code. The extension framework aims to separate customizations from the source code with a code layer — the extensibility API — and a framework for activating customizations so that they are automatically combined, as much as is necessary, into your site's final code.

We can summarize the similarities and differences between SuiteCommerce and SuiteCommerce Advanced like this:

SuiteCommerceSuiteCommerce Advanced
ExtensionsAconcagua or newer
Extensibility APIAconcagua or newer
Customization TypesExtensions and themesExtensions, themes and full source
Migration to Newer VersionAutomatic, always up-to-dateManual, when you decide to

There has been some confusion about whether the extension framework — the API, extensions and extension manager — are available for SCA and the answer is yes. If you're working on a modern version of SCA then the customizations available to SuiteCommerce are also available to you; the principle difference is that you are also given full access to the site's source code, so you can make more complex customizations if you wish. The extensibility API is available throughout the SCA source code (ie not just extensions).

While SCA sites might not be full SaaS by design, you can certainly streamline it as much as possible by following the same best practices that we provide SuiteCommerce developers. In short, they can be summed up as:

  1. Upgradeability
  2. Reusability
  3. Maintainability

Firstly, upgradeability means that a site or customization is easy to keep current. We don't want sites locked to a particular version of SuiteCommerce Advanced because of a customization that has gone awry. Modifying a site's source SCA directory will by definition make the migration process manual, as you will need to move across any modifications to the new version, but you can still put in work early on so that it's as painless as possible later.

Next, reusability. There's two ways to look at this: reuse the components that we provide (rather than writing your own) and write your customizations in such a way that they can be used on multiple sites. The latter is perhaps more important for developers who work at agencies who customize a lot of sites, or those who work at a business with multiple brands. In either case, you need to think about what exactly exists in the code and how you can plug it in to other sites.

Finally, we have the concept of maintability. This is all about increasing your productivity and reducing the time spent doing non-technical things. As an example, we are rapidly developing the site management tools and enriching them with new features. I strongly recommend that you take a look at them when building a site to see what you could use to lighten your load. As for your code, we're also thinking about how much work you need to put into your customizations to keep them running and interoperable (ie playing nicely with other customizations on your site).

So, why have these come up and now, and how do they factor into SuiteCommerce and SuiteCommerce Advanced? Well, we think that the best practices we used to prescribe are no longer appropriate. If you were to customize your SuiteCommerce Advanced site the same way you used to, you'd run into the same problems all over again. Instead, we need to redefine the best approaches to customization, keeping in mind SuiteCommerce and then we have new tools at our disposal — the API, extensions and the extension manager.

Traditional SCA Customizations in Extensions

To provide context, let's return to how we compare the two ways of making customizations to a SuiteCommerce (or SuiteCommerce Advanced) site: as a customization to the core code ('the old way') and as extensions ('the new way').

Before the extension framework, you created a folder in your SuiteCommerce source directory, wrote a bunch of stuff, updated your distro file and pushed it up to NetSuite. The compilation happened on your local computer and once it was finished, it was pushed up to NetSuite and immediately made live (you may have to clear the cache but it was there). With the extension framework, you work locally and push it up to NetSuite as before, but there is a round of what we call activation, which triggers recompilation and concatenation; when that finishes, then your code is live.

In this context, how you write your code didn't really need to change if this was just your site. However, there are two dimensions that we need to consider when creating SuiteCommerce customizations:

  1. How well will this age when the base SuiteCommerce Advanced source code is migrated to a new release?
  2. How well will this interoperate when it is added to other sites?

To put these another way: you need to keep in mind the changes that we, NetSuite, may make over SuiteCommerce's life cycle as well as the changes a customer may make to their SuiteCommerce site of it's life.

The reason why we consider the extensibility API to be your best bet is because it wraps so many customization avenues in a layer that is generally indifferent to version or site.

Version-Agnosticism

To give examples, consider the refactoring and renaming of the SuiteCommerce source code in Elbrus that changed how we handle product data, specifically the product detail page. If, before that time, you made customizations that affect the ItemDetails module and its files, you would have had to completely rewrite them because the class names you dependended on no longer existed. We split up the functionality into other modules, such as Item and ProductDetails. I imagine there is at least one person reading this now who got a headache over this.

However, this is something that we, NetSuite, recognize and forms the basis of the reasoning behind the extensibility API in the first place. As developers — consumers of our code — you shouldn't have to worry about when we change the internals of our code. Instead, you should be free to make customizations with a high degree of certainty that when it comes to migrating to a new release, it doesn't matter what we name our internal files and methods: all that matters are the names of the components and methods, and the things they output.

Site-Agnosticism

The second risk is more applicable to our partners, or developers who plan to develop customizations for use on multiple sites: site-dependence.

When you make a modification to fit the requirements of a specific site, you might fork so initimately away from the base code that renders it inapplicable to other sites. What I'm thinking about here are customizations that extend base SuiteCommerce classes, modifying prototypes, file overrides, adding configuration to the code (rather than the configuration record), modifying helpers, private functions, etc.

This behavior is risky not only because of the risks associated with NetSuite changing these files every release, but because there may be other site-specific customizations that exist on other sites. To give an easy example, consider a customization for which there is currently no easy extension-based solution: customizing the loading icon.

If you've read my blog post on this already, then you'll be aware that in order to modify this behavior, you need to edit (override) jQueryExtras > JavaScript > jQuery.ajaxSetup.js, as well as provide any additional assets. For a site-specific customization, this is relatively straightforward, but packaging up this customization into something that is distributable and applicable to many sites is problematic.

jQuery.ajaxSetup() is an important file. It defines the settings that will apply to all AJAX calls made using jQuery (including jQuery.ajax() and jQuery.get()). jQuery themselves recommend against using it, stating:

This can cause undesirable behavior since other callers (for example, plugins) may be expecting the normal default settings. For that reason we strongly recommend against using this API. Instead, set the options explicitly in the call or define a simple plugin to do so.

Indeed, this is also good advice in general. But specific to us, some sites may have their own customizations within this file and you creating an extension that overrides it will remove their customizations.

To offer a second, more abstract example, consider the traditional methods for customization: extending, wrapping, modifying prototypes, etc, adding your code to the source code using these methods is, again, not only problematic across versions but problematic if you consider the possible customizations that specific sites might have.

Another example could be custom NetSuite ERP features: custom fields, records, list, etc. At the time of writing, there's no way to package up any of these things into an extension so that they are created automatically when a extension is deployed to a site for the first time. This is something we are looking at, but in the mean time it remains problematic for developers who seek to distribute their extensions to other sites.

Can I Use vs Should I Use

Since the release of the extensibility API, a question I was frequently asked was, "Can I use [x]?" To which my answer is, "yes, but should you?".

The difficulty for you as developers is reconciling the fact that you know how to perform custom customizations using the base SC code and our recommendation that you don't. To put it bluntly: avoid using core SC classes and objects as much as possible. If you can't avoid it, then you need to be able to assess the risk using the two factors I mentioned above.

An important thing to keep in mind is that with every release, we are adding in new components and methods, which will diminish reliance on base SC classes.

Consider for example:

  • The layout component — available globally, you can use this for the timebeing to add, remove or replace the views throughout the site, particularly areas such as the homepage or customer account section, which don't currently have components
  • The environment component — also available globally, you can use this to access values from the site configuration and settings records, which means you no longer have to use SC.Configuration, for example

These are good things. It's part of our philosophy which is where we move towards extensions having complete independence from core SC code. Eventually, your code should hook into our code purely through API calls. Indeed, if you're a pure SuiteCommerce developer — with no access to the source code — you are not going to know any base SC classes.

But What About Backbone, Handlebars Extras, Global Views, Etc?

Once you've hooked your customization into NetSuite, and you want to build it out, you are free to use Backbone classes — routers, models, views and the like — how you wish to achieve your customizations. Using Backbone is considered particularly low-risk because of how it forms, well, the backbone of our applications.

When it comes to our custom 'utility' code such as HandlebarsExtras, Utilities, GlobalViews, BackboneExtras, etc, you should be relatively fine — there may be some risks involved with these as we make changes across versions, but they are fundamentally stable.

Keep in mind that a lot of custom utility functions are have been added to the Underscore namespace (_). So, for example, if you need to use the formatCurrency() method, you can use _.formatCurrency() instead. It is also where we make available a few extra bits of environment information, such as whether you're currently in the shopping domain. Explore the API docs for more information.

Some things, like Backbone.Validation, might be a little tricky because some sites implement custom validation rules to standard fields. Again, howevever, I would say that it is safe to assume that it would be rare for any site-specific customizations to interfere with an extension's validation (ie, I think it's unlikely to see extensions that modify the validation rules of standard fields).

Also note that there may be some nuances to developing on SuiteCommerce compared to SuiteCommerce Advanced. One of the things you can change in SCA is the distro file: you can control which (source) modules are included in your applications. There was an interesting thing that came up recently where someone was trying to use the country and states global view dropdowns. This was a good idea: re-use what we give you. However, there was an issue because the source code does not use these global views in the shopping application — only on the account and checkout applications. Therefore, they were not available for use in the shopping application as they weren't compiled into the source. If this was an SCA site, they could have just modified the distro to include them in the compile, but, as of me writing this, there are not currently available in the shopping application.

Examples of Extensibility API Best Practice

All of this is theoretical, so let's look at some examples.

I think no matter what we do to stabilize the customization process, there is always a very small risk that customizations may not work as intended when transferred to a different site, or the current site is migrated to a newer version. However, the extensibility API should be the safest bet. As much of your customizations should be done with the API as possible.

Getting Configuration Record Values

We have a global variable, SC, which can be accessed anywhere; generally speaking, we don't encourage pulling stuff out of it. For configuration values, we would normally recommend adding SC.Configuration as a dependency (named, for example, as Configuration) and then just use that. However, now we have the environment component, so just use that and then use its getConfig() method.

// ❌ Don't do this
var isMultiShipEnabled = SC.CONFIGURATION.isMultiShippingEnabled;

// ❌ Don't do this either
var isMultiShipEnabled = Configuration.isMultiShippingEnabled;

// ✅ Do this instead
var Environment = container.getComponent('Environment');
var isMultiShipEnabled = Environment.getConfig('isMultiShipEnabled');

Adding a New Child View

If you've written some new functionality that plugs into existing functionality, then you'll likely need to add a new child view. Ever since we made every view a composite view in the Elbrus release, adding a new view meant adding the view you want to add a child to as a dependency, and then just modifying the childViews object of its prototype. Prior to that, there was a lengthy process (an example available here) which involved converting simple views into composite views but, now, with the extensibility API, you can use a component specific for an area of a site, or use the layout component in general.

// ❌ Don't do this
CartSummaryView.prototype.childViews.MySuperCoolView = function ()
{
  return new MySuperCoolView
  ({
    model: this.model
  })
}

// ✅ Do this instead
var Cart = container.getComponent('Cart');
Cart.addChildView('Cart.Summary', function ()
{
  return new MySuperCoolView
  ({
    model: this.model
  })
});

Another way of making child views available is through something we added in 2018.2: registerView(). Unlike addChildView, which requires you to specify at the time of construction where you want it to go, this method lets you create a new child view that can be included anywhere on a site by simply adding markup to templates.

// ✅ You could also do this
var Layout = container.getComponent('Layout');
Layout.registerView('MySuperCoolView'), function ()
{
  return new MySuperCoolView
});

This method is perfect for situations where your extension adds new functionality but it is up to the implementor where it should be rendered. It's also good when responsibilities between theme development and code development are divided: ie, new views can be added without updating the JavaScript core code / extension after the initial implementation.

Adding a New Value to a Context Object

The Underscore method wrap() comes up in a lot of example customizations because it enables us to run a function and modify its output before its returned. In other words, if we have a finished context object, but want to add an additional property to it, then we can put it in a wrapper function that runs it, and then adds in our property.

With the extensibility API, we can access the addToViewContextDefinition() method which does the job for us. We specify the view whose context we want to modify, the property name (new or existing), its type, and then the function that determines its value. Handily for us, the existing context object is passed along with it, so we can query it, should we need a value out of it.

// For example, I want to add a property that returns the number of characters in the name of an item
// ❌ Don't do this
ProductDetailsFullView.prototype.getContext = _.wrap(ProductDetailsFullView.prototype.getContext, function(fn)
{
  var context = fn.apply(this, _.toArray(arguments).slice(1));
  context.nameLength = this.model.get('item').get('displayname').length;
  return context;
});

// ✅ Do this instead
var PDP = container.getComponent('PDP');
PDP.addToViewContextDefinition(PDP.PDP_FULL_VIEW, 'nameLength', 'number', function nameLength (context)
{
  return context.model.item.displayname.length
});

Modifying Backend Behavior

At the time of writing we have one component to our backend extensibility API: the cart. Thus, if you wish to modify the service (controller) of any other part of the backend, you will probably have to use traditional methods of customization. However, if you want to modify the behavior of the cart, you should use the backend cart component.

The cart component has a number of methods, as well as a myriad of events that you can listen for. The backend component is designed to have the same syntax and behavior of the frontend one, and although it operates synchronously behind the scenes, you can still write code that is asynchronous (ie, it won't operate asynchronously but it means the code you use on the frontend can be easily ported to the backend).

So, let's take another example, for which there are (at least) two ways of doing it. Let's say that when a shopper updates a line item in the cart (eg adjusts its quantity) we want to trigger a check so that we can test if its purple.

// ❌ Don't do this
_.extend(LiveOrderLineServiceController,
{
  put: _.wrap(LiveOrderLineServiceController.put, function (fn)
  {
    var options = this.data.options;

    _.each(options, function (option)
    {
      if (option.value && option.value.label == 'purple')
      {
        console.log('I like purple!')
      }
    });

    return fn.apply(this, _.toArray(arguments).slice(1));
  })
});

// ❌ Don't do something like this either
Application.on('after:LiveOrder.Line.ServiceController.put', function ()
{
  // ...
  // (Honestly, I can't think of a cleanish way of doing my example using this method)
});

// ✅ Do this instead
var Cart = Application.getComponent('Cart');
Cart.on('afterUpdateLine', function checkForPurple (data)
{
  var options = data.line.options;

  _.each(options, function (option)
  {
    if (option.value && option.value.label == 'purple')
    {
      console.log('I like purple!')
    }
  });
})

In other words, the backend cart component works like a wrapper for the LiveOrder model. So, I can recommend the above approach because it is possible to achieve what I want to achieve without touching the base class by virtue of the cart component.

While researching this post, I came across an old post where we looked at how to add reCAPTCHA to newsletter signup.

There are a few functionality changes we make to the core code:

  1. We extend a view
  2. We extend a service controller to wrap a method of it
  3. We override a template

Interacting with views and templates are things that we could handle with the extensibility API, but it's the question of our interaction with the service controller that's perhaps the most controversial. In this scenario I recognize that we really don't have an alternative for it.

It's unlikely that we will create components for every backend service on a SuiteCommerce site, and it's unlikely that we will create a generic backend component that can apply to all backend services (like the layout component does for the frontend). You will need to use traditional methods for these.

Can I Put Non-API Customizations into Extensions?

Let's say that you've decided on the customization you want to make. However, you can't figure out a way to write it in such a way so that it relies entirely on the API. Are you allowed to add these to your extensions?

Yes. We strongly recommend you use extensions as much as possible. We would recommend and prefer that you only connect to the core code through the extensibility API but if you have to make a customization that modifies/extends the source code, then it's still a good idea to put them into extensions.

Keeping no code in the SCA source directory for your site will ensure that when it comes time to migrate to a new release, no manual work to the source code needs to be done to get your site moved over. Consider this scenario:

  1. All site customizations are in extensions
  2. I upgrade my site's bundle to a new version of SCA
  3. I activate my theme and extensions

This clean process will ensure that even a non-developer can migrate a site to a new version. Compare with this scenario:

  1. Some site customizations are in the SCA source directory
  2. I upgrade my site's bundle to a new version
  3. I must wait for my developer to copy the customizations over, modify the distribution files, and deploy, before I am able to take advantage of all of my site's customizations

Yes, if some customizations are not compatible with the new version, that is not going to be fixed by having them in extensions. However, it does mean that if one malfunctions and is not ready, it can be just deactivated and the site go on (assuming, of course, it's non-essential functionality).

Anyway, I digress: keep in mind that using source classes and methods is risky. Remember, we're here to ensure sites can be upgraded and maintained easily. Extensions and the extensibility API are there to minimize risk by only using code that is reliably stable. You need to make the decision about whether it is worth it.

How Risky is 'Risky'?

Having given this advice before, I've had some questions about specific areas of functionality. As a team, it's hard for us to really analyze and propose definitive answers about each individual class, method and property in the source code. The new vs old examples above are pretty explicit, but what about more nuanced areas of the platform, those untouched by the API? Here's an illustration I made; note again: this is not definitive or there to give you a yes/no answer. Use it to help inform your decision-making process:

The two axes are trans-version risks (changes to the source code made by NetSuite in each release) and trans-site risks (changes made to a specific site).

So, for example, if you've written some custom functionality that depends on a custom field that you don't control, or is otherwise specific to the site it was written for, it's almost certainly not going to work on another site (unless you go through the steps to recreate them and their values) — but is this going to break when you upgrade the site? Probably not.

Let's take a look at Bootstrap, the third-party library we include in all of our sites to solve some of our presentation needs. Well, we might upgrade that library in a future release, and when we do, we might reference some part of that doesn't exist in older versions. So, if you try to use an extension that contains references to it in older sites, they're not going to work.

Those of you with long memories or experience migrating sites will have to cast yourselves back to that time and think about the things that caused you pain when you tried to reintegrate customizations. Some of the changes that SuiteCommerce code updates can bring include:

  • Class name changes
  • Method name changes
  • Source code refactoring

I mentioned this earlier with references to the changes we made to the ProductDetails module. Anyway, this is why we say you should use the API: if you call the names or methods directly then you may end up calling something that could change either its name, method name, or method behavior. API names, methods, and behavior should remain stable across releases; as for the contents of the source code, there's no guarantee.

If you've ever done a site migration from one version of SCA to another, then remember that the extensibility API and extension framework are there to alleviate those headaches. Thus, in this context, risk could be defined as having to do extra work when you migrate your site the next time.

If you've ever written code that is to be used on multiple sites, then addressing the idiosyncracies of those sites (plus the risk of differing versions) come into play. Thus, risk in this context might be thought of the extra work you have to put in to getting it to work in those different scenarios.

Ultimately, you can look at this way: you could avoid using the extensibility API entirely, but you'll probably end up doing more work. It's not strictly mandatory (ie if your code works, it works) but the API is here to aid you; it's here to make your lives easier and ensure that your code continues to work when it's transported from site to site, and those sites get updated.

Remember when we talked about site-specificity and version-specificity? Well, if you're working on your own site and you're happy introducing a bit of risk, then that's your call. If you're working on a customer's site then you should obviously minimize it as much as possible, or let the customer take that the risk from an informed position. If you're writing an extension for the marketplace, where you don't know the site or what version they're using, you should avoid risk as much as possible.

Can I Put API Customizations into My SCA Source Code?

Yes, we recommend it wherever possible.

How Do I Manage Small Customizations?

When it comes to big customizations: blog functionality, size guide, gift certificate value checker, etc, it's easy to package these up into individual extensions. But what about small changes? What's the best way to handle those? How do you package up a small change to the footer, or an addition to the PDP?

Having looked at how people are developing on SuiteCommerce Advanced in the wild, there seems to be two approaches:

  1. Follow traditional customization processes and put them all into a customizations folder in your SCA directory
  2. Create one big, generic, site-specific extension and put all of them into there?

And the answer is that we think you should use one big extension. Think about what we just looked at: how it's best to keep the source directory clean. In short, I think in most cases it would be much more preferable to put as many customizations as you can into extensions. The fact is, putting code into extensions keep your source code clean. Remember, one of the key benefits of the framework is that we make it easy to upgrade SuiteCommerce Advanced sites.

When it comes to upgrade time, the process for migrating a site to a newer version should be much easier if the base source code is untouched.

Are API Methods Backwards Compatible?

No. Just like the example above about Bootstrap, if you reference a method used in a newer version of the API than the site has access to, it won't work.

This might be problematic, but keep in mind that if a site follows best practices, there is no reason why they cannot keep their SuiteCommerce Advanced version up to date.

Are API Methods Forwards Compatible?

Yes. It's unlikely that we will fundamentally change the way a component or method works, so as long as you're not doing something hacky with it, it should continue to work fine when you upgrade.

Can I Extend Extensions?

Technically yes?

I mean, I think when you're considering this, you need to be careful. By doing this you're effectively creating a dependency in an environment where it's possible for a site administrator to disable one using the backend interface. Generally speaking, if these are extensions that you own and operate, it's probably low risk if the site administrators are aware that they are not to be treated independently.

It becomes more risky when you're extending an extension that you don't control. The original author may push an update for their extension that changes the behaviour, removes a class or method, and you're stuck having to either rewrite your extension or removing it entirely.

In this sense, you might think of extending extension classes as like extending base SuiteCommerce classes — we generally don't recommend it, and the risks detailed above (about moving sites or versions) apply.

There might be some exceptions to this. If the extension you're extending is very simple, or provides some sort of library or utility function, then you might be able to get away with it. Again, however, like so much of this stuff, it's a judgment call: how important is it to you that you create this dependency? Can you work around it?

If you are going to extend an extension consider the advice that Joaquín gave at SuiteWorld and in our previous post:

  • If you own the extension, consider creating an API for it (maybe a new component?) within that extension's code
  • If you don't own the extension, consider creating an adapter for it and then have your customization talk to it through that

I'll remind of you a diagram that he used to explain how to integrate your customizations into a site:

In this diagram, the numbered interfaces are:

  1. The extensibility API
  2. Templates and Sass
  3. API
  4. Adapter

The idea is that you want to create stability in areas where the are none. By creating your own API or component, any extension that depends on it may be able to avoid having to be updated when the underlying extension is updated. Similarly, creating an adapter may mean that some initial, extra work will need to be done to maintain the adapter, but the customization extension itself may escape having to be rewritten.

Anyway, interface and adapter design are pretty advanced topics to discuss, so you should definitely research and practice what you're doing before embarking on it.

Can I Use Overrides?

The only overrides we recommend using now are the ones that are available in the theme development tools. What this mechanism lets you do is override an HTML or Sass file originally found in an extension, with one that you're adding to your theme. The example we give in the documentation is a scenario where you want to add a site-specific Sass variable to an extension Sass file. Therefore, the developer chooses to override the file and add their Sass variable to the declarations found in that file. Good idea!

As for doing this in your source code, I really must advise against it. This is precisely the type of thing that will make it very difficult for you to migrate your site to a new release and you will certainly experience difficulties should you override a particular big file. Remember, with templates and Sass files now moved out into standalone themes, changes to templates and Sass files are done there and you shouldn't feel afraid to use them there.

I am aware of some anxiety around making extension-specific changes to your theme files. If it is not possible to replace or add a view for your functionality using the API, then this can be problematic but I would encourage perserverance. If you own both the extension and the site, and you're making a site-specific change to a template or style, then make it in your theme.

Final Thoughts

When it comes to customizing SuiteCommerce Advanced, the rules have changed. And they have been brought in line with the advise we give to SuiteCommerce site developers: upgradeability, reusability and maintainability. We also want to recommend that whatever customizations you make, you evaluate the risk of how it will affect your site when you upgrade and if you were to migrate it to another site.

Keeping a site fresh, using the latest source should be the prize you keep your eye on. However, we're aware that if you're using SuiteCommerce Advanced over SuiteCommerce, there is a good chance that you want to get into the guts of the application — and that's OK — but think about the changes you're making and whether they are things that could be made through extensions or, at least, made in such a way that does not hinder your ability to upgrade. We still encourage you to use the extensibility API and extensions as much as possible. Remember, the API is available throughout the source application, but you will need to have access to the container/application object in order to invoke its components and their methods.