Skip to main content

 

 

 

Template:OutSystems/Documentation_KB/Breadcrumb_New_Layout

 

 

Template:OutSystems/OSLanguageSwitcher

 

 

 

OutSystems

Code Analysis Patterns

Up to date list of code patterns analyzed by the current version of Architecture Dashboard.

Architecture

Client and server side Entities and logic aren't isolated

Both client and server side Entities and logic are implemented in the same module

Understanding this pattern
Supplying client-side logic and entities together with reusable server-side definitions does not optimize mobile functionality. While server-side logic can be extended to support several applications, generalizing client-side logic for the same purpose tends to generate redundant information and useless sync logic for each specific mobile app.
Fixing this pattern
Client-side logic should be catered to the use cases of each mobile application. The typical pattern is to have a generic server-side module around a concept, e.g., Accounts, and specialization for each mobile app that handles Accounts. For instance, if you have a Mobile Banking (MB) application for end-users and a Mobile Agent (MA) application for agents, on top of the server-side core service Account_CS module, you should create the MB_Account_CS and the MA_Account_CS modules each with specialized client-side databases and logic.

Monolithic mobile UI module

All mobile UI content is being kept in the same module

Understanding this pattern
Due to technical requirements for Native Build generation, all screens of a mobile application, including the exception flow and the menu, must be placed in the same Module. This means the UI screens module can become a monolithic Module that keeps growing, making different life cycles for different processes hard to maintain.
Fixing this pattern
Keep all the Screen Flows in a single Module but keep Screens only as layout containers. Supply UI content in blocks from different Core widgets modules, organized per functional area to allow different life cycles per such functional area.

Orchestration module providing services

Orchestration module providing services

Understanding this pattern
An Orchestration module is not intended to provide services - usually, any reference to it reveals the existence of misplaced reusable code. And, being in the top layer, any reference to an Orchestration module will inherit the entire hierarchy of modules beneath it.
Fixing this pattern
Check Discovery for which elements are being consumed. Extract those elements and move them to Core/Library modules, according to the concepts. Check the forge component Refactor for more support on this operation.

End-user module providing services

End-user module providing services to non-Orchestration modules

Understanding this pattern
An End-user module is not intended to provide services - usually, any reference to it reveals the existence of misplaced reusable code. References to an End-user compromise life cycle independence between applications and typically pull a lot of indirect unwanted references.
Fixing this pattern
Check Discovery to see which elements are being consumed. Extract those elements and move them to Core/Library modules, according to the concepts. Check the Forge component Refactor for more support on this operation.

Core module consumed by Foundation module

Core module providing services to Foundation module

Understanding this pattern
Foundation modules are not supposed to consume services from Core module. They need to be fully isolated with no business logic or reference to a business module, otherwise consuming a library might bring unexpected impacts.
Fixing this pattern
Check Discovery for consumed elements. Either they should be moved to a Foundation module if they aren't business logic, or consider if the logic in the Foundation module that is consuming the Core module elements is not misplaced.

Cyclic references between modules

Cyclic references between two Library or two Core modules

Understanding this pattern
Cycles between modules usually means an error in service abstraction and brings unwanted impacts in both development and runtime.
Fixing this pattern
Understand the conceptual relation between modules involved in a cycle, to decide which module is not supposed to consume the other. Check Discovery for the wrongly consumed elements and decide whether moving them to the module that is supposed to be consumed makes sense in terms of conceptual abstraction, or if there is a need to move them to a composition module on top of the two.

Monolithic Service Module

Module providing services with too many public elements

Understanding this pattern
A monolithic module with too many public elements probably contains too many concepts inside. The module becomes very heavy and hard to maintain, and the lack of granularity causes unwanted impacts for consumers that only require a subset of services.
Fixing this pattern
Identify the granular concepts inside the monolithic module and design a conceptual relation among those concepts, making sure that you get a graph with no cycles. Then refactor your monolithic module to split it into the identified concepts, respecting their conceptual relation. Check the Forge component Refactor for more support on this operation.

Orchestration application providing services

Orchestration application providing services

