Raised with Developer Support as ECOHELP-151866.
Posting here because this took days to track down and I would rather nobody else spent them.
Writing a value to a jira:customFieldType field takes 25–45 seconds instead of sub-second, if the module’s formatter.expression or validation.expression references configuration.
Why this needed a second app to find
Our saves had been slow for a long time and everything we suspected turned out to be innocent.
First we blamed our own code. The write goes through a resolver, an async queue and a consumer, so we added a button that called a bare view.submit with a single numeric property — no resolver, no queue, no REST calls of ours anywhere. Still ~34 s. Then we suspected the bridge, and measured the
same write through view.submit, through requestJira, and through a plain browser fetch fromthe DevTools console. All three identical. Then the issue itself — but writing summary on that very same issue took 650 ms.
At that point every remaining suspect lived in the manifest, and a shipped app has far too many of them to test by reasoning. Twenty modules, eight scopes, triggers, entity properties, licensing, two custom field types. So we built a throwaway app with one module and nothing else, confirmed it was fast, and then added the real field’s features to it one at a time, redeploying and re-measuring after each.
Each step below changes exactly one thing.
Step 1 — object field, six properties (three number, three string), each with a
searchAlias. Expressions that do not touch configuration:
validation:
expression: |
value == null || (value.amountA == null || value.amountA >= 0)
formatter:
expression: |
value == null ? "" : (value.labelA != null ? value.labelA : "-")
export: true
→ 634 / 697 / 667 ms
Step 2 — add contextConfig to the module. Nothing else changed.
→ 645 / 728 / 828 ms. No change. Making the field context-configurable is free.
Step 3 — read configuration from the expressions. Schema, searchAlias, contextConfig, issue and transport all identical to step 2:
validation:
expression: |
let cfg = configuration != null ? configuration : {};
let max = cfg.maxAmount != null ? cfg.maxAmount : 100000;
value == null || (value.amountA == null || (value.amountA >= 0 && value.amountA <= max))
formatter:
expression: |
let cfg = configuration != null ? configuration : {};
let unit = cfg.unit != null ? cfg.unit : "u";
value == null ? "" : (value.amountA != null ? value.amountA + unit : "")
export: true
→ 24 867 / 32 393 ms
That is the whole finding. One reference, 40×.
No configuration was ever saved for the field’s context, so both expressions fall through to their defaults. The field is on one project. Every slow response is HTTP 204 on the first attempt — no errors, no retries, no rate limiting, just a successful write that takes half a minute. Variance is around ±25% run to run, which argues against a fixed timeout.
The same in the shipped app
| write | result |
|---|---|
| our field, single property | 25.7 – 31.6 s |
| a second, independent field of the same type | 24.3 – 30.3 s |
summary (native Jira field), same issue |
544 – 724 ms |
A simpler field of ours with two properties, referencing configuration in the formatter but not in the validation, sits at 11–14 s — about half. Soft evidence that both expressions contribute.
Things we eliminated along the way, each by measurement rather than argument: our backend code · transport · the issue and project · issue properties and jira:entityProperty extraction · app.licensing.enabled · contextConfig on its own · searchAlias and JQL indexing (removing all six cut 34 s to 24–29 s, so real but only ~25%) · number of schema properties · a single corrupted field instance.
The detail that stings
Our validation started with:
let cfg = configuration != null ? configuration : {};
…and never read cfg on any code path. Left over from an earlier version of the expression.
A no-op reference to a documented expression variable, costing 40× on every write, is not something an app author can discover — and I could not find it mentioned anywhere in the docs.
The fix
We removed every configuration reference from the expressions.
The validation reference was dead code and simply went away. The formatter now reads display strings that our app already stores inside the field value, having formatted them with that same configuration at write time — the configuration is applied once, when writing, instead of on every read and write by the platform.
Saves went from 25–45 s to XXX ms. Same field, same issue, same everything else.
The cost is that one configuration option, which only the formatter ever honoured, no longer has any effect; we would have to bake it into the stored string instead. And an app whose rendering genuinely depends on per-context configuration has no equivalent escape — which is why this is also open as ECOHELP-151866 rather than just a note to self.