---
category: [Administration & Integrations, Platform and Product Extensions, Platform and Product Extensions/Integration, Administration & Integrations/Custom Integrations & Apps, Platform and Product Extensions/Workday Extend]
keyword: [Extend, Presentation Components, widgets, tags, UI components, Best practices, PMD Scripting]
nav: wcp_docs
parent_url: /wcp_docs/
title: >- 
  Concept: PMD Best Practices
layout: subsection
---

## <a id="section_pageDesign"></a>Page Design

-   Keep pages focused.

    Consider splitting a long or widget-heavy page into simple pages that are clean and focused. Workday design principles recommend that you create simple UI that is brief, familiar, and relevant.

-   Design fast pages.

    Another Workday design principle is to create efficient and manageable page-sized experiences. Streamline the display of the widgets on a page by grouping the widgets and controlling their visibility. However, to avoid poor page performance when a user updates container widgets, keep dynamic visibility to a minimum. You can also minimize the page load time by rendering widgets only when needed.

-   Place dependent tags after the tags that they depend on.

    Presentation Components render widgets sequentially, as you coded the tags in the PMD. If the value or an attribute of a tag depends on another tag, place the dependent tag after the referenced tag. If you need to reorder the widgets dynamically within a `section` or `fieldSet`, you can set its `sorted` attribute to true, and set the `sortOrder` attributes of the child tags. The `sortOrder` attribute enables tag dependencies to work when the order doesn't match the order of the tags in the PMD.

    Use the `id` attribute value to reference tags in attribute bindings and widgets in event-handling.

    <b>Note:</b> In general, Workday recommends that you include the `id` attribute in your tag definitions, especially for debugging purposes and widget interactions.

-   To change a widget's visibility, ensure that the widget is in a `fieldSet`.

    A `fieldSet` guarantees consistent behavior with dynamic visibility. If an `onChange` event handler calls the `setVisible` method on a widget or changes the `visible` attribute of a tag inside a `section`, inconsistent behavior might occur.

-   Avoid nested fieldSets and minimize dynamic visibility.

    Although the `fieldSet` widget provides a handy way to group widgets and enable dynamic visibility, don't nest fieldSets within each other and minimize dynamic visibility. If you need to use nested fieldSets, minimize the number of layers and minimize dynamic visibility across the nested layers.

    Nested fieldSets can easily introduce complex layers of dynamic visibility that cause inefficient rendering of the widgets. Nested fieldSets can also cause inconsistent results when referencing widgets across the different fieldSets.

    In this sample widget structure, `fieldSet2` is nested within `fieldSet1`. The nested `fieldSet2` contains `text2`, which is visible based on the root-level `text1`. Each time the user updates `text1`, the PMD must resynchronize and repaint the full set of widgets, which can cause display latency.

    ```
    {
     "type": "fieldSet",
     "id": "fieldSet1",
     "children": [
       {
         "type": "text",
         "id": "text1",
         "visible": "<% someCheckBox.value %>"
       },
       {
         "type": "fieldSet",
         "id": "fieldSet2",
         "children": [
           {
             "type": "text",
             "id": "text2",
             "visible": "<% text1.visible %>"
           }
          ]
       },
       {
         "type": "text",
         "id": "text3",
         "visible": "<% text1.visible %>"
       }
     ]
    }
    ```

    Instead, limit the dynamic visibility of widgets within a single level of fieldSets. In this next sample widget structure, `fieldSet2` is at the same level as `fieldSet1`:

    ```
    {
    "type": "section",
    "id": "section1",
    "children": [
    {
     "type": "fieldSet",
     "id": "fieldSet1",
     "children": [
       {
         "type": "text",
         "id": "text1",
         "visible": "<% someCheckBox.value %>"
       }
      ]
    },
    {
      "type": "fieldSet",
      "id": "fieldSet2",
      "children": [
        {
          "type": "text",
          "id": "text2",
          "visible": "<% text1.visible %>"
        }
       ]
    },
    {
      "type": "fieldSet",
      "id": "fieldSet3",
      "children": [
        {
          "type": "text",
          "id": "text3",
          "visible": "<% text1.visible %>"
        }
       ]
    }
    ]
    }
    ```

