<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://community.appian.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian</link><description /><dc:language>en-US</dc:language><generator>Telligent Community 12</generator><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian</link><pubDate>Tue, 23 Apr 2024 13:12:11 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>Appian Max Team</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Current Revision posted to Guide by Appian Max Team on 4/23/2024 1:12:11 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;Skills Covered&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;Activity Chains&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;Batch Size&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;Inflexible Processes&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic"&gt;Complex Logic&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not put complex business logic directly into process model nodes, and instead reference&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;Querying in a Loop&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;Returning All Fields&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;Synchronous Processing&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;Logic Overload&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;Excessive Rule Inputs&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;Screen Size Considerations&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/23</link><pubDate>Wed, 01 Nov 2023 13:55:38 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 23 posted to Guide by joel.larin on 11/1/2023 1:55:38 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;Skills Covered&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;Activity Chains&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;Batch Size&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;Inflexible Processes&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic"&gt;Complex Logic&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not put complex business logic directly into process model nodes, and instead reference&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;Querying in a Loop&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;Returning All Fields&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;Synchronous Processing&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;Logic Overload&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;Excessive Rule Inputs&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;Screen Size Considerations&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/22</link><pubDate>Wed, 22 Mar 2023 15:32:37 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>Devon Lee</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 22 posted to Guide by Devon Lee on 3/22/2023 3:32:37 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;Skills Covered&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;Activity Chains&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;Batch Size&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;Inflexible Processes&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic"&gt;Complex Logic&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not put complex business logic directly into process model nodes, and instead reference&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;Querying in a Loop&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;Returning All Fields&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;Synchronous Processing&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;Logic Overload&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;Excessive Rule Inputs&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;Screen Size Considerations&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/21</link><pubDate>Tue, 21 Mar 2023 13:02:16 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>Adam Levine</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 21 posted to Guide by Adam Levine on 3/21/2023 1:02:16 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_01.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_04.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_05.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic"&gt;&lt;img alt="Complex Logic" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_03.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not put complex business logic directly into process model nodes, and instead reference&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_5F00_2_2D00_06.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_07.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_10.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_09.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_11.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_08.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/20</link><pubDate>Mon, 20 Mar 2023 17:06:05 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>Ben Dudley</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 20 posted to Guide by Ben Dudley on 3/20/2023 5:06:05 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_01.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_04.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_05.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic"&gt;&lt;img alt="Complex Logic" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_03.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not put complex business logic directly into process model nodes, and instead reference&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_5F00_2_2D00_06.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_07.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_10.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_09.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_11.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_08.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/19</link><pubDate>Tue, 07 Feb 2023 20:32:09 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 19 posted to Guide by joel.larin on 2/7/2023 8:32:09 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_01.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_04.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_05.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic"&gt;&lt;img alt="Complex Logic" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_03.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not put complex business logic directly into process model nodes, and instead reference&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_5F00_2_2D00_06.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_07.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_10.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_09.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_11.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_08.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/18</link><pubDate>Tue, 07 Feb 2023 17:40:41 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 18 posted to Guide by joel.larin on 2/7/2023 5:40:41 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_01.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_04.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_05.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic"&gt;&lt;img alt="Complex Logic" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_03.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not put complex business logic directly into process model nodes, and instead reference&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_06.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_07.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_10.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_09.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_11.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_08.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/17</link><pubDate>Tue, 07 Feb 2023 17:38:55 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 17 posted to Guide by joel.larin on 2/7/2023 5:38:55 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_01.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_04.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_05.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic"&gt;&lt;img alt="Complex Logic" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_03.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not put complex business logic directly into process model nodes, and instead reference&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_06.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_07.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_10.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_09.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_11.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns_2D00_08.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/16</link><pubDate>Tue, 07 Feb 2023 17:11:47 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 16 posted to Guide by joel.larin on 2/7/2023 5:11:47 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic"&gt;&lt;img alt="Complex Logic" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not put complex business logic directly into process model nodes, and instead reference&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/15</link><pubDate>Tue, 07 Feb 2023 16:29:26 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 15 posted to Guide by joel.larin on 2/7/2023 4:29:26 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not put complex business logic directly into process model nodes, and instead reference&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;span style="font-weight:400;"&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li&gt;&lt;span&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/14</link><pubDate>Tue, 07 Feb 2023 16:20:33 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 14 posted to Guide by joel.larin on 2/7/2023 4:20:33 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/13</link><pubDate>Tue, 07 Feb 2023 16:19:48 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 13 posted to Guide by joel.larin on 2/7/2023 4:19:48 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Defaulting Paging Batch Size to -1 or Max Size&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Long-Lived Process Instances&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Adding Complex Logic Directly into PM Nodes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Querying in a Loop&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;&lt;span style="font-weight:400;"&gt;Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Process Models with Lots of Logic&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Excessive Rule Inputs&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Not Designing for All Applicable Screen Sizes&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4&gt;Appian&amp;rsquo;s Recommendations:&lt;/h4&gt;
&lt;ul style="font-size:115%;"&gt;
&lt;li&gt;&lt;ol style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/12</link><pubDate>Tue, 07 Feb 2023 16:15:33 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 12 posted to Guide by joel.larin on 2/7/2023 4:15:33 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h4&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h4&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;2. Defaulting Paging Batch Size to -1 or Max Size&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;3. Long-Lived Process Instances&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;4. Adding Complex Logic Directly into PM Nodes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;5. Querying in a Loop&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;6. Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;b&gt;7. Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;8. Process Models with Lots of Logic&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;9. Excessive Rule Inputs&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;10. Not Designing for All Applicable Screen Sizes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/11</link><pubDate>Tue, 07 Feb 2023 16:15:10 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 11 posted to Guide by joel.larin on 2/7/2023 4:15:10 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;h3&gt;Applying Activity Chains Across Many Nodes In A Process Model&lt;/h3&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;2. Defaulting Paging Batch Size to -1 or Max Size&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;3. Long-Lived Process Instances&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;4. Adding Complex Logic Directly into PM Nodes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;5. Querying in a Loop&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;6. Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;b&gt;7. Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;8. Process Models with Lots of Logic&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;9. Excessive Rule Inputs&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;10. Not Designing for All Applicable Screen Sizes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/10</link><pubDate>Tue, 07 Feb 2023 16:14:11 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 10 posted to Guide by joel.larin on 2/7/2023 4:14:11 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;1. Applying Activity Chains Across Many Nodes In A Process Model&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;2. Defaulting Paging Batch Size to -1 or Max Size&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;3. Long-Lived Process Instances&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;4. Adding Complex Logic Directly into PM Nodes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;5. Querying in a Loop&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;6. Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;b&gt;7. Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;8. Process Models with Lots of Logic&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;9. Excessive Rule Inputs&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;10. Not Designing for All Applicable Screen Sizes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style="font-size:115%;"&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/9</link><pubDate>Tue, 07 Feb 2023 16:13:26 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 9 posted to Guide by joel.larin on 2/7/2023 4:13:26 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;1. Applying Activity Chains Across Many Nodes In A Process Model&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;2. Defaulting Paging Batch Size to -1 or Max Size&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;3. Long-Lived Process Instances&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;4. Adding Complex Logic Directly into PM Nodes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;5. Querying in a Loop&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;6. Returning All Fields on a Query When Not Needed&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;b&gt;7. Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;8. Process Models with Lots of Logic&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;9. Excessive Rule Inputs&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;10. Not Designing for All Applicable Screen Sizes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;

