Showing the Power of RS-X with a “Scary” Credit-Risk Formula

Published: (February 5, 2026 at 04:12 AM EST)
2 min read
Source: Dev.to

Source: Dev.to

A “scary” credit‑risk formula

Below is a credit‑risk formula written in TypeScript that pulls together several data sources.

Model definitions

// Model
export interface ICustomer {
    age: number;
    income: number;
    employmentYears: number;
}

// Credit info
export interface ICredit {
    score: number;
    outstandingDebt: number;
}

// Market parameters
export interface IMarket {
    baseInterestRate: number;
}

// Risk parameters
export interface IRisk {
    volatilityIndex: number;      // e.g., market volatility
    recessionProbability: number; // e.g., probability of economic downturn
}

// Risk calculation parameters
export interface IRiskCalcParameters {
    readonly market: IMarket;
    readonly risk: IRisk;
}

// Asynchronous data sources (simulating API calls)
export interface IRiskModel {
    readonly customer: BehaviorSubject;
    readonly credit: BehaviorSubject;
    readonly riskParameters: BehaviorSubject;
}

The monolithic formula

(
    (credit.score = 0.75 ? 'HIGH' : (
(
    (credit.score = 0.45 ? 'MEDIUM' : 'LOW'
);

Even if you start with one huge formula, RS‑X lets you transform it into reactive expressions.

Splitting the formula with RS‑X

The data model is simple:

  • Customer info
  • Credit info
  • Market / risk parameters

These values may change independently or arrive asynchronously. RS‑X doesn’t care; you just plug them into expressions and updates flow automatically—no listeners, no manual subscriptions, no explicit recalculateRiskScore() calls.

Sub‑expressions

// Baseline personal risk
const basePersonalRisk = expressionFactory.create(model, `
    (credit.score ;
// Age‑based adjustment
const ageBasedRiskAdjustment = expressionFactory.create(_model, `
    customer.age ;
// Market risk component
const marketRisk = expressionFactory.create(_model, `
    (riskParameters.risk.volatilityIndex * 0.5) +
    (riskParameters.risk.recessionProbability * 0.5)
`) as IExpression;
// Interest‑rate impact
const interestRateImpact = expressionFactory.create(_model, `
    riskParameters.market.baseInterestRate * 2
`) as IExpression;

Combining the pieces

const riskScore = expressionFactory.create({
    basePersonalRisk,
    ageBasedRiskAdjustment,
    marketRisk,
    interestRateImpact
}, `
    basePersonalRisk + ageBasedRiskAdjustment + marketRisk + interestRateImpact
`);
const riskClassification = expressionFactory.create({
    riskScore: riskScore as IExpression,
    thresholds: { highRisk: 0.75, mediumRisk: 0.45 }
}, `
    riskScore >= thresholds.highRisk
        ? 'HIGH'
        : riskScore >= thresholds.mediumRisk
            ? 'MEDIUM'
            : 'LOW'
`);

Each sub‑expression recalculates only when its inputs change, giving you both readability and performance.

Other use cases

The same pattern works for:

  • Pricing rules
  • Validation logic
  • Feature flags
  • Scoring models
  • UI state

…basically anything you can imagine.

Takeaways

  • Messy formulas don’t need messy code.
  • RS‑X can split monolithic expressions automatically.
  • Async and sync data mix effortlessly.
  • Updates propagate automatically and efficiently.

RS‑X lets you describe what your logic is, not how to keep it up to date. Even a “scary” formula becomes readable, modular, efficient, and surprisingly calm.

Demo

You can see this in action with a live demo on StackBlitz:
StackBlitz demo

References

  • RS‑X GitHub
  • NPM packages:
    • @rs-x/core
    • @rs-x/state-manager
    • @rs-x/expression-parser
    • @rs-x/angular
Back to Blog

Related posts

Read more »

Replace Turbo confirm with native dialog

Rails, when using turbo‑links, ships with a built‑in confirmation dialog for destructive actions. You've probably used it countless times: erb The default turbo...