Skip to main content

ExcessiveContextDataRule — full reference

Looking for the short version?

See the user guide. This page is the complete technical reference — every constructor argument, every detection algorithm, every violation message format. Read it when you're tuning thresholds, debugging a violation, or planning a data-migration refactor.

Purpose

Custom properties on a Perspective view are meant to hold configuration, not data. This rule flags views that have crossed that line — large arrays, sprawling sibling structures, deep nesting, or huge overall data volume embedded directly in view.json. Datasets that large belong in a database (named queries), the tag historian, or are fetched at runtime via gateway scripts and message handlers.

Severity

error by default — excessive data in view JSON causes real performance and memory problems, so the rule errs on the side of blocking CI. Configurable via the severity option.

What it checks

The rule operates on the flattened JSON representation of the view and runs five independent detection methods, each scoped to keys that start with custom.:

MethodWhat it detectsDefault threshold
Array sizeArrays under custom.* with too many itemsmax_array_size = 50
Property breadthToo many sibling properties under any custom.* parentmax_sibling_properties = 50
Nesting depthCustom property paths nested deeper than the thresholdmax_nesting_depth = 5
Total data pointsTotal count of all flattened paths under custom.*max_data_points = 1000
Value lengthA single string value under custom.* that is too longmax_value_length = 10000

The first four checks measure the structure of the custom-property tree; the fifth measures the size of individual values, catching payloads that flatten to a single path — embedded SVG/HTML markup, base64-encoded images, serialized datasets — and are therefore invisible to the structural checks.

Because the checks run independently, a single view can produce violations from several methods at once.

Why it matters

Embedding large datasets in view.json causes a chain reaction of problems. The gateway has to parse and hold the full JSON document in memory every time the view loads, model building (and therefore linting) slows down proportionally to the document size, and the file balloons in your version control history. Worse, it conflates two separate concerns: a view file is supposed to describe a UI, not be a dump of the data the UI displays. Once the data is baked into the view, refreshing it requires a redeploy — so the data goes stale, gets duplicated across views, and becomes a maintenance liability. Datasets belong behind named queries or the tag historian, where they can be cached, paginated, and updated independently of the UI.

Configuration

The rule accepts six options grouped into two categories below. All thresholds are independent — disabling one detection method requires setting its threshold high enough that it cannot be exceeded; there is no single on/off switch per method.

Detection thresholds

max_array_size

Type: int  ·  Default: 50

