Best practices for spec development
This guide outlines best practices for developing Brossa specifications, drawn from production implementations across multiple carriers and product types.
Code organization
File structure
///////////////////////////////////////////////////////////////////////////////
// Imports and dependencies
import "currency_layer.art" as CL;
///////////////////////////////////////////////////////////////////////////////
// Conditions
condition decline;
condition refer;
///////////////////////////////////////////////////////////////////////////////
// Constants and configuration
@derived @hidden
const.max_limit: Number<"USD"> = 50_000_000<"USD">;
///////////////////////////////////////////////////////////////////////////////
// Basic information
// ... policy and insured details ...
///////////////////////////////////////////////////////////////////////////////
// Risk information
// ... risk characteristics ...
///////////////////////////////////////////////////////////////////////////////
// Coverage configuration
// ... coverage definitions ...
///////////////////////////////////////////////////////////////////////////////
// Rating calculations
// ... premium calculations ...
///////////////////////////////////////////////////////////////////////////////
// Submission structure
// ... UI organization ...
Naming conventions
-
Namespacing
-
Use descriptive prefixes:
policy.,risk.,coverage., etc. -
Group related fields under common namespaces.
-
Keep namespace hierarchy shallow (max 3–4 levels).
-
-
Field names
-
Use lowercase with underscores.
-
Be specific but concise.
-
Include units in name if not clear from type.
-
Example:
risk.building.construction_type: Text;
risk.building.year_built: Year;
risk.building.total_area_sqft: Number;
Data types and validation
Type selection
-
Basic types
-
Text: For string values. -
CaselessText: For case-insensitive text. -
Int: For whole numbers. -
Number: For decimal values. -
Decimal: For precise decimal calculations. -
Date: For dates. -
YesNo: For boolean values. -
Percent: For percentage values.
-
-
Currency values
policy.currency: Text = "USD"; risk.tiv: Number<policy.currency>; risk.tiv has minimum 0<policy.currency>; -
Sets and collections
coverage.type: #{Text}; coverage.type has multi_options #{ "Property Damage", "Business Interruption" };
Validation rules
-
Input validation
when risk.year_built > current_year() then risk.year_built^invalid with "Year built cannot be in the future"; when risk.tiv <= 0<policy.currency> then risk.tiv^invalid with "TIV must be greater than zero"; -
Business rules
when risk.tiv > const.max_limit * roe_to_policy("USD") then refer with "TIV exceeds automatic capacity"; when risk.building_age > 50 and risk.construction_type == "Frame" then decline with "Frame construction over 50 years old not eligible";
Rating calculations
CSV integration
-
Table definition
csv.construction_factors = table "construction_factors.csv" { type: "Construction Type" Text, factor: "Factor" Number }; -
Lookups
@derived rating.construction_factor = vertical_lookup_first( csv.construction_factors, "type", "factor", risk.construction_type ) <|> 1.0;
Premium calculation
-
Factor calculation
@derived rating.technical_premium(coverage_id: Uuid): Number<policy.currency> = risk.tiv * rating.base_rate(coverage_id) * rating.construction_factor * rating.age_factor; -
Aggregation
@derived rating.total_premium: Number<policy.currency> = sum_list([ rating.technical_premium(id) | id <- coverage.ids ]);
UI/UX design
Field properties
-
Labels and prompts
risk.construction_type: Text; risk.construction_type has { prompt= "Construction Type", description= "Building's primary construction material" }; -
Options and suggestions
risk.state: Text; risk.state has { options= column_list(csv.states, "state_code"), suggestions= column_list(csv.states, "state_name") };
Table rendering
-
Basic table
coverage.ids has render { type: "table", columns: [ {name: "type", label: "Coverage Type"}, {name: "limit", label: "Limit ({{policy.currency}})"} ], rows: [{ id: id, cells: { type: data-point(coverage.type(id)), limit: data-point(coverage.limit(id)) } } | id <- coverage.ids] }; -
Grouped fields
risk = { risk.construction_type, risk.year_built, risk.tiv }; risk has heading "Risk Information";
Performance optimization
-
Derived fields
-
Use
@derivedfor calculated fields. -
Add
@hiddenfor internal calculations. -
Cache complex calculations where possible.
-
-
Lookup optimization
@derived @hidden const.construction_factors = lookup_from_list([ {key: row->type, value: row->factor} | row <- table_records(csv.construction_factors) ]);
Error handling
-
Graceful fallbacks
@derived rating.factor = lookup(risk.type, const.factors) <|> 1.0; -
Clear error messages
when not(elem(risk.state, column_list(csv.states, "state_code"))) then risk.state^invalid with "Please select a valid state";
Common pitfalls to avoid
-
Currency handling
-
Always specify currency units.
-
Use proper currency conversion.
-
Handle exchange rates correctly.
-
-
Validation gaps
-
Validate all user inputs.
-
Include range checks.
-
Handle missing values.
-
-
Performance issues
-
Avoid unnecessary calculations.
-
Cache lookup results.
-
Use efficient data structures.
-
-
UI problems
-
Group related fields.
-
Provide clear labels.
-
Include helpful descriptions.
-