This content has been archived.

Developer best practices using Pega Platform

Use the following guidelines and best practices for developing applications in the Pega Platform to create efficient, sustainable, and guardrail-compliant applications .

User interface

  • Reuse and alter UI elements that already exist. Borrow, adapt, convert, and enhance from what is already available, rather than creating new UI elements. We want every part of our application to behave and look like all the other parts.
    • When deciding on style, look at styles already in use in your application and adopt what you can; adapt what you must.
    • For example: The UI should display errors in a layout that uses the Error container, not as a red x followed by a line of text. Similarly, warning messages should be displayed through the use of the Warning container, and not as a yellow triangle followed by text.
  • Avoid using icons for controls. Use link text or a button for clarity and ease of localization.
  • Always use dynamic layouts. Dynamic layouts separate content from presentation and make possible a highly flexible display of content across computers and mobile devices. Consider custom layouts and smart layouts as deprecated.
  • Use autogenerated controls. Convert older controls to the new autogenerated ones. The newer controls are required to render your application in HTML5 Document Type and support browser-agnostic display. Consider non-autogenerated controls to be part of your project's technical debt.
  • Do not create new legacy (non-autogenerated) records: Controls, sections, HTML rules, HTML fragments, text files, and so on. This includes specializing existing records, like breadcrumbs, to use in a class in your application.
  • Do not use inline styles at all. The Pega 7 Platform provides flexible tools for styling and branding that simplify creating and maintaining your application's look.
  • Do not use fixed widths. For table-style layouts that you must use, such as grids or tree grids, always set the width to 100%. This setting allows the display to adapt to the device on which the user is viewing it.
  • Use control validation to make sure entries and selections are valid. Check that a required field has a value, that a text entry has more than the minimum number of characters and fewer than the maximum, and so on. Autogenerated controls have validation options: make use of them so that you do not have to do your validation in a post-submit activity.
  • Select the Use property default checkbox. When possible, set the label value for a control from the short description for its property, rather than using inline labels, which increase maintenance costs.
    The Cell Properties dialog box with the Use property default check box
  • Choose the right control for each list type:
    • Simple lists. The same distinction (do I need a header row?) applies when you choose between column repeat and dynamic column repeat layouts.
      • If you need a header row, use a grid with a width of 100%. Do not set a fixed width in pixels.
      • If you want columns and automatic filtering options or you want the data sourced directly from a report definition, use a grid.
      • Otherwise, a repeating dynamic layout is preferable.
    • Nesting lists
      • If you need a table with multiple header cells, use a tree grid. But note that tree grids that display large amounts of data can have display and performance issues.
      • For other nesting lists, use a tree, which is much simpler and can display huge amounts of data with no performance issues.
  • Using a section versus nesting a dynamic layout. For performance and maintenance reasons, minimize the number of sections that you create.
    • Use a section if:
      • You plan to use it in multiple places.
      • Its contents need to be refreshed from the server more often than the content around it.
      • The data requires a table-based layout such as grid, tree grid, or column repeat but you want to put it in same layout as the rest of the UI, perhaps to share a container format, style, or header.
      • Its content uses a different step page from the UI around it.
    • Otherwise, nest a dynamic layout.
  • Only use a top-level clipboard page directly in your UI if:
    • It is a data page or system page, and you are displaying its data in read-only form.
    • It is an editable data page that you are using to support a stand-alone user input form.
    All other references to top-level pages that are unrelated to the primary page (or undefined pages) should be considered technical debt. This is especially true in the UI regardless of whether the page is used as read-only or editable.
  • Do not overwrite secret UI extension points. Do not overwrite Secret UI extension points at your class, which changes the default modal template for your class and all classes that inherit from it. Secret UI extension points include sections and activities such as pyModalTemplate, pyGridModalTemplate, pyPostGridUpdate, and pyOverlayTemplate.