Understanding this pattern
An Orchestration application must be fully isolated, not providing services - any reference to it usually reveals the existence of misplaced reusable code. Furthermore, being in the top layer, any reference to an Orchestration application may inherit the entire hierarchy of modules beneath it.
Fixing this pattern
Check Discovery for which modules are being consumed and move them to Core/Library applications, according to the concepts.

End-user application providing services

End-user application providing services to non-Orchestration applications

Understanding this pattern
An End-user application is not supposed to provide reusable modules for other applications. References to an End-user compromise life cycle independence between applications and typically pull a lot of unwanted indirect references.
Fixing this pattern
Check Discovery for which modules are being consumed and move them to Core/Library applications, according to the concepts. Check the forge component Refactor for more support on this operation.

Core application consumed by Foundations

Core application with modules consumed by Library applications

Understanding this pattern
Library applications are not expected to consume modules from a Core application since they should not rely on business-related services.

Check Discovery for consumed modules. If they are Libraries, they should be moved to a Library application. If they are Core modules, you need to question if the logic in the Library modules that is consuming those modules are not misplaced.

Cyclic references between applications

Cyclic references between two Library or two Core applications

Understanding this pattern
Cycles between Core or Library applications usually means wrong isolation of reusable modules that leads to consumers being unnecessarily impacted when requiring only one of the applications.
Fixing this pattern
Understand the conceptual relation between applications involved in a cycle, to decide which application is not supposed to be consumed by the other. Check Discovery for the wrongly consumed modules and decide whether moving them to the application that is supposed to be consumed makes sense in terms of conceptual abstraction.

Lack of module classification

Module not placed in one of the Canvas layers in Discovery

Understanding this pattern
Since a Module isn't classified (undefined) in Discovery, it doesn't allow proper validation of the architecture and the detection of Canvas violation patterns.
Fixing this pattern
On the Discovery application, check the unclassified module. Classify its layer based on its nature and architecture best practices (Orchestration, End-user, Core, or Foundation). In case a module falls on more than one layer, classify it as the top one. For example, if a module has concepts of End-user layer (screens) and the Core layer (entities), it should be set as End-user.

Foundation or Core module with screens

Misplaced End-user screens

Understanding this pattern
End-user screens that support business processes should only be provided at Orchestration or End-user modules. The only exceptions are exception handling screens or reusable pop-up screens.
Fixing this pattern
Move End-user screens supporting business processes to end-user modules. If you are building test screens to validate your logic, remember to delete them when obsolete. If you want to keep the test for future reference, then move the screen to a test end-user module. This way, you can temporarily perform quick tests directly on the Module that you are developing. If you are building reusable popup screens, suffix them with popup and they will be ignored.

Public Entities aren't read-only

Public Entities should be exposed as read-only

Understanding this pattern
When public Entities are not exposed as read-only, their entity actions for creating, updating and deleting records are available to be referenced by any consumer. These entity actions directly affect records in the entity, allowing any consumer to perform inconsistent and potentially destructive changes without considering the complete semantic and validation of the operation.
Fixing this pattern
Enable Expose Read-Only in public Entities and create your own public actions for creating, updating and deleting records of those entities, abstracting all validations, business rules or other side-effects such as audit trailing or notifying changes to another system.

Monolithic Web UI Module

Module has too many end-user interfaces

Understanding this pattern
A monolithic End-user or Orchestration module with too many Screens probably contains too many business processes inside with independent lifecycles. The module becomes very large and causes unwanted impacts on maintainability and release management.
Fixing this pattern
Identify the independent UI flows, with no destinations among themselves, and separate them in multiple Modules.

Core module providing services to sublayers

Core module in a higher sublayer is providing services to core modules in lower sublayers

Understanding this pattern
Within the core sublayer of modules there are upper layer modules (like BL, API, or Sync modules) that are consumed by lower layered ones (like CS). This can indicate a wrong abstraction of concepts, where some core concept is becoming dependent on a particular business concept composition. These types of dependencies inside core modules should usually be done from top to bottom also."
Fixing this pattern
Check Discovery. Move the upper core module to a lower sublayer (because it is supposed to provide services to other consumers below) alternatively, check if any of the consumed object/logic from the upper module is misplaced, requiring it to be refactored to the lower sublayer module.