-   Ensure that required fields on edit pages have labels.

    If an edit page has a required field (the `required` attribute of the PMD tag is `true`), ensure that the PMD tag has a `label`. The `label` conveys to the user the required information, and is standard for usability and accessibility requirements.



## <a id="section_pageEndpoints"></a>Page Endpoints

-   Ensure valid URLs.

    Check the validity of the `baseUrlType` in all endpoints, as defined in the `dataProviders[]` property of the App Metadata (AMD). Ensure that the `baseUrlType` and the endpoint `url` values form a correct, fully qualified URL. A common, avoidable mistake happens when the base URL value in the AMD or the PMD endpoint contains an extra `/`. You can establish a consistent pattern with the base URLs in the AMD to help ensure valid URLs.



-   Use optimized endpoints.

    To improve the page load performance, ensure that all endpoints are performant and use appropriate filter and paging parameters.

    -   Use the `exclude` attribute of an endpoint for conditional invocation, which avoids unnecessary endpoint execution on page load. Always set the `exclude` attribute with deferred endpoints.

    -   If you declare an endpoint but never reference it in the PMD, consider removing it to avoid negative impact on performance.

    -   Use the `isCollection` attribute with caution. Don't use `isCollection` with extremely large sets of data. If `isCollection` is true, the data collection is created with many iterative requests, which can severely impact performance. Use an endpoint that returns more targeted results.


-   Optimize the number of endpoints.

    Presentation Components impose a 24-second timeout on each endpoint. Individual endpoints exceeding the 24-second limit return a timeout error. In addition to the endpoint limit, Presentation Components also impose a 60-second timeout on a request. A request can include initial page loads, page submissions, and remote validations. (A single request can call multiple endpoints.) All of these operations must complete within 60 seconds; otherwise the page returns a timeout error. This measure protects the Workday tenant from poor performance. To avoid the request timeout, consider limiting the number of endpoints and ensure that you call performant endpoints on each page.

    If you use multiple outbound endpoints, note that if an endpoint fails, any updates from preceding outbound requests won't roll back. Due to potential unsynchronized updates, we recommend that you avoid multiple outbound endpoints in favor of a single one.

-   Ensure that you listed the endpoints in the correct order in the PMD.

    If you have multiple endpoints, Presentation Components invoke all endpoints as ordered in the PMD. To invoke an endpoint only when necessary, set its `exclude` attribute. Example: Set `exclude` to a binding that evaluates whether the user has entered required data.

-   Send grid data as an array in a single outbound endpoint request.

    Instead of submitting a separate outbound endpoint for each grid row, submit all grid rows in a single outbound endpoint request. Set the `isArrayOutBinding` attribute of the `grid` to true. For details, see Submitting Data as an Array in the `grid` documentation.

-   Display endpoint error messages.

    Use the `responseErrorDetail` tag with inbound and outbound endpoints for displaying error messages. If an endpoint returns with an HTTP response code other than 2xx, the error messages display on the page error window. For inbound endpoints, you can route HTTP errors to custom error pages.



## <a id="section_pmdScripting"></a>PMD Scripting

-   Ensure that your scripts return a value.

    Unless you have an explicit `return` statement, the last line of the script is the script return value. If you want to log the return value of the script on the console, ensure that the last line returns the value after the `console` method call. Example:

    ```
    "value": "<% var result = getStaticText();
    console.info(result);
    result;
    %>"
    ```