&lt;div style="font-size: 90%;"&gt;Tags: design, Antipatterns, Architecture&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/8</link><pubDate>Tue, 07 Feb 2023 16:08:51 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 8 posted to Guide by joel.larin on 2/7/2023 4:08:51 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-size:150%;"&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b style="font-family:inherit;"&gt;1. Applying Activity Chains Across Many Nodes In A Process Model&lt;/b&gt;&lt;/span&gt;&lt;span style="font-family:inherit;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;b&gt;7. Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;8. Process Models with Lots of Logic&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;9. Excessive Rule Inputs&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;10. Not Designing for All Applicable Screen Sizes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/7</link><pubDate>Tue, 07 Feb 2023 16:07:47 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 7 posted to Guide by joel.larin on 2/7/2023 4:07:47 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-size:150%;"&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b style="font-family:inherit;"&gt;1. Applying Activity Chains Across Many Nodes In A Process Model&lt;/b&gt;&lt;/span&gt;&lt;span style="font-family:inherit;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="misuse_of_synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Misuse of Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;b&gt;7. Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;8. Process Models with Lots of Logic&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;9. Excessive Rule Inputs&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considerations&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;10. Not Designing for All Applicable Screen Sizes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/6</link><pubDate>Tue, 07 Feb 2023 16:07:13 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 6 posted to Guide by joel.larin on 2/7/2023 4:07:13 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-size:150%;"&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b style="font-family:inherit;"&gt;1. Applying Activity Chains Across Many Nodes In A Process Model&lt;/b&gt;&lt;/span&gt;&lt;span style="font-family:inherit;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="misuse_of_synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Misuse of Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;b&gt;7. Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;h2 id="logic_overload"&gt;&lt;img alt="Logic Overload" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Logic Overload&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;8. Process Models with Lots of Logic&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="excessive_rule_inputs"&gt;&lt;img alt="Excessive Rule Inputs " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Excessive Rule Inputs&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;9. Excessive Rule Inputs&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="screen_size_considerations"&gt;&lt;img alt="Screen Size Considersations " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Screen Size Considersations&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;10. Not Designing for All Applicable Screen Sizes&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/5</link><pubDate>Tue, 07 Feb 2023 16:01:11 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 5 posted to Guide by joel.larin on 2/7/2023 4:01:11 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-size:150%;"&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b style="font-family:inherit;"&gt;1. Applying Activity Chains Across Many Nodes In A Process Model&lt;/b&gt;&lt;/span&gt;&lt;span style="font-family:inherit;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="complex_logic_in_pm_nodes"&gt;&lt;img alt="Complex Logic in PM Nodes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Complex Logic in PM Nodes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;h2 id="querying_in_a_loop"&gt;&lt;img alt="Querying in a Loop  " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Querying in a Loop &lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="returning_all_fields"&gt;&lt;img alt="Returning All Fields " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Returning All Fields&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="misuse_of_synchronous_processing"&gt;&lt;img alt="Misuse of Synchronous Processing " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Misuse of Synchronous Processing&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&amp;gt;&lt;b&gt;7. Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b&gt;8. Process Models with Lots of Logic&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b style="font-family:inherit;"&gt;9. Excessive Rule Inputs&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;font-weight:400;"&gt;&lt;b style="font-family:inherit;"&gt;10. Not Designing for All Applicable Screen Sizes&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item><item><title>Antipatterns:  Solution Design Mistakes to Avoid in Appian</title><link>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian/revision/4</link><pubDate>Tue, 07 Feb 2023 15:44:29 GMT</pubDate><guid isPermaLink="false">d3a83456-d57b-489c-a84c-4e8267bb592a:0c6e36f2-a6b9-48dd-b06c-c7d8d358406e</guid><dc:creator>joel.larin</dc:creator><comments>https://community.appian.com/success/w/guide/3066/antipatterns-solution-design-mistakes-to-avoid-in-appian#comments</comments><description>Revision 4 posted to Guide by joel.larin on 2/7/2023 3:44:29 PM&lt;br /&gt;
&lt;div style="margin:8px 16% 8px 8%;"&gt;
&lt;h2 id="skills_covered"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Skills Covered&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Low-code development platforms, such as Appian, make creating applications faster and easier compared to common high-code development languages. That said, low-code solution design mistakes (or &amp;lsquo;antipatterns&amp;rsquo;) can reduce coding efficiency and hinder solution scalability and performance. &lt;/span&gt;&lt;b&gt;Appian defines an antipattern as a suboptimal solution design to a common problem.&lt;/b&gt;&lt;span style="font-weight:400;"&gt; A recent survey of Appian&amp;rsquo;s Customer Success team identified 10 antipatterns most frequently observed in customer solutions (see graph below).&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt="Activity Chains" src="/resized-image/__size/960x600/__key/communityserver-wikis-components-files/00-00-00-00-46/2772.antipatterns1.png" /&gt;&lt;/p&gt;
&lt;h2 id="activity_chains"&gt;&lt;img alt=" " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Activity Chains&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-size:150%;"&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b style="font-family:inherit;"&gt;1. Applying Activity Chains Across Many Nodes In A Process Model&lt;/b&gt;&lt;/span&gt;&lt;span style="font-family:inherit;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Activities in a &lt;/span&gt;&lt;a style="font-family:inherit;" href="https://docs.appian.com/suite/help/latest/process_modeling.html"&gt;&lt;span&gt;process model&lt;/span&gt;&lt;/a&gt;&lt;span style="font-family:inherit;"&gt; instance are prioritized and processed based on a first in-first out approach that includes all other process activities occurring on the platform. When using activity chaining, the processing of that activity is given a higher priority in the processing queue. Chaining too many activities together will take processing resources away from other activities happening in the platform for longer periods of time. As such, users or systems may experience a decrease in performance.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only chain when not chaining would negatively impact user experience. For example, between two user input tasks completed by the same person, or between a user input task and an important write to the database that would change the context of the page they will be returned to.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Shorten long process chains by combining transactions into the smallest number of nodes or by moving transactions that can be done asynchronously outside of the chain.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Avoid chaining activities across sub processes or time consuming system nodes like integrations.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns2.png" /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this model, nearly all steps in the process are activity chained. Chaining between the Start Node and the &amp;ldquo;Review Request&amp;rdquo; user input task provides no benefit to the user, and simply ties up precious process resources. A different user will be completing the second user input task, so there is no difference in user experience between chaining vs not chaining, and therefore this flow should not be chained.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The top flow, however, may need to be chained. If the outcome of the &amp;ldquo;Write Maintenance Record&amp;rdquo; node will impact what is seen when the user returns to the page they launched the action from (such as the status of the request), then you may consider chaining this path. Otherwise, this should not be chained either.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="batch_size"&gt;&lt;img alt="Batch Size" src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Batch Size&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In order to access data, a designer may leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_queryrecordtype.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryRecordType&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; or &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/fnc_system_a_queryentity.html"&gt;&lt;span style="font-weight:400;"&gt;a!queryEntity&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; to query against records or the DB respectively. There is a required parameter to both of these functions, &amp;ldquo;pagingInfo&amp;rdquo;, that requires the user to set the startIndex and batchSize. For dynamic data sets, there may be a propensity to set this value to either the max allowable batch size for record queries or to -1 for entity queries. There are times where this is a warranted and justified design decision, but if the correct design thinking has not been applied then it can lead to bringing too much data into memory, causing slow performance and poor user experience. Also, in the unfortunate event that a query&amp;rsquo;s filters have not been correctly applied, this could return a full dataset that could cause extremely slowness and even outages in some cases.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&amp;nbsp;&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;As an emergency brake, set an upper bound that is well above the threshold that would seem appropriate for the returned dataset, but not so high that returning that amount of data could cause serious performance issues.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not use the max batch size of -1 unless there is a very good reason to do so, and typically only in situations where processing is happening unattendedly/asynchronously (i.e. a user is not involved) or during off-hours.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns3.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this use case, there is a query to return service requests based on id or request type. If you pass in an id, it is assumed you should only return 1 item, as that is the primary key. If you pass in a request type, then it can return many values. If you forget to pass in either variable, and ignoreFiltersWithEmptyValues is set to true, then it will return the whole dataset. Given the size of this data, returning the full dataset returns this error, which a user would see on the front-end as a pink box error.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns4.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;In this case, you&amp;rsquo;d likely want to either cap the batchSize at the maximum allowable value, or pass in paging info if this query could be used to populate a grid, which should have a batch size based on the number of rows being displayed to the user.&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="inflexible_processes"&gt;&lt;img alt="Inflexible Processes " src="/resized-image/__size/712x304/__key/communityserver-wikis-components-files/00-00-00-00-46/1261.delivery_5F00_methodology_5F00_1_2D00_02.png" /&gt;&lt;span class="sr-only"&gt;Inflexible Processes&lt;/span&gt;&lt;/h2&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;To be very clear, it is a common business need to implement business workflows that take a quarter, a year, or even multiple years to complete. This only becomes an issue when a single process model is used to span that entire business process.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;For example, developers sometimes attempt to mirror application processes and business processes: if a business process takes 6 months to complete, the application&amp;rsquo;s process also lasts 6 months. Long-lived processes consume more memory and are inflexible when it comes to changing application needs. They can also hinder future maintenance or updates to the process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Break long processes down into smaller, shorter processes for increased flexibility. Natural break points usually align with stopping points where human actors need to take prolonged action outside of the application (eg. training, completion of a project, etc.).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Take a records-first approach by centering workflows around data and records, instead of focusing on BPM workflows that mimic real life.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns5.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Consider an example of a long-running process. We will pretend that each individual user input task can take up to 2 weeks to complete, and that there is also a 3 week wait timer in the middle of the second flow that would add additional time. In total, this process could take up to 11 weeks to complete, meaning this process will be consuming memory on the platform for at least 11 weeks, and potentially longer based on the archiving settings set on the process model.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, consider breaking out each step of the workflow into a separate process model and calling them via &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/record-actions.html#related-actions"&gt;&lt;span style="font-weight:400;"&gt;related actions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;. This means that users will only activate the task when they are prepared to work on it, and the process instance will only live as long as it takes to complete the user input task.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b&gt;4. Adding Complex Logic Directly into PM Nodes&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models provide a lot of flexibility to configure entire complex workflows directly within the confines of the process modeler. That said, a process model node is not always the best place to keep your workflow&amp;rsquo;s important business logic. Adding important logic directly to inputs/outputs of nodes or into script tasks imposes limitations on maintenance and the ability to change in-flight processes.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Firstly, as a best practice, important business logic should be encapsulated in expressions so that it can be referenced, reused, and maintained more easily. Secondly, if for whatever reason there is a bug in the business logic that has been added directly into PM nodes, it is a much harder process to fix that logic in live processes than to simply update the expression.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not put complex business logic directly into process model nodes, and instead reference &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Expressions.html#overview"&gt;&lt;span style="font-weight:400;"&gt;expressions&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; that contain that logic.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only put simplistic logic into PM nodes&amp;rsquo; input/outputs, such as basic setters and getters that allow the process to proceed as expected.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;We have a multi-step process model. Step 1 is a user input task, step 2 is an XOR, and the XOR branches down multiple flows based on complex business logic configured directly into the gateway. There are 1000 live instances currently sitting on step 1, the user input task. A bug is found within the logic of the XOR. When a process launches, it launches based on the process model version that is currently published. Simply fixing the logic in the process model and re-publishing will not address the 1000 live instances that are about to hit the bug once the user input task is completed. If this logic had been encapsulated into an expression rule, simply updating the expression rule would allow all 1000 live instances to proceed appropriately.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns6.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns7.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b style="font-family:inherit;"&gt;5. Querying in a Loop &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;A common mistake among novice developers is to query for data in a loop, such as forEach() or reduce().This can cause unnecessary strain on the application, as this does not properly leverage what each hardware component is best at. A database is very good at taking query parameters that would result in multiple rows being returned and returning that to the user, therefore each roundtrip to the database often has a higher cost than completing the query itself. Not only that, but each time a query is executed, connections/threads in the database, engines, CPU, etc. are being dedicated to that action that could be used for other activities.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Parameterize your query rules so that you can properly execute those queries without a loop.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way to implement a query.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns8.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&amp;hellip;and here is the correct way.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns9.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;font-weight:400;"&gt;&lt;b style="font-family:inherit;"&gt;6. Returning All Fields on a Query When Not Needed&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;There are sometimes use cases where you are executing a query for the sole intent of grabbing a subset of the fields from that record or table. You may be inclined to return the full dataset and then index out the appropriate fields, but that means you are pulling more data than is actually useful into memory. To avoid wasteful memory consumption, leverage a selection parameter to set the fields that you need.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;When querying data, only return data that is absolutely necessary to what you are trying to accomplish in your application.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is the wrong way..&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns10.png" /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.. and here is the right way&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;.&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns11.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b&gt;7. Using Synchronous Processing When it&amp;rsquo;s Actually Asynchronous&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Synchronous processing means that operations must be completed in sequence and only one can be completed at a time. Asynchronous processing means multiple operations can happen simultaneously and therefore another task can kick off while another is completing. This can be discussed through two lenses; 1) How can I properly leverage parallel processing when there are activities that are not dependent on one another to complete? 2) How do I ensure I do not make a user wait to move onto another action, if there is no reason to do so?&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;How you &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Ways_to_Start_a_Process_From_a_Process.html"&gt;&lt;span style="font-weight:400;"&gt;launch a process&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;, chain the process, and how you leverage &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/Sub-Process_Activity.html"&gt;&lt;span style="font-weight:400;"&gt;subprocesses&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt; requires design thinking to determine whether they should be synchronous or asynchronous.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Unless a user needs to wait for completion of a process, use asynchronous methods to start or complete a process.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Only activity chain up until the point required to support user experience (i.e. between user input tasks or to ensure particular activities, such as writes, complete before going back to their prior page).&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Use a startProcess node instead of a subprocess if leveraging MNI and processing is asynchronous.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Most headless actions should be considered inherently asynchronous, at least from the perspective of the user, and should leverage an asynchronous method for launching those processes.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is an example of a process model that has both synchronous and asynchronous concepts. Only one node can execute at a time except for in the middle section, where multiple writes can occur in parallel. This means that the writes are happening asynchronously. This will increase the speed at which the process completes, and can be accomplished because the writes are not dependent on each other.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x240/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns12.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b&gt;8. Process Models with Lots of Logic&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Process models have a rich feature-set, but that doesn&amp;rsquo;t mean every piece of dynamism or logic should exist within that process. Part of the reason is that you do not want process models to become too large, as they will then require more memory to run. Another reason is maintenance and backward compatibility - as noted in antipattern #4, process model instances are based on the published version at the time of launch. If changes need to be made to the logic of the process, then in-flight processes will need to be addressed rather than updating core interfaces or rules within that process.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Minimize the number of nodes in a process model.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Encapsulate logic into rules and interfaces, rather than in the process model.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Here is a process model with logic that would be better served outside of the process model. In this use case, based on the user&amp;rsquo;s group they would see a different version of an approval interface.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns15.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Rather than put that logic in a process model, it is better to do this directly within the interface itself and then dynamically show the appropriate interface content to the user. This will be more scalable and maintainable, and will reduce the need to branch workflows frequently throughout your process model, reducing memory footprint.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;b style="font-family:inherit;"&gt;9. Excessive Rule Inputs&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-family:inherit;"&gt;Have you ever opened up an expression rule or interface, just to find it has a never-ending list of inputs? If you have, it is a tell-tale sign that an object is trying to do too many things. Oftentimes we see that when trying to make a rule more generic, you add parameters so that it can be used in many different scenarios. As the use cases for that rule proliferate, so too do the edge cases and the need for more parameters. Once the list of rule inputs starts requiring a scroll bar, it may be time to split this rule into multiple rules based on common patterns between use cases. Another path forward may be to leverage a helper input, either of its own CDT type or map type, that contains many elements within it. For example, I could pass in a single rule input called &amp;ldquo;supplementalData&amp;rdquo; that is a map type containing an array of other data types and data.&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Do not force components to be reusable for the sake of reusability. Analyze whether the use case is truly different enough to justify splitting logic into multiple rules that require fewer parameters.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Consider using &amp;ldquo;helper&amp;rdquo; rule inputs that contain multiple data elements instead of passing them all individually.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;If rule inputs can be combined into a single rule input in a meaningful way, consider doing so to decrease maintenance costs.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;The below example doesn&amp;rsquo;t have too many rule inputs yet, but given some of the design decisions here that could happen fairly quickly.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns13.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Notice that interface components, in this case columns, are being shown/hidden based on individually set rule inputs. If new columns or interface components are added, then a new rule input would need to be added for every new component if this design was continued. This could quickly proliferate, and result in many unnecessary rule inputs.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Instead, I can pass in a single rule input that holds all the sections that should be dynamically shown to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns14.png" /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="color:#0000ff;font-weight:400;"&gt;&lt;b style="font-family:inherit;"&gt;10. Not Designing for All Applicable Screen Sizes&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;&lt;span style="font-family:inherit;"&gt;The interface object has a lot of power and flexibility to create complex, rich interfaces that provide an excellent user experience. There are many interface layouts, components, and configurations that can be designed, and as a result of this flexibility designers need to be mindful of how interfaces look in different aspect ratios and page sizes. Simple tweaks to interfaces can be the difference between a wonderful UX in both desktop and mobile, and a terrible one.&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Appian&amp;rsquo;s Recommendations:&lt;/span&gt;
&lt;ol&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Understand the current and future usages of the application, and common screen sizes for people within the user base.&lt;/span&gt;&lt;/li&gt;
&lt;li style="font-weight:400;"&gt;&lt;span style="font-weight:400;"&gt;Leverage the tools in the interface designer to view interfaces in different desktop width, tablet, and &lt;/span&gt;&lt;a href="https://docs.appian.com/suite/help/latest/working_in_design_mode.html#previewing-interfaces-for-mobile"&gt;&lt;span style="font-weight:400;"&gt;mobile configurations&lt;/span&gt;&lt;/a&gt;&lt;span style="font-weight:400;"&gt;.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Example:&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;span style="font-weight:400;"&gt;Be sure to click all options in the section highlighted with the arrow, and be particularly mindful of those that you know your customers will be using.&lt;/span&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns16.png" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style="font-size:115%;"&gt;&lt;img alt=" " src="/resized-image/__size/800x600/__key/communityserver-wikis-components-files/00-00-00-00-46/antipatterns17.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item></channel></rss>