You can solve one use case by overwriting a Secret UI extension point but doing so can cause maintenance issues. In addition, your change affects every UI interaction of that type in your process. Changing the templates to fix a problem in a modal dialog box is like rewriting the browser because you do not like how a website is displayed.

Instead, use built-in parameters to change the template or behavior for your particular use case.

  • Use set value to set flags. Use case: you have a property .pyIsThingVisible. Put the property in a hidden input field in the UI and use "set value 'pyIsThingVisible = true" (or false). This is lighter weight than using "Run data transform pzSetIsThingVisible" (and creating and maintaining the data transform).
  • Using Refresh When and Refresh Other Section. Use Refresh When whenever possible, especially for read-only references that need to stay in sync with data on the server, as it declares dependencies ("This section needs to be updated when .x changes"). When this is not possible (for example, you have editable references that get updated based on other input or based on untrackable input such as button clicks), use a Refresh Other Section targeted refresh.
  • Avoid Refresh section. Refreshes interrupt the user experience. The user has to wait, the screen seems to hang until the section refreshes, and after the refresh is done, the user has lost focus and has to manually get back to the original screen. It is much better to use client-side interactions by using change tracker whenever possible. For example, rather than refresh a section with a pre-processing data transform, call a data transform or use set value and set your visibility conditionals to evaluate on client.
    • Do refresh a section when:
      • Property values have been updated on the server and the new values need to be reflected in the UI.
      • An action that causes a change to more than one property and occurs only on the client, such as deleting a row, needs to be submitted.
      • Parts of the UI need to be removed from the DOM because of other input.
    • Do not refresh a section when:
      • You need to submit user input (use post value instead).
      • You need to call a data transform or an activity after a user action.
      • You need to recalculate visibility, enabled/disabled state, or read-only state. Select the evaluate on client check box next to the expression field.
    • Judgment calls:
      • You have a complex expression to set visibility, enabled/disabled, or read-only state requiring a when condition. Consider using a declare expression to provide a value for a property and conditionalize on changes to that declare expression.
      • You have to handle style changes based on value changes.
      • You have to validate input and potentially show messages related to various properties. Note that validation provided by the controls, and a validate rule on the flow action, should cover most such situations.
  • Combine server actions. Every action in an action set that makes a call to the server (run a data transform, refresh a section, and so on) is a separate Ajax call that affects server resources.

Return to top

Data

  • Use the tools at your disposal. There are two kinds of data in the application: user input and everything else. Any piece of information used in the process or on the primary page that the user did not give you is non-user input data.
    When you work with non-user-input data, you have five major tools at your disposal:​
    • Read-only data pages (see below for editable data pages) take a fixed set of scalar parameters and load any piece of complex data from them.
      • Obvious examples:
        • Instance from a database based on keyed access.
        • Instance list from a parameterized report.
        • Connector to pull in information from a remote location.
      • Less obvious examples:
        • Taking in a WSDL and parsing out important pieces.
        • Taking in XML or JSON data and creating a nested treeview list from it.
        • Taking in XML or JSON data and turning it into a matching clipboard page or putting it through an XML Parse.
        • Taking in a URL and parsing it into its significant components.
        • Taking in a generation request page and obtaining an evaluation of rules to be generated if the request is valid.
        • Creating a page of simulation data based on the parameters provided to simulate a connector.
        • Taking in the Applies To class and the type of the record that you want to create and returning a list of rulesets that are valid for that context.
        • Basically, if you need a page or object loaded with data, use a data page. It will work for your use case.​
    • Declare expressions define a relationship between the value of a single scalar property and a list of other scalar properties on the work page. When you specify an expression involving one or more properties on the top-level, parent, or primary page specified, the property always contains the value of that expression. You can set the value of a property on every page of a page list this way.

Your expression can involve multiple when conditions and functions, giving you a powerful tool for populating property values.