-   Use instance methods for more intuitive scripts.

    Most PMD Scripting namespace functions provide equivalent instance methods, which give the same result. If an instance method is available, call the instance method to make your scripts more intuitive and easier to read. Example: This sample WQL endpoint uses the `string:urlEncode(string)` namespace function, which includes a parameter binding `worker.id`:

    ```
    {
    "baseUrlType": "workday-wql",
    "name": "employeeInfo",
    "authType": "sso",
    "url": "<% '/data?query=' + string:urlEncode('SELECT user_BasedSecurityGroupsForUser FROM allWorkers WHERE activeStatus=true AND workdayID=\"' + worker.id  + '\"' ) %>"
    }
    ```

    This sample WQL endpoint uses the equivalent string instance method, `<thisString>.urlEncode()`:

    ```
    {
    "baseUrlType": "workday-wql",
    "name": "employeeInfo",
    "authType": "sso",
    "url": "<%
           var wql = 'SELECT user_BasedSecurityGroupsForUser FROM allWorkers WHERE activeStatus=true AND workdayID=\"' + worker.id + '\"';
           'data?query=' + wql.urlEncode();
    %>"
    }
    ```

-   If your PMD has increased its size due to long, complex PMD scripts, or if multiple pages in your app use duplicate PMD scripts, organize your scripts into script modules. In general, we recommend that you keep script modules simple. However, if complex business requirements require you to create multiple complex script modules, consider creating a module dependency that supports reusability across the script modules. Script module dependency can help you organize your code and avoid duplication. Ensure that the script modules export stateless, static variables and function declarations that the PMDs can invoke.
-   When a string value includes both static text and variable substitutions, enclose both static and variable strings inside the `<% %>` notation. Example:

    ```
    "value": "<% 'static text ' + variable %>"
    ```

    The PMD validation returns a warning if you separate static text from an interpolated expression. Example: `"value": " 'static text' + <%variable%> "` returns a warning.

-   When you set the value of a date widget to an empty value, specify `null` instead of an empty string. Example: `dateWidgetId.setValue(null)` or `dateWidgetId.value = null`
-   If you reference a variable that might contain a null value, check for the null value before you reference it.

    Example: To check whether the list `myEndPoint.data.customers` is empty or null before accessing its items:

    ```
    var customers = myEndPoint.data.customers;
    empty customers ? '' : customers[0].name;
    ```

    If you use any of the predefined variables containing a map of custom properties, ensure that the property already exists before you reference it.

    Example: To check the empty state of a `flowVariables` property, you can use either the ternary operator `?:` or the null-coalescing operator `??`.

    ```
    "value": "<% flowVariables.aFlowVariable ?: 'falseValue' %>"
    ```

    For a list of the predefined app variables, see [Reference: Predefined App Variables](/wcp_docs/urd1528494500512.html).

-   You can't define a variable outside a function declaration, such as this declaration:

    ```
    "script": "<%
    var someInvalidVariable;
    var someFunction = function() {
        . . .
    };
    %>",
    ```



## <a id="section_v4z_cwg_lvb"></a>PMD Scripting Memory Management

Although we have a mechanism that allocates memory to accommodate each script execution while we protect our service, we recommend that you follow these best practices for managing memory consumption. If your script exceeds the allocated memory, the script will throw an error.

To manage memory consumption:

-   Reduce the number of loops on a page.

    Nested loops consume more memory than anticipated.

-   Avoid overly complex functions.

    To avoid memory allocation errors on a page, try to break functions into smaller, discrete operations.

    You can reduce the memory usage of a massive function by splitting it into separate functions invoked at different portions of the page. The tradeoff is that PMD scripts on complex widgets might be hard to debug in the logs.

