Dynamic Logic Expressions
This guide explains how to write and use expressions with Fluid’s Dynamic Logic engine.
Dynamic Logic expressions are used across Fluid to calculate values, evaluate rules, and drive behaviour such as governance checks, calculated fields, automation, and conditional logic.
This article explains the core concepts, syntax, and common patterns, with practical examples you can reuse.
1. What Is a Dynamic Logic Expression?
A Dynamic Logic expression is a small formula that is evaluated against a context, i.e. a set of named fields and values available at the point the expression runs.
Expressions are used throughout Fluid to calculate values, evaluate rules, and drive conditional behaviour. They are commonly used to:
Calculate the value of custom properties (for example, deriving a score, status, or category in a form)
Apply business rules and governance logic based on project or card data
Categorise data using thresholds or conditions (for example, risk bands or duration buckets)
Control outcomes in automation and workflow logic
Derive values from dates, numbers, and text fields without manual updates
An expression can:
Compare values
Combine text
Perform arithmetic
Make decisions (using
IForCASE)Work with dates (for example, durations and categorisation)
Expressions are evaluated dynamically, meaning the result updates automatically whenever the underlying data changes.
2. Referencing Fields
Field values are referenced using square brackets:
[FieldName]Field names are case-insensitive.
Expressions can reference both first class properties (such as project attributes) and custom properties. Custom properties are exposed using multiple reference formats.
For a custom property with the key Score and the label Priority Score, the following references are available:
[Score] // key name[Priority Score] // label name[_Score] //Underscore alias to the key nameUsing the underscore alias (for example [_Score]) is recommended because it:
Makes it immediately clear that the value comes from a custom property
Reduces the risk of name clashes with standard (first-class) fields
Improves readability in longer or more complex expressions
Where there is any risk of ambiguity or conflict, the underscore alias provides the most predictable and maintainable reference.
3. Operators
Arithmetic
+ - * / %
Comparison
= <> < <= > >=
Logical
AND OR NOT
Grouping
( ... )
Strings
String concatenation uses +.
If either side is non-numeric, the result is treated as text.
[Tier] + "-" + [_RiskLevel]
4. Decision Functions: IF and CASE
IF
Use IF for a single yes/no decision.
IF(condition, whenTrue, whenFalse)
Example:
IF([Tier] = "Gold", "Premium", "Standard")
CASE
Use CASE when you have multiple ordered conditions.
CASE(
condition1, result1,
condition2, result2,
...
defaultResult
)
Example:
CASE(
[Prop.Score] >= 90, "Gold",
[Prop.Score] >= 80, "Silver",
"Bronze"
)
Conditions are evaluated top-to-bottom, and the first match is returned.
5. COALESCE – First Non-Null
COALESCE returns the first non-null and non-empty value.
COALESCE([Missing], [Tier], "Unknown")
This is the recommended way to handle optional fields safely.
6. Text Matching Functions (CONTAINS, STARTSWITH, ENDSWITH)
CONTAINS
Determines whether a given search term (term) appears in a target string (value). Returns a boolean (TRUE / FALSE).
Syntax:
CONTAINS(value, term, {case insensitive}, {whole word match})
• value (required): The text to search.
• term (required): The search term to look for.
• case insensitive (optional, default TRUE): If TRUE, comparison is case-insensitive. If FALSE, comparison is case-sensitive.
• whole word match (optional, default FALSE): If TRUE, only whole word matches are considered. if term = 'here', then value 'are you here' will match, but term 'are you there' will NOT
Example:
CONTAINS([Value], "risk") -- TRUE if 'risk' appears (case-insensitive by default)
CONTAINS([Value], "Risk", FALSE) -- Case-sensitive; TRUE only if exact 'Risk' occurs
CONTAINS([Value], "risk", TRUE, TRUE) -- Whole word search (e.g. 'risk' not 'risky')
CONTAINS("The quick brown fox", "quick", TRUE) -- returns TRUE
CONTAINS("Thequickbrownfox", "quick", TRUE, TRUE) -- returns FALSE (not a whole word)
CONTAINS([Title], "", TRUE) -- returns FALSE (empty needle)
CONTAINS(NULL, "any") -- returns FALSE
STARTSWITH
Determines whether a target string (value) begins with a specified search term (term). Returns a boolean (TRUE / FALSE), TRUE only if the match occurs at the very start (position 0).
Syntax:
STARTSWITH(value, term, {case insensitive}, {whole word match})
• value (required): The text to search.
• term (required): The search term to look for.
• case insensitive (optional, default TRUE): If TRUE, comparison is case-insensitive. If FALSE, comparison is case-sensitive.
• whole word match (optional, default FALSE): If TRUE, only whole word matches are considered. if term = 'are', then value 'are you here' will match, but term 'aren't you here' will NOT
Example:
STARTSWITH([Value], "proj") -- TRUE if Title starts with "proj" (case-insensitive)
STARTSWITH([Value], "Proj", FALSE) -- Case-sensitive; TRUE only if exact "Proj" case
STARTSWITH("Project Alpha", "Project", TRUE, TRUE) -- TRUE (word boundary)
STARTSWITH("ProjectAlpha", "Project", TRUE, TRUE) -- FALSE (no word boundary)
STARTSWITH("Header", "Head", TRUE, TRUE) -- FALSE ('e' - not a word boundary)
STARTSWITH([Code], "") -- FALSE (empty needle)
STARTSWITH(NULL, "Any") -- FALSE
ENDSWITH
Determines whether a target string (value) ends with a specified search term (term). Returns a boolean (TRUE / FALSE), TRUE only if the match occurs at the very end.
Syntax:
ENDSWITH(value, term, {case insensitive}, {whole word match})
• value (required): The text to search.
• term (required): The search term to look for.
• case insensitive (optional, default TRUE): If TRUE, comparison is case-insensitive. If FALSE, comparison is case-sensitive.
• whole word match (optional, default FALSE): If TRUE, only whole word matches are considered. if term = 'here', then value 'are you here' will match, but term 'are you there' will NOT
Example:
ENDSWITH([Value], "status") -- TRUE if Title ends with "status" (case-insensitive)
ENDSWITH([Value], "Status", FALSE) -- Case-sensitive; TRUE only if exact "Status"
ENDSWITH("Task done", "done", TRUE, TRUE) -- TRUE (word boundary)
ENDSWITH("Taskdone", "done", TRUE, TRUE) -- FALSE (no word boundary)
ENDSWITH("pretestpost", "test", FALSE, TRUE) -- FALSE (no word boundary)
ENDSWITH([Note], "", TRUE) -- FALSE (empty needle)
ENDSWITH(NULL, "anything") -- FALSE
7. Working with Dates
DIFF
Calculates the difference between two dates.
DIFF(start, end, unit)
Supported units:
d,day,daysm,month,monthsy,year,years
Examples:
DIFF([_StartDate], [_EndDate], 'day')
DIFF([_StartDate], [_EndDate], 'month')
Dates are evaluated internally using epoch milliseconds.
ABS
Returns the absolute value.
ABS(DIFF([_StartDate], [_EndDate], 'day'))
Useful when date order is uncertain.
Negative differences
IF(
DIFF([_EndDate], [_StartDate], 'day') < 0,
"Reversed",
"Forward"
)
8. Combining Text and Values
[Tier] + "-" + [_RiskLevel]
Result:
Gold-Medium
9. Numeric Logic Patterns
Simple threshold:
IF([Prop.Score] >= 85, "Pass", "Review")
Tiered categorisation:
CASE(
[Prop.Score] >= 90, "Gold",
[Prop.Score] >= 80, "Silver",
"Bronze"
)
10. Boolean Logic Patterns
([IsProgram] AND NOT [IsAutoSchedule]) OR ([Tier] = "Gold")
Boolean coercion rules:
"true"/"false"recognisedNon-empty string → TRUE
0→ FALSENULL → FALSE
11. Examples
Goal | Expression | Result |
|---|---|---|
Pass / Fail |
| Pass |
Risk label |
| Gold-Medium |
Duration (days) |
| 5 |
Duration category |
| Medium |
Year span |
| Single |
Fallback tier |
| Gold |
12. Example Expressions
Use Case | Expression |
Rank logic |
|
Automatic flag |
|
Fallback label |
|
Concatenate text |
|
13. Validation Checklist
Parentheses are balanced.
IFhas exactly three arguments.CASEhas an odd number of arguments (last one is default).Field names are wrapped in
[ ].Strings use quotes (
"text"or'text').
14. Common Errors
Error Message | Likely Cause |
Expression cannot be empty | No content in the expression field |
CASE requires at least three arguments | Missing default value |
Expected identifier | Misspelt function or missing comma |
Division by zero | Numeric denominator is 0 |
Maximum expression depth exceeded | Too many nested IFs (use CASE instead) |
15. Tips
Use uppercase for functions (IF, CASE, COALESCE).
Indent multi-line expressions for readability.
Order comparisons from highest to lowest thresholds.
Prefer
CASEfor complex multi-condition logic.