Examples:

  • The display version of a URL is always the normal URL without the query part.
  • The display version of a URL is always the normal URL without the query part.
  • The default name is always set from the service name without the namespace.
  • The branch ruleset is always <ChosenRuleSet> + "_Branch_" + <ChosenBranch>.
  • The authentication profile name for each method is equal to the authentication profile name selected for the workspace.
  • If @isAnIPAddress returns false, the data source name equals @getResourcePaths(.pySourceURL, 3); otherwise it equals the Integration name
  • Auto-populated properties define a relationship between the value of a single page or page list property and a list of other scalar properties on the work page. You specify a data page to construct the page or page list and the properties to use as parameters to determine the data page's value. This is functionally the same as using a data page directly. So why would you use an autopopulated property instead of just using the data page?
    • Maintainability. If you find yourself repeatedly referencing a data page and passing in the same values as parameters, make an autopopulated property. That way, if later you change the data page or any of the properties, you will not have to hunt all over to find all the places to make the change.
    • Understandability. Non-user input data loaded from user input can still be vital to a user's understanding of how the process works. The data model should lay out exactly what the process needs, and that includes loaded data.
    • Keyed page lists. This feature is extremely powerful, and in the Pega 7 Platform, the only way to leverage it is through autopopulated properties. See Instantly access embedded pages in a data page.
  • There are three types of data transforms: Data transforms support complex data manipulation. If you move data around in your application, you can do it with a data transform (in some cases you may also need to make and use functions).
    • Maps define a translation between two different pieces of data. For example, when mapping the work page to a more compact generation request object you should use a data transform. See Pega-DataModel-DataSources.pyCreateGenerationRequest for an example.
    • Models are used to initialize an object based on its state. For example, it is a good idea to have a pyDefault and a data transform in between the steps in a flow. The first pair sets the object's initial state, and the others set the object to its initial state for the each step.
    • Setting optional relationships is a replacement for declare expressions when they cannot be used. Declare expressions cannot be used to set editable fields. You can have a field set with a declare expression or set with user input, but not both. So if you have a field that is always initially set to .A + " " + .B when .A or .B is set, but the user has the option to customize the field's value, you can't use a declare expression. You need to create a data transform and call it as necessary, because a declare expression cannot do it for you.
  • For data management, activity usage should be primarily restricted to persistence. Creating, updating, and deleting data require using an activity. Otherwise, use activities sparingly in your data layer. While a few situations require using an activity, such as trying to source a page or page list when there is no other way to define the parameters as a set of scalars, in most other cases, you can use specific rules. There are also methods to pass some types of complex objects as parameters. See D_EvalResults for an example with data integration.
  • What about editable data pages? Functionally, they are almost the same as read-only data pages. However, they serve a different purpose: editable data pages provide a way to modify data that is tangential to what you are working on.
    • Examples:
      • The user must select a data record to use in the process. You give them a way to view, and if they want to update and save, the data record in a modal dialog box. This has no effect on your process, because you reference the record by name and the details of that data record are not part of your work object. The modal dialog box should use an editable data page.
      • Your process asks you to select users from your contacts list. It gives you a way to view and edit your contacts, if you want, in a modal dialog box. Again, this has no effect on your process because you only store the reference to the contact, not the details of the contact, in the work object. The modal dialog box should use an editable data page.
      • Your process gives users an investigative tool to explore what they are creating (for example, a testing tool). If they want, users can save snapshots of their configuration and the work page can reference those saved snapshots. Working in this tool has no effect on the process because the details of the tool's configuration are not stored in the work object. This tool should use an editable data page.
      • A rare example: a piece of the portal is mutable, and its details need to be accessible to all processes and actions in that session. This requires a requestor-level editable data page. An example in Designer Studio is the Recent Items explorer.
  • Examples of how not to use an editable data page:
    • Storing information that is part of your process outside of the work page.
    • Storing changes to tangential data to be persisted as part of the original process.
  • Define your data model. Capture every piece of data your process needs with a meaningful property name.
  • Simplify your data model. Fred Brooks wrote, in The Mythical Man-Month, "Show me your code and conceal your data structures, and I shall continue to be mystified. Show me your data structures and I won't usually need your code: it'll be obvious." This should apply to us as we work in the Pega 7 Platform. A developer should be able to look at your class in the Application explorer, or open up the clipboard and look at the primary page, and it should just make sense. The developer should not have to dig into the code to understand what is going on in the application.
  • Do not duplicate properties if you can avoid it.
    • If multiple classes use a property, define the property once where it is accessible to all classes that need to use it.
    • If one of two classes that need to use the property do not share a parent (for example, one is Embed- and the other is not) you can copy the property; but make sure to use the exact same name.
  • Avoid copying data.
    • Data the process uses should exist in only one place. In the Pega 7 Platform, that means in a single property.
    • If the data appears in many places, use the same property in each of those places. Do not copy the data from one property to another property.
    • If user input has a cascading effect (setting a value in one place defaults a number of other values), then it is all right to copy data if the effect is one-directional. In other words, it is only all right to copy if you can define using something like a declare expression. If two properties have a circular dependency then something is wrong.

