GoRules Version 2 is here - redesigned, now with managed cloud.GoRules Version 2 is here!
Watch the launch videoWatchSwitch from Higson to GoRules.
A step-by-step guide to moving your Higson decision tables, Groovy functions, and flows to GoRules: export, convert with the AI copilot, validate in parallel, and cut over.
What you get with GoRules
Where your rules land: the parts of GoRules most relevant to this migration.
One expression language everywhere
Decision table cells, expression nodes, and switch conditions all use Zen - a single expression language with 60+ built-in functions (sum, avg, filter, map, date, duration, matches). For logic beyond expressions, functionNode runs sandboxed JavaScript with zod, big.js, dayjs, and http imports.
Native SDKs across languages
The same Rust core ships as @gorules/zen-engine for Node.js, zen-engine for Python and Rust, zen-go for Go, io.gorules:zen-engine for JVM (Java and Kotlin), GoRules.ZenEngine for .NET, plus Swift, Android, and browser or Lambda via WASM.
Versioning, environments, and rollback built in
Semantic versioned releases, environment-scoped deployments with rollback, and branching for parallel rule development. Publish, release, and deploy are explicit human actions, each with an audit trail.
AI copilot for authoring and conversion
The copilot converts uploaded rule files into decision graphs, edits tables from plain-language instructions, generates test cases, and explains what a decision did on a given input. Deploys and releases stay explicit human actions.
The migration path
A step-by-step route from your current setup to running your rules on Zen Engine.
1. Export decision tables and functions from Higson Studio
Decision tables export as XLSX (single) or ZIP of XLSX files (mass export); each bundle contains a Data sheet (matrix), a Configuration sheet (levels + definition), and a Mark sheet. Functions export as ZIP/XLSX with their Groovy source. Flows mass-export emits a ZIP containing per-flow JSON files plus an XLSX of details (or per-flow SVG). Profiles, Tags, and Test Packages are bundled in a Snapshot ZIP - useful as a reference, but the AI copilot reads decision-table data and Groovy source most directly.
2. Map Higson constructs to GoRules node types
Decision tables (parameters) → decisionTableNode. best match mode resolves by specificity, so when converting to a first hit policy table, re-order rows by specificity - exact-match rows above wildcard/catch-all rows; union match maps to collect hit policy. Higson matchers (between/ii, between/ie, between/ei, contains/all, contains/any, contains/none, in, not in, text/like, text/regex) become Zen unary tests in input cells (> 50, [1..10], "US","CA", contains($, "test"), matches($, "...")). Functions (Groovy) → functionNode (JavaScript). Flows → the JSON graph topology itself, with Flow Condition blocks becoming switchNode and Flow sub-flow blocks becoming decisionNode. Domain attribute calls (DOMAIN_OBJECT) → separate decisions referenced via decisionNode or per-attribute dispatch in a switchNode. Dictionary tables → inline Zen unary test sets ("A","B","C") or a referenced decisionNode returning a validity flag.
3. Convert decision tables with the AI copilot
Open the AI chat panel on a new decision document and paste the XLSX export contents (or upload the file). The copilot generates decisionTableNode rows: input columns get Zen unary tests, output columns become output values, and a default catch-all row is added. Cells using cascade get ($p table[idx]) or cascade call ($f fun(args)[code]) need manual redesign - paste the affected rows separately, describe what the cascade resolves to (which decision table it calls, what argument types it passes, which output level it picks), and the copilot can re-express the chain as upstream decisionNode or expressionNode connections. Implicit type conversions (_dec(), _num(), _int(), _str(), _date(), _bool()) become explicit Zen casts (number(), string(), date()).
4. Rewrite Groovy functions as JavaScript functionNode
Paste the Groovy function body into the AI copilot and ask for a functionNode conversion. The copilot translates def x = ... to let/const, ctx.getDecimal("path") to direct property access on the input object, math.* to Math.* or big.js, and dates handled via util.* to dayjs. Calls to other decision tables from inside a function (higson.getNumber("table", ctx)) cannot stay inside the function in GoRules - they become an upstream decisionNode that is wired into the function with the result available on input. Calls to other functions become upstream functionNode references the same way. The JavaScript sandbox restricts imports: zod, big.js, dayjs, http, and zen are available.
5. Redesign Domain model and Region/Version/Timeline by hand
The Domain Definition + Domain Configuration model has no GoRules equivalent. Flatten it: each domain attribute that resolves polymorphically (literal vs decision-table vs function) becomes a separate decision file or a switchNode branch keyed off the attribute identifier. Domain Configuration trees with inherited Global Attributes are persisted externally and passed in context on each evaluate call. Local Attributes (created ad-hoc per element) cannot survive - every consumer must agree on a fixed input schema. Region/Version/Timeline date-windowed selection has no native equivalent: encode date checks as switchNode statements keyed off date(input.now), or pre-publish each Timeline window as a separate release and rotate the active release on the environment via external scheduling. External Source JDBC integrations become customHandler HTTP integrations against a service that fronts the SQL source.
6. Swap the API integration
Replace POST /api/execution/invoke with POST /projects/:projectId/evaluate/*. The structured request envelope (ctx.properties[] of key/value/complexValue tuples + elements[] of PARAMETER/FUNCTION/DOMAIN_OBJECT) becomes a free-form context object: every ctx.properties[] entry becomes a path on context (e.g. policy.insured.age flattens to { "policy": { "insured": { "age": 42 } } }). Each element in the request becomes one HTTP call to the URL of that decision (the file path replaces code). Auth becomes x-access-token: <project access token>. Multi-row decision-table results returned as multiple resultValue objects become a single array under the configured output field when the table uses the collect hit policy. For embedded JVM use, replace HigsonEngineFactory (or the HyperonEngineFactory alias) with io.gorules:zen-engine from Maven Central; for Node.js use @gorules/zen-engine directly with engine.createDecision(graph).safeEvaluate(context, opts).
7. Validate in parallel, then cut over
Rebuild the Tests and Test Packages from your snapshot as GoRules simulator test cases and run identical inputs through both engines. GoRules simulation returns a per-node trace - each node reports its input, output, and execution time - so divergences localize quickly. Once outputs match, publish the decision documents and release them to the target environment, then switch your invoke calls over.
Let the copilot do the translation.
The AI copilot works best with text-readable inputs: the XLSX exports of decision tables, the per-flow JSON files in a Flow ZIP, and the Groovy source of functions. Pasted XLSX decision-table data converts cleanly to decisionTableNode rows - Higson matchers map to Zen unary tests, and the copilot adds a catch-all default row. Cascade get ($p) and cascade call ($f) cell syntax requires manual redesign: paste the cell, describe what each cascade resolves to (target table or function, argument types, output level via [idx] or [code], implicit conversion via _dec()/_num()/_int()/_str()/_date()/_bool()), and the copilot can re-express it as upstream decisionNode or functionNode connections plus explicit Zen casts. Flows can be described in text (block list, variable names and types, condition logic, returned variables) and the copilot will generate the equivalent DAG with switchNode for Condition blocks and decisionNode for sub-flows. Groovy functions: paste the body and request a functionNode conversion - ctx.* reads become input property access, math.*/util.* map to Math.*/big.js/dayjs, and any higson.getNumber("table", ctx) calls are surfaced as upstream node references rather than in-function calls. What the copilot cannot do: resolve DOMAIN_OBJECT addressing (the profileCode + attributeCode polymorphic dispatch has no automatable mapping), migrate the Region/Version/Timeline date-window model (no analogue in GoRules releases), or translate External Source JDBC integrations - those require an external HTTP wrapper that the GoRules graph reaches via customHandler integrations.
Other migration guides
View all guidesSwitch from Drools
A step-by-step guide to moving your DRL rules, DMN models, and spreadsheet decision tables to GoRules: export, convert with the AI copilot, validate in parallel, and cut over.
GuideSwitch from IBM ODM
A step-by-step guide to moving your BAL rules, decision tables, and ruleflows to GoRules: export, convert with the AI copilot, validate in parallel, and cut over.
GuideSwitch from FICO Blaze Advisor
A step-by-step guide to moving your SRL rulesets, decision tables, ruleflows, and score models to GoRules: map the BOM to JSON, convert with the AI copilot, validate in parallel, and cut over.
Leave the build step
behind.
Bring your Higson rules over, run both engines in parallel, and cut over when the outputs match.