Dynamic Logic Expressions

Edited

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 IF or CASE)

  • 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 name

Using 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, days

  • m, month, months

  • y, 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" recognised

  • Non-empty string → TRUE

  • 0 → FALSE

  • NULL → FALSE


11. Examples

Goal

Expression

Result

Pass / Fail

IF([Prop.Score]>=50,"Pass","Fail")

Pass

Risk label

[Tier] + "-" + [_RiskLevel]

Gold-Medium

Duration (days)

DIFF([_StartDate],[_EndDate],'day')

5

Duration category

CASE(ABS(DIFF([_StartDate],[_EndDate],'day'))<30,"Short",ABS(DIFF([_StartDate],[_EndDate],'day'))<90,"Medium","Long")

Medium

Year span

IF(ABS(DIFF([_StartDate],[_EndDate],'year'))=0,"Single","Multi")

Single

Fallback tier

COALESCE([Missing],[Tier],"Unknown")

Gold


12. Example Expressions

Use Case

Expression

Rank logic

CASE(

[Severity] = "Critical" AND [Status] <> "Closed", "P1",

[Severity] = "High", "P2",

[Severity] = "Medium", "P3",

"P4")

Automatic flag

IF([Budget] > 0 AND ([Phase] = "Build" OR [Phase] = "Test"), TRUE, FALSE)

Fallback label

COALESCE([ShortName], [DisplayName], "Unnamed")

Concatenate text

"RAG=" + [RAG] + ", Score=" + [Score]


13. Validation Checklist

  1. Parentheses are balanced.

  2. IF has exactly three arguments.

  3. CASE has an odd number of arguments (last one is default).

  4. Field names are wrapped in [ ].

  5. 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 CASE for complex multi-condition logic.

Was this article helpful?

Sorry about that! Care to tell us more?

Thanks for the feedback!

There was an issue submitting your feedback
Please check your connection and try again.