Maximum allowed length for any array stored under custom.*. Computed as max_observed_index + 1 for each property path that matches the regex ^(custom\.[^\[]+)\[(\d+)\]. Each array property that exceeds this threshold emits its own violation.


max_sibling_properties

Type: int  ·  Default: 50

Maximum allowed number of unique sibling keys under any custom.* parent path. Array indices [N] are stripped before counting, so custom.data[0].x and custom.data[1].x count as a single child x under custom.data. Each parent path that exceeds this threshold emits its own violation.


max_nesting_depth

Type: int  ·  Default: 5

Maximum allowed depth for any custom.* path, measured as len(parts) - 1 after splitting on . (so custom.a.b.c is depth 3). The rule tracks only the single deepest path it observes and emits at most one depth violation per file, even when multiple paths exceed the threshold.


max_data_points

Type: int  ·  Default: 1000

Maximum allowed count of total flattened paths under custom.*. Flattening produces an entry for every leaf and every intermediate path component, so deeply nested objects accumulate counts faster than shallow flat ones. At most one violation is emitted per file from this method.


max_value_length

Type: int  ·  Default: 10000

Maximum allowed character length for any single string value stored under custom.*, measured with len() on the flattened leaf value. Only string values are checked — numbers, booleans, and container placeholders have no meaningful character length. Each value that exceeds this threshold emits its own violation. The threshold is exclusive: a value of exactly max_value_length characters passes.

Severity

severity

Type: "warning" | "error"  ·  Default: "error"

Severity for every violation emitted by this rule (all five detection methods share the same severity). Set to "warning" during legacy migrations or initial adoption so the rule reports but does not break CI.

Detection methods in detail

1. Array size

The rule iterates every key in the flattened JSON and matches it against the regex ^(custom\.[^\[]+)\[(\d+)\]. The first capture group is the property path (e.g., custom.dropdownData.rows) and the second is the array index. For each property path, the rule keeps the maximum index it encounters and reports array_size = max_index + 1. If that size exceeds max_array_size, a violation is added.

Violation message format (from add_violation in source):

<property_path>: Custom property '<property_name>' contains <N> items. Large datasets should be stored in databases, not view JSON. Maximum recommended: <max_array_size> items.

<property_name> is <property_path> with the leading custom. stripped.

2. Property breadth

For every flattened path under custom.*, array indices [N] are stripped (so custom.data[0].value and custom.data[1].value collapse into custom.data.value). The rule then walks each path part-by-part starting at custom.X and records the unique child segments observed under each parent. Any parent whose unique child count exceeds max_sibling_properties produces a violation.

Violation message format:

<parent_path>: Contains <N> sibling properties. Large flat structures should be stored in databases, not view JSON. Maximum recommended: <max_sibling_properties> siblings.

3. Nesting depth

The rule normalizes each custom.* path (stripping array indices) and counts depth as len(parts) - 1 after splitting on .. So custom.a.b.c is depth 3, custom.a.b.c.d.e.f is depth 6. The rule tracks only the single deepest path it sees; if its depth exceeds max_nesting_depth, one violation is emitted per file (regardless of how many other paths also exceed the threshold).

Violation message format:

<deepest_path>: Custom properties are nested <N> levels deep. Deeply nested structures indicate complex data that should be in databases. Maximum recommended: <max_nesting_depth> levels.

4. Data points

The rule counts every flattened path that starts with custom.. Because flattening produces an entry for every leaf and every intermediate path component, a few large objects can quickly accumulate thousands of paths. If the total exceeds max_data_points, a violation is added.

Violation message format:

custom: Contains <N> total data points across all custom properties. Large volumes of data should be stored in databases, not view JSON. Maximum recommended: <max_data_points> data points.

5. Value length

The rule iterates every flattened path under custom.* whose value is a string and compares len(value) against max_value_length. Non-string values are skipped. Unlike the structural methods, this catches a payload that flattens to a single path-value pair — an embedded SVG or HTML document, a base64-encoded image, a serialized JSON blob — which registers as just one data point at depth 1 and would otherwise pass every other check. Each offending value emits its own violation; <property_name> is the path with array indices and the leading custom. stripped.

Violation message format:

<path>: Custom property '<property_name>' contains a string value of <N> characters. Large values should be stored in external resources or databases, not view JSON. Maximum recommended: <max_value_length> characters.

Examples

Problematic code: Excessive array size

The fixture tests/cases/BadContextData/view.json parks a giant lookup table inside a custom property. The structure looks like:

{
"custom": {
"dropdownData": {
"rows": [
{ "label": "ROW-1A", "value": "ROW-1A" },
{ "label": "ROW-1B", "value": "ROW-1B" },
{ "label": "ROW-1C", "value": "ROW-1C" }
]
}
}
}

The real fixture contains hundreds of rows ([ ... 784 items total ]) — enough to push the view well past max_array_size = 50.

Problematic code: Excessive breadth

A flat dictionary of devices keyed by ID will trip the breadth check:

{
"custom": {
"devices": {
"device1": { "name": "Pump A" },
"device2": { "name": "Pump B" },
"device3": { "name": "Pump C" }
}
}
}

Once custom.devices accumulates more than 50 unique children, the rule fires.

Problematic code: Excessive depth

Deep object trees produce a single depth violation for the deepest path:

{
"custom": {
"a": { "b": { "c": { "d": { "e": { "f": "value" } } } } }
}
}

custom.a.b.c.d.e.f is six levels deep, exceeding the default max_nesting_depth = 5.

Problematic code: Excessive data points

This violation fires when the total number of flattened paths under custom.* crosses max_data_points. A single property with hundreds of nested objects can trip it on its own, even when no individual array, breadth, or depth threshold is breached.

Problematic code: Oversized value

A single custom property holding a large embedded payload — inline SVG markup, a base64-encoded image, a serialized dataset — trips the value-length check even though it is structurally just one property:

{
"custom": {
"svgContent": "<svg xmlns=\"http://www.w3.org/2000/svg\" ... tens of thousands of characters ... </svg>"
}
}

Store the asset as a project resource (e.g. an image or embedded view) or fetch it at runtime instead of inlining it into view.json.

Auto-fix support

This rule does not provide auto-fixes — moving data out of a view is a semantic refactor that the rule cannot perform safely. The replacement strategy depends on where the data should live (named query, tag historian, message handler), and only a developer can make that call.

Edge cases & exemptions

  • Properties outside custom.* (e.g., params.*, propConfig.*, props.*) are not analyzed.
  • Each detection method runs independently; the same property can be reported by multiple methods.
  • The value-length method only measures string values; numbers, booleans, and empty-container placeholders are skipped.
  • Array size is computed as max_index + 1; sparse arrays are not handled specially — only the highest observed index matters.
  • The depth method reports only the deepest path even if many paths exceed the threshold (one violation per file from this method).
  • The data-points method counts intermediate path components produced by JSON flattening, not just leaf values, so deeply nested objects accumulate counts faster than wide flat ones.
  • The breadth method strips [N] array indices before counting siblings, so custom.data[0].x, custom.data[1].x count as a single child x under custom.data.
  • The rule processes the flattened JSON directly (set_flattened_json) rather than visiting model nodes, so target_node_types is intentionally an empty set — the rule's logic runs once per file in process_nodes.

See also