-   Use efficient filters.

    If you can filter the data using report or endpoint query parameters, filter the data with the endpoint instead of with PMD Scripting.

    If you're using the `filter(closure)` function to find only the first match in a list or set, use the `find(closure)` function instead. You can reduce memory usage with the `find(closure)` function because it stops processing after it finds the first match.

    When filtering a large list of data, instead of using the `filter` or `find` functions, consider converting the list to a map that allows a search on the map keys. Depending on the amount of data, looking up map keys can reduce memory usage significantly.

    Example: If you often search a list by a specific key, you can convert the list to a map with that key. This sample script converts a list to a map containing the `id` and `primaryWorkEmail` fields as the map keys. In this case, looking up the `id` and `primaryWorkEmail` map keys is faster than repeatedly filtering a list.

    ```
    var lookupBusinessTitle = function(lookupKey) {
    var list = [
     {
       "descriptor": "Adam Carlton",
       "id": "1da8b422311b4929bfa4520f7f0b4e83",
       "primaryWorkEmail": "acarlton@workday.net",
       "businessTitle": "Staff Payroll Specialist",
       "isManager": false
     },
     {
       "descriptor": "Adesh Shah",
       "id": "ca392111583a102b55041fd6ebc85b93",
       "primaryWorkEmail": "ashah@workday.net",
       "businessTitle": "Resource Manager",
       "isManager": false
     }
    ];
    
    // Assume "id" and "primaryWorkEmail" are keys that can search the same employee.
    // Convert the above list to a map.
    var map = {:};
    for (var employee : list) {
       map.put(employee.id, employee);
       map.put(employee.primaryWorkEmail, employee);
    }
    
    // Looking up the map by "id" or "primaryWorkEmail" key  
    // should be faster than filtering the list every time.
    theBusinessTitle.value = map.get(lookupKey).businessTitle;
    }
    ```

-   Avoid unnecessary indexed list references.

    Avoid repeatedly accessing a list element by referring to its index number when it’s not necessary. The repeated, unnecessary references to indexed list elements consume extra memory and add to memory consumption, especially when combined with looping.

    This sample script repeats unnecessary indexed references:

    ```
    var concatenateString = '';
    for (var i : 0 until size(list)) {
    concatenateString += list[i].key1 + list[i].key2 + list[i].key3 + list[i].key4; }
    ```

    This revised script reduces the memory usage to half:

    ```
    var concatenateString = '';
    for (var obj: list) {
    concatenateString = concatenateString + obj.key1 + obj.key2 + obj.key3 + obj.key4; }
    ```

-   To monitor the memory usage, select <b>Analytics</b> from the <b>Console</b> section of the Developer Site menu.

    The app log displays the function invocation that has the highest usage on the page.



## <a id="section_miscellaneous"></a>Miscellaneous

-   Ensure non-null values in URL parameter bindings.

    If you use dynamic binding in path parameters, always check for null values. Ensure non-null parameter values in:

    -   Endpoint URLs.
    -   The `routingPattern` attribute of a task definition in the AMD.
    -   The `parameterBindings` attribute of `taskReference`.
    -   App variables in `queryParams`, `flowVariables`, `pageVariables`.

-   Exclude PII data when debugging.

    When you send log messages to the console, don't include personally identifiable information (PII) data.

    Also, exclude PII data in URL parameter bindings and app source, including mock data response files.

-   Minimize the use of session variables.

    Use `pmdSessionVariables` with caution. Session variables can affect performance and resource usage.

-   When you use `flowVariables` and `sessionVariables` with `outboundVariable`, consider these best practices:

    -   We recommend that you use `flowVariables` when passing variables across pages in a flow. Although you can also reference flow variables with `queryParams`, use `flowVariables` instead.
    -   Use session variables with caution and at a minimum. Session variables can potentially affect performance and resource usage.
    -   When you reference a property in `flowVariables`, ensure that the property already exists before you reference it. To check the empty state of a `flowVariables` property and ensure an initial value, you can use either the ternary operator `?:` or the null-coalescing operator `??`. Example: `"value": "<% flowVariables.isManager ?: false %>"`.
    -   Don't store widget objects in `flowVariables` and `sessionVariables`. Example: You can't store `someGrid.getSelectedRows()` in `flowVariables`.



### Related Information

[Reference: Limits on Presentation Components](/wcp_docs/GUID-54854abf-9a2e-4788-8aa3-9a92ddf1e71e.html)

[Workday Canvas Design System](https://canvas.workdaydesign.com/)

