Best practices

In this section we will outline some guidelines and best practices. As with any rules there are times where the best option is to break them but following these guidelines will help avoid some common pitfalls.

Define specific extensions

That is, develop extensions that does one thing and does it well. This is one of the core principles of software design; If the description of a function contains “and”, “or” “but” “if” the function does too much and should be split into several smaller functions to among other things simplify maintenance.
This is very true for extension since end users are using these functions directly. An extension with many complex parameters to accomplish various behaviors is vastly more difficult to understand and use than having several well named and clearly defined functions. Complex parameterized behaviors are also difficult to maintain and extend.
In practical terms, consider a query extension “ReadSystemEntity” that accepts a parameter “EntityName” with the domain [Clients, Accounts, Region]. Depending on the EntityName parameter, one of the three entities is queried. It is sometimes tempting to combine different functions like these because they technically require much the same boiler plate code, e.g. loading API libraries and authenticating etc.
It is much better to create three different extensions ReadClients, ReadAccounts and ReadRegion and put them in a common folder. It is easier to understand and use from the user perspective and if you create suitable abstractions (such as extracting common code into classes that can be reused) it is much easier to maintain in the long run.

Avoid dynamic metadata wherever possible

By dynamic metadata we mean defining output that is not fully hard-coded but depend on some dynamic factor like what arguments the user supplies.
Although it may seem like a viable option in some cases, dynamic metadata greatly limits the usage of query data and makes building reports and interfaces that much harder for the user. A column that may or may not be there runtime can for example never be used for filtering, grouping, joining calculations etc.

Execution node may change

Extensions can be installed to run either client or server side or both. This means you cannot make any assumptions on where your extension will execute, or even that it will execute from the same location each time.

  • Don’t store any state in local files.
    
  • Don’t try to communicate with the user using popups.
    

Portability

The path and name of the extension header file (.pyplugin/.netplugin), relative to the extension root directory, is the unique identity of the extension.
Any collateral based on extensions depends on the extension ID. E.g. when the user configures a report using a query extension, the ID of the extension is stored so the same extension is loaded when the report executes.

  • Consider carefully what the directory structure should look like to be efficient to use. Changing the directory structure will break any existing collateral (e.g. reports based on a query extension).
  • Avoid including minor version numbers in the directory structure and file name. A minor change causing a minor version number update breaks all related collateral.
  • Consider including major version number as a part of the folder structure.
    • Update the major version number only when the changes will naturally break collateral anyway.
    • This allows installing the new version in parallel with the old one and perform the migration with both extensions available.
  • Use the default python environment if possible. Depending on a secondary environment limits portability and reusabilty.