Foundation module providing services to sublayers

Foundation module in a higher sublayer is providing services to Foundation modules in lower sublayers

Understanding this pattern
An incorrect abstraction of concepts may lead to unmanageable dependencies. The module in the higher layer should be a composition of lower modules and not the other way around. "Check Discovery for the producer module.
Fixing this pattern
If the producer is supposed to provide services to its current consumers move it to a lower sublayer. If not, reevaluate the inclusion of the consumed elements in the producer module, and move the consumed elements to a lower sublayer module.

Maintainability

Missing description on public element

Required description on public elements

Understanding this pattern
Meaningful descriptions in modules, public elements, entities, attributes and input/output parameters clarify their purpose and expected behavior. It’s crucial when consuming closed modules, because the implemented logic isn't visible.
Fixing this pattern
Add a description to the module that explains the module's purpose and identifies the concepts it contains. Add meaningful descriptions to all of the module public elements, and their related parameters (or attributes in the case of Entities or Structures). If parameter/attribute names already follow well-established naming conventions, you can choose not to add a description (e.g., Id, Name, Label, Description, CreatedBy, UpdatedBy, CreatedOn, UpdatedOn).

Long undocumented flow

Action with a long and undocumented flow

Understanding this pattern
A Preparation or screen action with more than 20 nodes or an action with more than 40 nodes is hard to maintain, especially if it has no comments to explain the logic.
Fixing this pattern
Break flow logic into smaller and potentially reusable actions and/or place comments to explain portions of your flow. Note: explore the 'Extract to Action' feature, available in the right-click menu when you select a portion of a flow.

Unidentified public action managing transaction

Description of a public action doesn't mention that it manages a database transaction

Understanding this pattern
Explicit CommitTransaction or AbortTransaction operations may commit/rollback data in unexpected places, affecting your app. Since the content of public reusable actions may not be accessible, it is extremely important to explicitly describe when the transaction is being handled inside it, to avoid unwanted runtime behaviors.
Fixing this pattern
Clearly identify in the description of the public action that the transaction is being managed and in which cases is being committed or aborted by adding, for example, "[commit transaction]" or "[abort transaction]" at the end of the description.

Too much disabled code

Module containing too much disabled code

Understanding this pattern
Keeping a large amount of disabled code leaves clutter and makes it difficult to read. It increases maintenance costs and wastes time, as people tend to interpret disabled code to understand its relevance better.
Fixing this pattern
Remove the code if it has been disabled for a while or the app is in production, and behaving correctly.

Security

SQL Injection

Avoid enabling the Expand Inline property of a SQL Query Parameter since it could make your application vulnerable to SQL injection.

Understanding this pattern
OutSystems uses prepared statements by default to execute the SQL queries that you define in SQL elements. These prepared statements contain SQL parameters or placeholders, for which you define values before executing the SQL statement. These parameters can only store a value of a given type and not arbitrary SQL fragments. If you enable the Expand Inline property for a Query Parameter, its value will no longer be handled as a SQL parameter value. Instead, the Query Parameter value will be included in the SQL statement without first being evaluated and turned into a literal by the SQL engine. This means that you can use the Query Parameter to insert SQL fragments in the full SQL statement dynamically, but it also means that your end-users may be able to exploit this fact if you do not take the necessary precautions.

OutSystems will use an SQL parameter for every Query Parameter that has the Expand Inline property disabled. This property is disabled by default, providing you default protection against SQL injection attacks.

It's difficult to use properly expanded parameters inline since you need to make sure that any user input is properly escaped before using it in an SQL statement. If you can, avoid enabling this property altogether.

Fixing this pattern

OutSystems provides ways of implementing common use cases without enabling the Expand Inline property. Check Building dynamic SQL statements the right way Best Practice in the documentation.

