OutSystems Tech Lead cheat sheet
OutSystems Tech Lead · Keep the key ideas within reach.
Architecture
OutSystems canonical layers: End-User, UI (screens/blocks), Logic (server/client actions), Data (entities, static entities), and Integration (REST/SOAP, BPT).
Separation rule: UI modules reference Core/Logic modules, never the reverse; cyclic module references are a build error and signal a layering violation.
Module types: Reactive Web App, Traditional Web App, Mobile App, Service (REST/SOAP exposure), Library (reusable logic), and Extension (C# integration).
Exposure decision: set 'Expose as REST/SOAP' only on Service modules; public elements form the contract, so treat them as versioned API surface.
Entity scope: entities are exposed per module; use public entities sparingly and prefer service actions to avoid tight data coupling across modules.
BPT: processes are defined in a Process entity with Human Activity, Automatic Activity, Wait, Decision, and End; process instances persist in the platform's process tables.
Client vs server action rule: client actions run in the browser and cannot hold secrets; server actions can call integrations and use server-side entities safely.
Screens should be stateless; state belongs in session variables, client variables (Local Storage), or server-side entities, not in prepared screen inputs alone.
Architecture review gate: any new module must declare its layer, its owner, and its dependency direction before the first sprint closes.
Delivery and project leadership
OutSystems delivery lifecycle: Development -> QA/Test -> Pre-Production -> Production, with each environment bound to a deployment controller (LifeTime).
Deployment rule: publish to the next environment only via a deployment plan; direct publishing across stages breaks the audit trail and rollback path.
Sprint sizing heuristic: keep a user story within one module and one developer to avoid merge conflicts in the shared module model.
Estimation anchor: count screen+action+entity surface, not screens alone; integration work usually carries the highest uncertainty and should be timeboxed first.
Definition of Done includes: unit tests for server actions, a published version in QA, and a passing impact analysis on the dependency graph.
Tagging convention: tag application versions at each promotion (e.g., v1.4.0-QA) so rollback targets are unambiguous in LifeTime.
Risk register entry rule: any external integration without a mock or contract test is a delivery risk and must be listed at sprint planning.
Team topology: assign module ownership per developer or pair; shared ownership of a single module is the most common source of lost work.
Security
Authentication: use the platform's built-in Users/Roles model or an external IdP (SAML/OIDC via the Users provider); never build a custom credential store in entities.
Authorization rule: check roles in server actions and screen-level 'Roles' property; client-side role checks are UX only and are bypassable.
Sensitive data: never place secrets, tokens, or PII in client variables, screen inputs, or client actions; they are visible in the browser.
SQL injection: use platform aggregates and parameterized SQL; dynamic SQL built by string concatenation is a finding regardless of input validation.
XSS: prefer built-in widgets and expression escaping; use the sanitize/encode built-ins when rendering raw HTML from user input.
Integration security: store API keys in the platform's secure settings or a secrets manager, not in module constants committed to the repository.
Least privilege: define roles per business function, not per screen; audit role assignments each release because role drift is a common finding.
Logging: log security-relevant events (login failures, permission denials) in server actions, and never log full tokens or passwords.
Dependency risk: third-party Forge components must be reviewed for permissions and maintained status before entering a production app.
Performance and troubleshooting
Aggregate rule: fetch only needed attributes and filter on indexed attributes; an unfiltered aggregate over a large entity is the most common performance defect.
N+1 pattern: a loop that runs an aggregate or server action per iteration should be replaced by a single aggregate plus in-memory join.
Screen performance: reduce data on initial load; use pagination or lazy loading for lists above roughly 50 rows.
Caching: use the platform cache for reference data with a defined invalidation trigger; cache without invalidation is a defect, not an optimization.
Troubleshooting path: reproduce in QA, check the error log in Service Center/LifeTime, then inspect the module's logs and the specific action's stack trace.
Timeout diagnosis: distinguish client timeout, server request timeout, and integration timeout; each has a different configuration surface.
Database health: review slow queries and index usage in the platform's monitoring before adding code-level workarounds.
Impact analysis: run it before publishing to see which modules and consumers break; it is the fastest way to catch a breaking public-element change.
Performance budget: define a target response time per screen and per API action at design time; measure in QA with realistic data volume.
Team and lifecycle governance
LifeTime roles: separate IT Manager, Developer, and Operator permissions; developers should not hold production deployment rights.
Environment promotion rule: only the deployment plan moves code; hotfixes go through the same path to keep environments consistent.
Versioning: applications carry a version; modules are versioned by the application tag, so tag before every promotion.
Code review gate: a change touching a public element or a shared library requires review by the module owner plus one consumer owner.
Onboarding: new developers get read access to Production logs and write access only to their owned modules in Development.
Technical debt rule: track unused public elements and orphan modules each quarter; delete or deprecate them with a migration note.
Incident process: production issues are triaged in LifeTime, fixed in Development, promoted through QA, and closed with a post-mortem entry.
Documentation: each module records its purpose, owner, and public contract in its description; undocumented public elements are flagged at review.
Audit cadence: review user roles, integration credentials, and environment access every release cycle, not annually.
Architecture
OutSystems canonical layers: End-User, UI (screens/blocks), Logic (server/client actions), Data (entities, static entities), and Integration (REST/SOAP, BPT).
Separation rule: UI modules reference Core/Logic modules, never the reverse; cyclic module references are a build error and signal a layering violation.
Module types: Reactive Web App, Traditional Web App, Mobile App, Service (REST/SOAP exposure), Library (reusable logic), and Extension (C# integration).
Exposure decision: set 'Expose as REST/SOAP' only on Service modules; public elements form the contract, so treat them as versioned API surface.
Entity scope: entities are exposed per module; use public entities sparingly and prefer service actions to avoid tight data coupling across modules.
BPT: processes are defined in a Process entity with Human Activity, Automatic Activity, Wait, Decision, and End; process instances persist in the platform's process tables.
Client vs server action rule: client actions run in the browser and cannot hold secrets; server actions can call integrations and use server-side entities safely.
Screens should be stateless; state belongs in session variables, client variables (Local Storage), or server-side entities, not in prepared screen inputs alone.
Architecture review gate: any new module must declare its layer, its owner, and its dependency direction before the first sprint closes.
Delivery and project leadership
OutSystems delivery lifecycle: Development -> QA/Test -> Pre-Production -> Production, with each environment bound to a deployment controller (LifeTime).
Deployment rule: publish to the next environment only via a deployment plan; direct publishing across stages breaks the audit trail and rollback path.
Sprint sizing heuristic: keep a user story within one module and one developer to avoid merge conflicts in the shared module model.
Estimation anchor: count screen+action+entity surface, not screens alone; integration work usually carries the highest uncertainty and should be timeboxed first.
Definition of Done includes: unit tests for server actions, a published version in QA, and a passing impact analysis on the dependency graph.
Tagging convention: tag application versions at each promotion (e.g., v1.4.0-QA) so rollback targets are unambiguous in LifeTime.
Risk register entry rule: any external integration without a mock or contract test is a delivery risk and must be listed at sprint planning.
Team topology: assign module ownership per developer or pair; shared ownership of a single module is the most common source of lost work.
Security
Authentication: use the platform's built-in Users/Roles model or an external IdP (SAML/OIDC via the Users provider); never build a custom credential store in entities.
Authorization rule: check roles in server actions and screen-level 'Roles' property; client-side role checks are UX only and are bypassable.
Sensitive data: never place secrets, tokens, or PII in client variables, screen inputs, or client actions; they are visible in the browser.
SQL injection: use platform aggregates and parameterized SQL; dynamic SQL built by string concatenation is a finding regardless of input validation.
XSS: prefer built-in widgets and expression escaping; use the sanitize/encode built-ins when rendering raw HTML from user input.
Integration security: store API keys in the platform's secure settings or a secrets manager, not in module constants committed to the repository.
Least privilege: define roles per business function, not per screen; audit role assignments each release because role drift is a common finding.
Logging: log security-relevant events (login failures, permission denials) in server actions, and never log full tokens or passwords.
Dependency risk: third-party Forge components must be reviewed for permissions and maintained status before entering a production app.
Performance and troubleshooting
Aggregate rule: fetch only needed attributes and filter on indexed attributes; an unfiltered aggregate over a large entity is the most common performance defect.
N+1 pattern: a loop that runs an aggregate or server action per iteration should be replaced by a single aggregate plus in-memory join.
Screen performance: reduce data on initial load; use pagination or lazy loading for lists above roughly 50 rows.
Caching: use the platform cache for reference data with a defined invalidation trigger; cache without invalidation is a defect, not an optimization.
Troubleshooting path: reproduce in QA, check the error log in Service Center/LifeTime, then inspect the module's logs and the specific action's stack trace.
Timeout diagnosis: distinguish client timeout, server request timeout, and integration timeout; each has a different configuration surface.
Database health: review slow queries and index usage in the platform's monitoring before adding code-level workarounds.
Impact analysis: run it before publishing to see which modules and consumers break; it is the fastest way to catch a breaking public-element change.
Performance budget: define a target response time per screen and per API action at design time; measure in QA with realistic data volume.
Team and lifecycle governance
LifeTime roles: separate IT Manager, Developer, and Operator permissions; developers should not hold production deployment rights.
Environment promotion rule: only the deployment plan moves code; hotfixes go through the same path to keep environments consistent.
Versioning: applications carry a version; modules are versioned by the application tag, so tag before every promotion.
Code review gate: a change touching a public element or a shared library requires review by the module owner plus one consumer owner.
Onboarding: new developers get read access to Production logs and write access only to their owned modules in Development.
Technical debt rule: track unused public elements and orphan modules each quarter; delete or deprecate them with a migration note.
Incident process: production issues are triaged in LifeTime, fixed in Development, promoted through QA, and closed with a post-mortem entry.
Documentation: each module records its purpose, owner, and public contract in its description; undocumented public elements are flagged at review.
Audit cadence: review user roles, integration credentials, and environment access every release cycle, not annually.
Architecture
OutSystems canonical layers: End-User, UI (screens/blocks), Logic (server/client actions), Data (entities, static entities), and Integration (REST/SOAP, BPT).
Separation rule: UI modules reference Core/Logic modules, never the reverse; cyclic module references are a build error and signal a layering violation.
Module types: Reactive Web App, Traditional Web App, Mobile App, Service (REST/SOAP exposure), Library (reusable logic), and Extension (C# integration).
Exposure decision: set 'Expose as REST/SOAP' only on Service modules; public elements form the contract, so treat them as versioned API surface.
Entity scope: entities are exposed per module; use public entities sparingly and prefer service actions to avoid tight data coupling across modules.
BPT: processes are defined in a Process entity with Human Activity, Automatic Activity, Wait, Decision, and End; process instances persist in the platform's process tables.
Client vs server action rule: client actions run in the browser and cannot hold secrets; server actions can call integrations and use server-side entities safely.
Screens should be stateless; state belongs in session variables, client variables (Local Storage), or server-side entities, not in prepared screen inputs alone.
Architecture review gate: any new module must declare its layer, its owner, and its dependency direction before the first sprint closes.
Delivery and project leadership
OutSystems delivery lifecycle: Development -> QA/Test -> Pre-Production -> Production, with each environment bound to a deployment controller (LifeTime).
Deployment rule: publish to the next environment only via a deployment plan; direct publishing across stages breaks the audit trail and rollback path.
Sprint sizing heuristic: keep a user story within one module and one developer to avoid merge conflicts in the shared module model.
Estimation anchor: count screen+action+entity surface, not screens alone; integration work usually carries the highest uncertainty and should be timeboxed first.
Definition of Done includes: unit tests for server actions, a published version in QA, and a passing impact analysis on the dependency graph.
Tagging convention: tag application versions at each promotion (e.g., v1.4.0-QA) so rollback targets are unambiguous in LifeTime.
Risk register entry rule: any external integration without a mock or contract test is a delivery risk and must be listed at sprint planning.
Team topology: assign module ownership per developer or pair; shared ownership of a single module is the most common source of lost work.
Security
Authentication: use the platform's built-in Users/Roles model or an external IdP (SAML/OIDC via the Users provider); never build a custom credential store in entities.
Authorization rule: check roles in server actions and screen-level 'Roles' property; client-side role checks are UX only and are bypassable.
Sensitive data: never place secrets, tokens, or PII in client variables, screen inputs, or client actions; they are visible in the browser.
SQL injection: use platform aggregates and parameterized SQL; dynamic SQL built by string concatenation is a finding regardless of input validation.
XSS: prefer built-in widgets and expression escaping; use the sanitize/encode built-ins when rendering raw HTML from user input.
Integration security: store API keys in the platform's secure settings or a secrets manager, not in module constants committed to the repository.
Least privilege: define roles per business function, not per screen; audit role assignments each release because role drift is a common finding.
Logging: log security-relevant events (login failures, permission denials) in server actions, and never log full tokens or passwords.
Dependency risk: third-party Forge components must be reviewed for permissions and maintained status before entering a production app.
Performance and troubleshooting
Aggregate rule: fetch only needed attributes and filter on indexed attributes; an unfiltered aggregate over a large entity is the most common performance defect.
N+1 pattern: a loop that runs an aggregate or server action per iteration should be replaced by a single aggregate plus in-memory join.
Screen performance: reduce data on initial load; use pagination or lazy loading for lists above roughly 50 rows.
Caching: use the platform cache for reference data with a defined invalidation trigger; cache without invalidation is a defect, not an optimization.
Troubleshooting path: reproduce in QA, check the error log in Service Center/LifeTime, then inspect the module's logs and the specific action's stack trace.
Timeout diagnosis: distinguish client timeout, server request timeout, and integration timeout; each has a different configuration surface.
Database health: review slow queries and index usage in the platform's monitoring before adding code-level workarounds.
Impact analysis: run it before publishing to see which modules and consumers break; it is the fastest way to catch a breaking public-element change.
Performance budget: define a target response time per screen and per API action at design time; measure in QA with realistic data volume.
Team and lifecycle governance
LifeTime roles: separate IT Manager, Developer, and Operator permissions; developers should not hold production deployment rights.
Environment promotion rule: only the deployment plan moves code; hotfixes go through the same path to keep environments consistent.
Versioning: applications carry a version; modules are versioned by the application tag, so tag before every promotion.
Code review gate: a change touching a public element or a shared library requires review by the module owner plus one consumer owner.
Onboarding: new developers get read access to Production logs and write access only to their owned modules in Development.
Technical debt rule: track unused public elements and orphan modules each quarter; delete or deprecate them with a migration note.
Incident process: production issues are triaged in LifeTime, fixed in Development, promoted through QA, and closed with a post-mortem entry.
Documentation: each module records its purpose, owner, and public contract in its description; undocumented public elements are flagged at review.
Audit cadence: review user roles, integration credentials, and environment access every release cycle, not annually.
Architecture
OutSystems canonical layers: End-User, UI (screens/blocks), Logic (server/client actions), Data (entities, static entities), and Integration (REST/SOAP, BPT).
Separation rule: UI modules reference Core/Logic modules, never the reverse; cyclic module references are a build error and signal a layering violation.
Module types: Reactive Web App, Traditional Web App, Mobile App, Service (REST/SOAP exposure), Library (reusable logic), and Extension (C# integration).
Exposure decision: set 'Expose as REST/SOAP' only on Service modules; public elements form the contract, so treat them as versioned API surface.
Entity scope: entities are exposed per module; use public entities sparingly and prefer service actions to avoid tight data coupling across modules.
BPT: processes are defined in a Process entity with Human Activity, Automatic Activity, Wait, Decision, and End; process instances persist in the platform's process tables.
Client vs server action rule: client actions run in the browser and cannot hold secrets; server actions can call integrations and use server-side entities safely.
Screens should be stateless; state belongs in session variables, client variables (Local Storage), or server-side entities, not in prepared screen inputs alone.
Architecture review gate: any new module must declare its layer, its owner, and its dependency direction before the first sprint closes.
Delivery and project leadership
OutSystems delivery lifecycle: Development -> QA/Test -> Pre-Production -> Production, with each environment bound to a deployment controller (LifeTime).
Deployment rule: publish to the next environment only via a deployment plan; direct publishing across stages breaks the audit trail and rollback path.
Sprint sizing heuristic: keep a user story within one module and one developer to avoid merge conflicts in the shared module model.
Estimation anchor: count screen+action+entity surface, not screens alone; integration work usually carries the highest uncertainty and should be timeboxed first.
Definition of Done includes: unit tests for server actions, a published version in QA, and a passing impact analysis on the dependency graph.
Tagging convention: tag application versions at each promotion (e.g., v1.4.0-QA) so rollback targets are unambiguous in LifeTime.
Risk register entry rule: any external integration without a mock or contract test is a delivery risk and must be listed at sprint planning.
Team topology: assign module ownership per developer or pair; shared ownership of a single module is the most common source of lost work.
Security
Authentication: use the platform's built-in Users/Roles model or an external IdP (SAML/OIDC via the Users provider); never build a custom credential store in entities.
Authorization rule: check roles in server actions and screen-level 'Roles' property; client-side role checks are UX only and are bypassable.
Sensitive data: never place secrets, tokens, or PII in client variables, screen inputs, or client actions; they are visible in the browser.
SQL injection: use platform aggregates and parameterized SQL; dynamic SQL built by string concatenation is a finding regardless of input validation.
XSS: prefer built-in widgets and expression escaping; use the sanitize/encode built-ins when rendering raw HTML from user input.
Integration security: store API keys in the platform's secure settings or a secrets manager, not in module constants committed to the repository.
Least privilege: define roles per business function, not per screen; audit role assignments each release because role drift is a common finding.
Logging: log security-relevant events (login failures, permission denials) in server actions, and never log full tokens or passwords.
Dependency risk: third-party Forge components must be reviewed for permissions and maintained status before entering a production app.
Performance and troubleshooting
Aggregate rule: fetch only needed attributes and filter on indexed attributes; an unfiltered aggregate over a large entity is the most common performance defect.
N+1 pattern: a loop that runs an aggregate or server action per iteration should be replaced by a single aggregate plus in-memory join.
Screen performance: reduce data on initial load; use pagination or lazy loading for lists above roughly 50 rows.
Caching: use the platform cache for reference data with a defined invalidation trigger; cache without invalidation is a defect, not an optimization.
Troubleshooting path: reproduce in QA, check the error log in Service Center/LifeTime, then inspect the module's logs and the specific action's stack trace.
Timeout diagnosis: distinguish client timeout, server request timeout, and integration timeout; each has a different configuration surface.
Database health: review slow queries and index usage in the platform's monitoring before adding code-level workarounds.
Impact analysis: run it before publishing to see which modules and consumers break; it is the fastest way to catch a breaking public-element change.
Performance budget: define a target response time per screen and per API action at design time; measure in QA with realistic data volume.
Team and lifecycle governance
LifeTime roles: separate IT Manager, Developer, and Operator permissions; developers should not hold production deployment rights.
Environment promotion rule: only the deployment plan moves code; hotfixes go through the same path to keep environments consistent.
Versioning: applications carry a version; modules are versioned by the application tag, so tag before every promotion.
Code review gate: a change touching a public element or a shared library requires review by the module owner plus one consumer owner.
Onboarding: new developers get read access to Production logs and write access only to their owned modules in Development.
Technical debt rule: track unused public elements and orphan modules each quarter; delete or deprecate them with a migration note.
Incident process: production issues are triaged in LifeTime, fixed in Development, promoted through QA, and closed with a post-mortem entry.
Documentation: each module records its purpose, owner, and public contract in its description; undocumented public elements are flagged at review.
Audit cadence: review user roles, integration credentials, and environment access every release cycle, not annually.
Architecture
OutSystems canonical layers: End-User, UI (screens/blocks), Logic (server/client actions), Data (entities, static entities), and Integration (REST/SOAP, BPT).
Separation rule: UI modules reference Core/Logic modules, never the reverse; cyclic module references are a build error and signal a layering violation.
Module types: Reactive Web App, Traditional Web App, Mobile App, Service (REST/SOAP exposure), Library (reusable logic), and Extension (C# integration).
Exposure decision: set 'Expose as REST/SOAP' only on Service modules; public elements form the contract, so treat them as versioned API surface.
Entity scope: entities are exposed per module; use public entities sparingly and prefer service actions to avoid tight data coupling across modules.
BPT: processes are defined in a Process entity with Human Activity, Automatic Activity, Wait, Decision, and End; process instances persist in the platform's process tables.
Client vs server action rule: client actions run in the browser and cannot hold secrets; server actions can call integrations and use server-side entities safely.
Screens should be stateless; state belongs in session variables, client variables (Local Storage), or server-side entities, not in prepared screen inputs alone.
Architecture review gate: any new module must declare its layer, its owner, and its dependency direction before the first sprint closes.
Delivery and project leadership
OutSystems delivery lifecycle: Development -> QA/Test -> Pre-Production -> Production, with each environment bound to a deployment controller (LifeTime).
Deployment rule: publish to the next environment only via a deployment plan; direct publishing across stages breaks the audit trail and rollback path.
Sprint sizing heuristic: keep a user story within one module and one developer to avoid merge conflicts in the shared module model.
Estimation anchor: count screen+action+entity surface, not screens alone; integration work usually carries the highest uncertainty and should be timeboxed first.
Definition of Done includes: unit tests for server actions, a published version in QA, and a passing impact analysis on the dependency graph.
Tagging convention: tag application versions at each promotion (e.g., v1.4.0-QA) so rollback targets are unambiguous in LifeTime.
Risk register entry rule: any external integration without a mock or contract test is a delivery risk and must be listed at sprint planning.
Team topology: assign module ownership per developer or pair; shared ownership of a single module is the most common source of lost work.
Security
Authentication: use the platform's built-in Users/Roles model or an external IdP (SAML/OIDC via the Users provider); never build a custom credential store in entities.
Authorization rule: check roles in server actions and screen-level 'Roles' property; client-side role checks are UX only and are bypassable.
Sensitive data: never place secrets, tokens, or PII in client variables, screen inputs, or client actions; they are visible in the browser.
SQL injection: use platform aggregates and parameterized SQL; dynamic SQL built by string concatenation is a finding regardless of input validation.
XSS: prefer built-in widgets and expression escaping; use the sanitize/encode built-ins when rendering raw HTML from user input.
Integration security: store API keys in the platform's secure settings or a secrets manager, not in module constants committed to the repository.
Least privilege: define roles per business function, not per screen; audit role assignments each release because role drift is a common finding.
Logging: log security-relevant events (login failures, permission denials) in server actions, and never log full tokens or passwords.
Dependency risk: third-party Forge components must be reviewed for permissions and maintained status before entering a production app.
Performance and troubleshooting
Aggregate rule: fetch only needed attributes and filter on indexed attributes; an unfiltered aggregate over a large entity is the most common performance defect.
N+1 pattern: a loop that runs an aggregate or server action per iteration should be replaced by a single aggregate plus in-memory join.
Screen performance: reduce data on initial load; use pagination or lazy loading for lists above roughly 50 rows.
Caching: use the platform cache for reference data with a defined invalidation trigger; cache without invalidation is a defect, not an optimization.
Troubleshooting path: reproduce in QA, check the error log in Service Center/LifeTime, then inspect the module's logs and the specific action's stack trace.
Timeout diagnosis: distinguish client timeout, server request timeout, and integration timeout; each has a different configuration surface.
Database health: review slow queries and index usage in the platform's monitoring before adding code-level workarounds.
Impact analysis: run it before publishing to see which modules and consumers break; it is the fastest way to catch a breaking public-element change.
Performance budget: define a target response time per screen and per API action at design time; measure in QA with realistic data volume.
Team and lifecycle governance
LifeTime roles: separate IT Manager, Developer, and Operator permissions; developers should not hold production deployment rights.
Environment promotion rule: only the deployment plan moves code; hotfixes go through the same path to keep environments consistent.
Versioning: applications carry a version; modules are versioned by the application tag, so tag before every promotion.
Code review gate: a change touching a public element or a shared library requires review by the module owner plus one consumer owner.
Onboarding: new developers get read access to Production logs and write access only to their owned modules in Development.
Technical debt rule: track unused public elements and orphan modules each quarter; delete or deprecate them with a migration note.
Incident process: production issues are triaged in LifeTime, fixed in Development, promoted through QA, and closed with a post-mortem entry.
Documentation: each module records its purpose, owner, and public contract in its description; undocumented public elements are flagged at review.
Audit cadence: review user roles, integration credentials, and environment access every release cycle, not annually.
Architecture
OutSystems canonical layers: End-User, UI (screens/blocks), Logic (server/client actions), Data (entities, static entities), and Integration (REST/SOAP, BPT).
Separation rule: UI modules reference Core/Logic modules, never the reverse; cyclic module references are a build error and signal a layering violation.
Module types: Reactive Web App, Traditional Web App, Mobile App, Service (REST/SOAP exposure), Library (reusable logic), and Extension (C# integration).
Exposure decision: set 'Expose as REST/SOAP' only on Service modules; public elements form the contract, so treat them as versioned API surface.
Entity scope: entities are exposed per module; use public entities sparingly and prefer service actions to avoid tight data coupling across modules.
BPT: processes are defined in a Process entity with Human Activity, Automatic Activity, Wait, Decision, and End; process instances persist in the platform's process tables.
Client vs server action rule: client actions run in the browser and cannot hold secrets; server actions can call integrations and use server-side entities safely.
Screens should be stateless; state belongs in session variables, client variables (Local Storage), or server-side entities, not in prepared screen inputs alone.
Architecture review gate: any new module must declare its layer, its owner, and its dependency direction before the first sprint closes.
Delivery and project leadership
OutSystems delivery lifecycle: Development -> QA/Test -> Pre-Production -> Production, with each environment bound to a deployment controller (LifeTime).
Deployment rule: publish to the next environment only via a deployment plan; direct publishing across stages breaks the audit trail and rollback path.
Sprint sizing heuristic: keep a user story within one module and one developer to avoid merge conflicts in the shared module model.
Estimation anchor: count screen+action+entity surface, not screens alone; integration work usually carries the highest uncertainty and should be timeboxed first.
Definition of Done includes: unit tests for server actions, a published version in QA, and a passing impact analysis on the dependency graph.
Tagging convention: tag application versions at each promotion (e.g., v1.4.0-QA) so rollback targets are unambiguous in LifeTime.
Risk register entry rule: any external integration without a mock or contract test is a delivery risk and must be listed at sprint planning.
Team topology: assign module ownership per developer or pair; shared ownership of a single module is the most common source of lost work.
Security
Authentication: use the platform's built-in Users/Roles model or an external IdP (SAML/OIDC via the Users provider); never build a custom credential store in entities.
Authorization rule: check roles in server actions and screen-level 'Roles' property; client-side role checks are UX only and are bypassable.
Sensitive data: never place secrets, tokens, or PII in client variables, screen inputs, or client actions; they are visible in the browser.
SQL injection: use platform aggregates and parameterized SQL; dynamic SQL built by string concatenation is a finding regardless of input validation.
XSS: prefer built-in widgets and expression escaping; use the sanitize/encode built-ins when rendering raw HTML from user input.
Integration security: store API keys in the platform's secure settings or a secrets manager, not in module constants committed to the repository.
Least privilege: define roles per business function, not per screen; audit role assignments each release because role drift is a common finding.
Logging: log security-relevant events (login failures, permission denials) in server actions, and never log full tokens or passwords.
Dependency risk: third-party Forge components must be reviewed for permissions and maintained status before entering a production app.
Performance and troubleshooting
Aggregate rule: fetch only needed attributes and filter on indexed attributes; an unfiltered aggregate over a large entity is the most common performance defect.
N+1 pattern: a loop that runs an aggregate or server action per iteration should be replaced by a single aggregate plus in-memory join.
Screen performance: reduce data on initial load; use pagination or lazy loading for lists above roughly 50 rows.
Caching: use the platform cache for reference data with a defined invalidation trigger; cache without invalidation is a defect, not an optimization.
Troubleshooting path: reproduce in QA, check the error log in Service Center/LifeTime, then inspect the module's logs and the specific action's stack trace.
Timeout diagnosis: distinguish client timeout, server request timeout, and integration timeout; each has a different configuration surface.
Database health: review slow queries and index usage in the platform's monitoring before adding code-level workarounds.
Impact analysis: run it before publishing to see which modules and consumers break; it is the fastest way to catch a breaking public-element change.
Performance budget: define a target response time per screen and per API action at design time; measure in QA with realistic data volume.
Team and lifecycle governance
LifeTime roles: separate IT Manager, Developer, and Operator permissions; developers should not hold production deployment rights.
Environment promotion rule: only the deployment plan moves code; hotfixes go through the same path to keep environments consistent.
Versioning: applications carry a version; modules are versioned by the application tag, so tag before every promotion.
Code review gate: a change touching a public element or a shared library requires review by the module owner plus one consumer owner.
Onboarding: new developers get read access to Production logs and write access only to their owned modules in Development.
Technical debt rule: track unused public elements and orphan modules each quarter; delete or deprecate them with a migration note.
Incident process: production issues are triaged in LifeTime, fixed in Development, promoted through QA, and closed with a post-mortem entry.
Documentation: each module records its purpose, owner, and public contract in its description; undocumented public elements are flagged at review.
Audit cadence: review user roles, integration credentials, and environment access every release cycle, not annually.
Architecture
OutSystems canonical layers: End-User, UI (screens/blocks), Logic (server/client actions), Data (entities, static entities), and Integration (REST/SOAP, BPT).
Separation rule: UI modules reference Core/Logic modules, never the reverse; cyclic module references are a build error and signal a layering violation.
Module types: Reactive Web App, Traditional Web App, Mobile App, Service (REST/SOAP exposure), Library (reusable logic), and Extension (C# integration).
Exposure decision: set 'Expose as REST/SOAP' only on Service modules; public elements form the contract, so treat them as versioned API surface.
Entity scope: entities are exposed per module; use public entities sparingly and prefer service actions to avoid tight data coupling across modules.
BPT: processes are defined in a Process entity with Human Activity, Automatic Activity, Wait, Decision, and End; process instances persist in the platform's process tables.
Client vs server action rule: client actions run in the browser and cannot hold secrets; server actions can call integrations and use server-side entities safely.
Screens should be stateless; state belongs in session variables, client variables (Local Storage), or server-side entities, not in prepared screen inputs alone.
Architecture review gate: any new module must declare its layer, its owner, and its dependency direction before the first sprint closes.
Delivery and project leadership
OutSystems delivery lifecycle: Development -> QA/Test -> Pre-Production -> Production, with each environment bound to a deployment controller (LifeTime).
Deployment rule: publish to the next environment only via a deployment plan; direct publishing across stages breaks the audit trail and rollback path.
Sprint sizing heuristic: keep a user story within one module and one developer to avoid merge conflicts in the shared module model.
Estimation anchor: count screen+action+entity surface, not screens alone; integration work usually carries the highest uncertainty and should be timeboxed first.
Definition of Done includes: unit tests for server actions, a published version in QA, and a passing impact analysis on the dependency graph.
Tagging convention: tag application versions at each promotion (e.g., v1.4.0-QA) so rollback targets are unambiguous in LifeTime.
Risk register entry rule: any external integration without a mock or contract test is a delivery risk and must be listed at sprint planning.
Team topology: assign module ownership per developer or pair; shared ownership of a single module is the most common source of lost work.
Security
Authentication: use the platform's built-in Users/Roles model or an external IdP (SAML/OIDC via the Users provider); never build a custom credential store in entities.
Authorization rule: check roles in server actions and screen-level 'Roles' property; client-side role checks are UX only and are bypassable.
Sensitive data: never place secrets, tokens, or PII in client variables, screen inputs, or client actions; they are visible in the browser.
SQL injection: use platform aggregates and parameterized SQL; dynamic SQL built by string concatenation is a finding regardless of input validation.
XSS: prefer built-in widgets and expression escaping; use the sanitize/encode built-ins when rendering raw HTML from user input.
Integration security: store API keys in the platform's secure settings or a secrets manager, not in module constants committed to the repository.
Least privilege: define roles per business function, not per screen; audit role assignments each release because role drift is a common finding.
Logging: log security-relevant events (login failures, permission denials) in server actions, and never log full tokens or passwords.
Dependency risk: third-party Forge components must be reviewed for permissions and maintained status before entering a production app.
Performance and troubleshooting
Aggregate rule: fetch only needed attributes and filter on indexed attributes; an unfiltered aggregate over a large entity is the most common performance defect.
N+1 pattern: a loop that runs an aggregate or server action per iteration should be replaced by a single aggregate plus in-memory join.
Screen performance: reduce data on initial load; use pagination or lazy loading for lists above roughly 50 rows.
Caching: use the platform cache for reference data with a defined invalidation trigger; cache without invalidation is a defect, not an optimization.
Troubleshooting path: reproduce in QA, check the error log in Service Center/LifeTime, then inspect the module's logs and the specific action's stack trace.
Timeout diagnosis: distinguish client timeout, server request timeout, and integration timeout; each has a different configuration surface.
Database health: review slow queries and index usage in the platform's monitoring before adding code-level workarounds.
Impact analysis: run it before publishing to see which modules and consumers break; it is the fastest way to catch a breaking public-element change.
Performance budget: define a target response time per screen and per API action at design time; measure in QA with realistic data volume.
Team and lifecycle governance
LifeTime roles: separate IT Manager, Developer, and Operator permissions; developers should not hold production deployment rights.
Environment promotion rule: only the deployment plan moves code; hotfixes go through the same path to keep environments consistent.
Versioning: applications carry a version; modules are versioned by the application tag, so tag before every promotion.
Code review gate: a change touching a public element or a shared library requires review by the module owner plus one consumer owner.
Onboarding: new developers get read access to Production logs and write access only to their owned modules in Development.
Technical debt rule: track unused public elements and orphan modules each quarter; delete or deprecate them with a migration note.
Incident process: production issues are triaged in LifeTime, fixed in Development, promoted through QA, and closed with a post-mortem entry.
Documentation: each module records its purpose, owner, and public contract in its description; undocumented public elements are flagged at review.
Audit cadence: review user roles, integration credentials, and environment access every release cycle, not annually.