Return to top

In general

  • Do not duplicate logic.
    • If the same set of steps is used in two places, capture the steps in a single data transform or activity and call that record from both places where the steps are needed.
    • If the same UI elements appear in two places, capture them in a single section, and reference the section in both places where the UI elements are required.
    • If the same Java code or complex expression is used in two places, capture the code or expression in a function and include the function in both places where it is required.
  • Use inheritance.
    • If the same rule applies to two different classes, move it to a shared parent class so both child classes can inherit it.
    • If the same record applies to two classes, with tiny differences, move the shared logic to a record in a shared parent and add py extension points. Overwrite the extension points as needed in the subclass and include the subtle differences there. See Pega-DataModel-DataSources.pxGenerateRules for an example. It uses pyCreateGenerationRequest as an extension point in its subclasses.
  • Do not reinvent the wheel. Leverage the work of others whenever you can. If an API already exists for your task, and has been tested and proven reliable, use it. Time spent researching what is already available to you when you are in unfamiliar territory is almost never time wasted. If you don't know, ask around and find someone who knows the territory to advise you.
    • The Designer Studio has APIs for creating rulesets, creating ruleset versions, creating branches, updating records, and thousands and thousands of other actions.
    • Integration has APIs, records, and utilities for creating HTTP requests, parsing XML and JSON, making SOAP requests, authenticating requests, parsing and validating URLs, and thousands of other tasks.
  • Do not assume a top-level page is there in a data transform, activity, or elsewhere.
    • The golden rule: if another developer with no knowledge of your use case would not be able to tell immediately where the data you are using came from, you should not be using it.
    • Exceptions to this rule are:
      • Data pages
      • System pages
      • Parameter pages
      • Pages created within that rule, and then removed at the end
      • Properties on the primary page that are:
        • Autopopulated
        • Linked
        • Set from user input
        • Set from declare expressions
    • Avoid:
      • Using non-parameter pages not created within that rule
      • Properties on editable data pages that are not loaded by the definition
    • Gray areas:
      • Manually populated properties on the primary page are not loaded within that rule. This is sometimes required, but it still creates confusion for other developers.
      • Top or Parent references. This introduces a dependency in your rule: the rule not only requires a page of the Applies To class as primary, but the primary page must also be embedded in a page with a specific class.
  • Use validate rules. Only do validation in a post-load activity if validate rules do not support the case. They usually do.
  • Do not use obj-save except for testing. Use "call pxUpdateRecord" or "Call Save" instead.

Return to top

Pega Platform 7.1.1 - 8.3.1 System Architect Lead System Architect Business Architect UI/UX Specialist
Suggest Edit

95% found this useful

Have a question? Get answers now.

Visit the Collaboration Center to ask questions, engage in discussions, share ideas, and help others.