If you must enable Expand Inline, take the following recommendations into account:

  • Do not perform manual string encoding using the Replace. String literals should only be encoded using the EncodeSql function. Doing it manually through the Replace function is prone to errors and can introduce bugs into your application that can later be exploited by end-users.
  • Use EncodeSql() to encode string literals. The EncodeSql function encodes string literals to be used in SQL statements when the Expand Inline property is enabled. Make sure you avoid the following bad practices when using EncodeSql():
  • Do not use EncodeSql() to encode the full contents of an SQL parameter. For example: myparameter = EncodeSql(""WHERE surname = "" + @myVariable1 + "" OR name = "" + @myVariable2) This pattern is wrong on most occasions, so you will get a warning if you use it. Use EncodeSql only to encode string literals, not complete fragments of an SQL statement.
  • Do not build ""WHERE column IN (@values)"" clauses by wrapping all the values in a EncodeSql call: values = EncodeSql(name1 + "","" + name2 + "","" + name) This approach will not protect you from SQL injection. Instead, use the BuildSafe_InClauseIntegerList() and BuildSafe_InClauseTextList() functions to build ""WHERE column IN (@values)"" clauses.

Visible disabled Button

Disabled button that is still visible

Understanding this pattern
A button that is disabled doesn't prevent an experienced person from re-enabling the button at runtime by using, for example, the development tools on a browser. This will lead to the ability to enable the functionality and allow the user to press the button even if the user didn't have permission or was originally unable to press it.
Fixing this pattern
In the button, instead of having the Enable property set to false, set the Visible property as false instead (or in conjunction with the other one). This will prevent the rendering of the button completely on the client browser and will prevent the possibility of an experienced user to hack the button and enable the functionality.

Avoid Anonymous and/or Registered access Screens

Screens should have custom Roles set instead of using System Roles (Anonymous and Registered)

Understanding this pattern
OutSystems provides you with a default set of System Roles (Anonymous and Registered) but you should define your own custom Roles that are specific to your app.\ Giving access to the Registered role, a Screen can be accessed by any end-user with a valid OutSystems session, namely any user that has logged into an app running in the same Platform Server.\ Giving access to the Anonymous role, a Screen can be accessed by any end-user, including users that are not logged in.
Fixing this pattern
Disable the Registered Role access on all Screens (that don't have the Anonymous Role) and explicitly grant access for custom Roles that are specific to your app. Disable the Anonymous Role access unless you want to make a Screen public and accessible by everyone that can reach your app."

JavaScript or HTML injection

Unescape/unencoded user inputs or screen variables

Understanding this pattern
Screen user inputs and variables may be used for HTML or JavaScript injection. This vulnerability may also be exploited in Cross-Site Scripting (XSS) attacks.
Fixing this pattern

Do one of the following:

  • Enable the Escape Content property of the expression.
  • Use the EncodeHtml() built-in function to replace all HTML reserved characters by their escaped counterpart.
  • Use the EncodeJavascript() built-in function to replace all JavaScript reserved characters by their escaped counterpart so they can be included in a JavaScript string.
  • Use the SanitizeHtml() function from the Sanitization extension module to ensure that the value entered by the end-user does not contain any malicious content.
  • Use the EncodeUrl() built-in function to replace all URL invalid characters by their percent-encoded counterpart."

Exposed REST services are not secured

Exposed REST services should enforce SSL/TLS, and authentication

Understanding this pattern
Unsecured connections may be read by unauthorized third-party and be target of man-in-the-middle attacks.
Fixing this pattern
Secure application end-points by configuring SSL/TLS, which ensures the data sent to the exposed service can't be eavesdropped or tampered with. OutSystems provides controls to exposed REST APIs with login/password protection, except when its configured for internal access only.

Not checking if the mobile device is compromised

Not checking if the mobile device is compromised, meaning it's rooted (Android) or jailbroken (iOS).

Understanding this pattern
Mobile apps are susceptible to code modification. Even when encrypted by the operating system, sophisticated attackers can, and will, recover a decrypted binary, which gives them every opportunity to edit system calls, conditional statements, and other application logic.
Fixing this pattern
To prevent an attacker from tampering your application, OWASP recommends you validate the integrity of your executable at runtime. With the Secure Device plugin (available on Forge), you can check whether the end-user has compromised their devices to gain access to privileged subroutines and then shut down the application in response. To use the Secure Device plugin, add a reference to the plugin and use the CheckSecureDevicePlugin action in the OnApplicationReady event. This ensures that the plugin will be included in the native build.

Performance

Query data in ViewState

Screen Actions are using query data obtained in Preparation

Understanding this pattern
If you use Preparation data on a Screen Action, that data will be saved in the screen's ViewState. Adding more data to the ViewState increases response size and loading time in the browser since it's sent to the user at every page request, and is also sent back to the server on every POST, postback, or AJAX request.
Fixing this pattern
Avoid using data from Preparation in Screen Actions. For example, instead of using the TableRecords record data in a Screen Action, send the Record's identifier as an Input Parameter of the Screen Action, only fetching the data from the database when needed. If you need the full list of records, refresh the query. It is better to rerun the query server-side than to send the data back and forth through the ViewState.

Large Local Variable in ViewState

A large screen Local Variable is being used in a Screen Action

Understanding this pattern
When a screen's Local Variable is used in a Screen Action, the Local Variable data is saved in the ViewState of that Screen. Local Variables with data types Compound or Collection are considered large. Including additional data to the ViewState increases response size and loading time in the browser since it's sent to the user at every page request, and is also sent back at every POST, postback, or AJAX request.
Fixing this pattern
Avoid using screen Local Variables in Screen Actions.

Inefficient empty list test

Using the Count property of an Aggregate or SQL query to check if results were returned

Understanding this pattern
For performance, OutSystems query optimizer makes sure the output of Aggregates and advanced queries only the essential data to feed a screen. This means, the Count property needs to execute an additional query to get the total number of registries.
Fixing this pattern
Use List.Empty property to test for lack of results instead of List.Count.

Inefficient query count

Counting query results using an inefficient query

Understanding this pattern
SQL queries are usually designed for retrieving data and may perform joins and fetch extra data, needed for processing but that are not required to count the query results. When using the Count property of a query, the same query is executed to count the results, which is inefficient since it will use the same query definition.
Fixing this pattern
Use a simplified SQL query to efficiently count the results, removing unneeded extra data and joins.

Inline javascript

Inline JavaScript defined in an unescaped Expression

Understanding this pattern
JavaScript defined at the screen/web block level is optimized by OutSystems. For example, if you have the same web block twice on your screen, it’s only included once. It also improves maintenance.
Fixing this pattern
Define JavaScript at the screen/web block level instead of in inline expressions.

Inline CSS style

CSS style is being defined as an extended property of a screen element

Understanding this pattern
CSS and HTML should be kept separate. Inline styles are inefficient, harder to maintain, and make your HTML larger.
Fixing this pattern
CSS should be centrally managed in the application style guide to avoid loading a large number of CSS files. If the CSS is specific to one screen or web block, define your CSS at the screen/web block level instead of in extended properties.

Unlimited records in SQL query

Number of records fetched from database is not set in SQL query

Understanding this pattern
More records are fetched from the database than are used by the application, resulting in useless I/O and memory consumption.
Fixing this pattern
Use ROWNUM (for Oracle) or TOP (for MS SQL Server) in the SQL query to limit the number of records to the required amount. Note that in SQL queries the Max. Records parameter only limits the number of records displayed, and not the number of records fetched.

Unlimited records in Aggregate

Number of records fetched from database is not set in Aggregate

Understanding this pattern
More records are fetched from the database than are used by the application, resulting in useless I/O and memory consumption.
Fixing this pattern
Set the Max. Records parameter of the Aggregate to the required amount of records.

Site Property update

Site Property is being updated using Application logic

Understanding this pattern
When a Site Property is updated it invalidates the Module's cache. Therefore subsequent accesses to cached data have to be fetched from database or recalculated in the application logic, which may result in a performance hit.
Fixing this pattern
Avoid changing Site Property values programmatically. Use alternatives such as storing the value in the database, accessing it when needed.

Dynamic inline parameter

Dynamic expression used in an inline expanded Query Parameter of a SQL Query

Understanding this pattern
Query Parameters that are expanded inline and change too often don't allow the database to optimize execution plans since it keeps generating new queries
Fixing this pattern
Change the query to remove the frequently changing inline parameters. Consider selecting specialized queries depending on the parameter or using sub-queries or temporary tables.

Inadequate data preparation

Query is being executed inside a loop

Understanding this pattern
Each run of the query may be fast enough, but when inside a loop, the total amount of database effort may be considerable.
Fixing this pattern
Often, executing only one complex SQL query to obtain the required information is better than executing a simple Aggregate in a For Each loop. Also, check if the Entity model copes with your needs - when the database model is inadequate, getting the required information proves overly complex to be fetched by a single query.

Large Session Variable

Large Session Variable is being used

Understanding this pattern
On all Screen requests, the current session's data is loaded from the database. This data is binary and includes all Session Variables. Session Variables with data type Compound or Collection are considered large. If large Session Variables are used, each request will take longer to process the session data (include serializing and deserializing it), increasing response times and causing contention in all concurrent requests.
Fixing this pattern
Store this data in an Entity using the session identifier as primary key and fetch it only when needed. Keep the session limited to context information that is useful in every request.

Large image

Large images included in the Module

Understanding this pattern
Large images have different kinds of impact on an application. When large images are being used in a screen to be displayed, they will need to be fetched from the server, increasing bandwidth usage and request processing time in the browser. Even setting their width/height to lower values, will not reduce the bandwidth fetch of the image from the server. On the development side, a Module with large images takes longer to be saved and published, consuming additional bandwidth when uploaded or downloaded from the server.
Fixing this pattern
Reduce the size of images to the minimum needed to be correctly displayed to the user (below 150KB/500KB for Mobile/Web Applications). Reduce the image's resolution to a maximum of 1024px. Consider the possibility of having big images as external resources not contained inside the module itself.

Long Server Requests Timeout

Long Server Request Timeout

Understanding this pattern
The default timeout for server action requests is too long or an explicit timeout in a server call is too long (more than 10 seconds). In a mobile application, a server request should be efficient. 10 seconds is all it takes for a device to go to sleep mode or lose network connectivity.
Fixing this pattern
Prepare and cache data in advance on the server-side to be promptly available when required. The Server Request timeout should also be reduced to fail quickly (with a "retry later" message), except on explicit operations, because it's acceptable for those to take a little bit longer.

Server requests in client events

Server actions being called in client events

Understanding this pattern
Server calls should be avoided on client events (On Initialize, On Ready, On Render, On After Fetch). These events are serialized in the request and server calls may tremendously impact the wait time to render the screen.
Fixing this pattern
A mobile app should rely on local storage for performance and offline. Server-side requests should be limited to synchronization requests (typically performed on business events fired in screen actions, session start or online events) and online transactions (typically performed in screen actions).

Non-optimized local data fetch

Local data fetch performed in client events

Understanding this pattern
Local data fetch should be avoided on client events (On Initialize, On Ready, On Render). These events are fully serialized, not taking advantage of the parallel fetch of data while the screen is being already rendered.
Fixing this pattern
Retrieving data should occur inside data fetch calls to enable the parallelization of several data fetches and the screen render. If a data fetch depends on a previous fetch, use the On After Fetch event.

Non-optimized Local Storage

Local Storage model is not optimized

Understanding this pattern
Local Storage is being either copied exactly from server Entities or is using a complex model (including too many fields, Foreign Keys, or Complex data types). This forces the use of multiples joins in Client Aggregates, hindering the performance of the application on mobile devices.
Fixing this pattern
Simplify local entities to the minimum number of attributes and de-normalize them as much as possible, still keeping them simple; review client Aggregates for unnecessary joins.

Not taking advantage of Local Storage

Using too many server requests (screen data action) instead of using Local Storage

Understanding this pattern
Using too many screen data actions that gather data from server is an indication that local storage is not properly defined or being used, as all data is being retrieved from the server. This hinders performance and offline requirements in mobile applications.
Fixing this pattern
Implement proper synchronization mechanisms and Local Storage to make sure that most data is available in the local storage, also facilitating offline scenarios.

Multiple server requests (Aggregates or actions) inside Client Actions

Multiple server Aggregates or multiple Server Action requests inside Client Actions

Understanding this pattern
Each server request or server Aggregate is a different request, generating its overhead on establishing the connection and launching a server-side process. Multiple processes also generate different database transactions.
Fixing this pattern
Instead of sequencing a set of server requests or server Aggregates on your client-side code, compose all required server logic in a single Server Action to reduce the number of server requests.

Incorrect offline sync method

Offline sync patterns are not implemented correctly

Understanding this pattern
No offline synchronization is being made or is being executed with poor performance
Fixing this pattern
Place the local entity synchronization actions inside the OfflineDataSync action, configure the manual and automatic start of sync and use TrigerOfflineDataSync for background synchronizations. SyncUnit parameter should be used to prevent updating unnecessary entities.

No asynchronous offline sync

Server data is not being stored in the local database asynchronously

Understanding this pattern
Synchronously storing server data will result in blocking screens and, or actions that may impact the overall user experience.
Fixing this pattern
Use TriggerOfflineDataSync to execute OfflineDataSync asynchronously and react to the OnSyncComplete event to update UI modules.

Not addressing bad network and server connection

Not addressing a bad network and server connection

Understanding this pattern
The logic must be designed to deal with different network conditions, not just setting as ON and OFF.
Fixing this pattern
Use GetNetworkStatus to detect the network conditions and ensure that the logic and UI take the conditions into consideration and reacts appropriately.

CSS in the screen's style sheet

CSS in the screen's style sheet

Understanding this pattern
Having CSS spread through different screens may create maintenance issues. Centralizing CSS in the app's Theme helps to reduce the maintenance cost. Also, defining CSS in mobile Screens will create flicker when navigating through different pages.
Fixing this pattern
Define the class inside the theme of the application. Even if it's only a small change it is better to define a specific class (that can then be reused) for it than add to a specific page and then copy the same class over and over.

Image widgets without width

Set the width of Image widgets

Understanding this pattern
If you do not set the width and height of an Image widget, the user of your app may see a flickering effect while the final image is being downloaded. For example, not setting the image height might cause the total height of the screen to change until the image loads completely since the widget height will be changing from 0px to the height of the final image.
Fixing this pattern
Set the width and height of the Image widget to the expected size of the final image. If you only set one of the dimensions, the other will be adjusted proportionally.

Complex splash screen

Keep the splash screen simple and fast

Understanding this pattern
Adding heavy or lengthy operations to the splash screen will increase the time until the app can be used. Additionally, if the splash screen has a complex UI, the users may see a blank screen before the splash screen renders.
Fixing this pattern

To keep the splash screen simple and fast to load, you should avoid:

  • Requests to the server.
  • Heavy logic.
  • Complex UI, namely too many Blocks.

Avoid long-running Timers

Avoid running Timers for longer than 30 minutes.

Understanding this pattern
A timer that exceeds its Timeout in the Minutes property may result in the code and data being reprocessed reprocessing because the automatic retry mechanism for timers reruns the code when errors occur. Following the wake timer pattern will decrease the probability of the timer being interrupted by the Scheduler process due to reaching the Timeout in Minutes threshold. Using the wake timer pattern can: * Reduce the probability of a timer being interrupted. * Avoid cases of data inconsistency. * Avoid endless reprocessing of the same data.
Fixing this pattern
Long execution Timers should follow the wake timer pattern to reschedule themselves to restart and continue the current task at hand. To implement the wake timer pattern start by adding an explicit logical timeout inside the Timer logic that when reached takes the necessary actions to properly terminate the current execution, store the current progress of the process in such a way that when its execution restarts it can easily pick up the execution from this stored last point. This pattern ends with a wake timer action for itself at the end of the timer flow. Another good practice for long Timers is to define them with checkpoints so that the Timer can be killed and restarted with no impact on the data. At these checkpoints, consider executing partial commits to ensure that if some error occurs the processed data is only rolled back until the last commit (and avoid processing the same data all over again on next execution).

Large resource

Large resources included in module

Understanding this pattern
When publishing to the environment, large resources in the module can slow down publishing and downloading, impacting the development team.
Fixing this pattern
Reduce the size of the resources to the minimum needed for its usage (below 150KB/500KB for Mobile/Web Applications). Consider the possibility of having the resources served externally to the application, and for example, having a screen to upload the resource, then having it stored in the file system or a Binary database table.
  • Was this article helpful?