How a hotel pricing engine decides rates, the architecture of a dynamic pricing engine, surge pricing logic, the design patterns inside a pricing engine (Strategy, Chain of Responsibility, Decorator, Specification), challenges of dynamic pricing, improving revenue optimisation, and designing and building a rule engine — internals, Drools vs a custom DSL, rule storage, versioning, testing and performance.
Published September 25, 2026
How to use this lesson
Pricing is a great senior topic, because it mixes:
Pricing configuration service: revenue managers edit rate plans, rules and promotions (versioned, and audited).
Price computation: when inputs change (demand signals, a rule edit, a BAR update), recompute the affected prices asynchronously, and publish them into a rate cache (Redis, keyed by hotel:roomType:ratePlan:date:occupancy) and to channels.
Price serving: the search and booking paths read the precomputed prices, with light on-request adjustments (a user segment, a coupon).
Price quote with an ID and expiry, at booking, so the guest pays exactly the price shown (it's re-validated on confirmation).
Q2. Design a dynamic pricing engine architecture. How does surge pricing logic work?
Short answer:
The inputs (signals):
occupancy and pace (bookings versus the same time last year);
demand indicators (searches, look-to-book, events and holidays);
competitor rates (rate shopping);
cancellations;
seasonality;
the length of lead time.
The flow:
Ingest the signals through Kafka (booking events, search events, competitor-rate feeds).
A stream processor (Kafka Streams or Flink) keeps the demand features per hotel and date (windowed aggregations).
The pricing model (rules plus optionally ML price-elasticity models) computes a recommended BAR, within guardrails (a floor, a ceiling, and a maximum change per day).
Human in the loop: auto-apply within limits, or send the recommendation to a revenue manager for approval.
Publish the new prices to the rate cache and the channels (OTA sync), with the change reason.
Factory: build the right strategy or pipeline from configuration.
Template Method: a common calculation skeleton with hooks for specific rate types.
Observer or events: recalculate prices when inputs change.
Immutable value objects (Money with BigDecimal and currency, PriceBreakdown), for correctness and auditability.
publicinterfacePriceAdjuster { PriceContext apply(PriceContext ctx); }
publicfinalclassPricingPipeline {
privatefinal List<PriceAdjuster> adjusters; // ordered, from configurationpublic PriceBreakdown price(PriceRequest req) {
PriceContextctx= PriceContext.start(req);
for (PriceAdjuster a : adjusters) ctx = a.apply(ctx); // each records its line itemreturn ctx.breakdown();
}
}
Common trap: never use double for money. Use BigDecimal with an explicit RoundingMode (or long minor units), and round at defined points (per night, or per stay) consistently everywhere, or the channels will show different totals.
Q4. What challenges come up in dynamic pricing? How would you improve revenue optimisation?
Short answer:
Challenges:
Rate parity and consistency: the same price across the website, OTAs and GDS; propagation delays create mismatches and penalties.
Cache staleness: the price shown at search differs from the booking price. Handle it with price quotes, and re-validation.
Volume: hotels × room types × rate plans × dates (365+) × occupancies × channels × currencies means millions of prices to recompute. Use incremental recomputation, only for affected keys.
Data quality: noisy competitor rates, missing demand signals.
Customer trust: extreme swings look like price gouging, so use guardrails and smoothing.
Explainability and audit for revenue managers and disputes.
Regulation: price-display laws (total price including fees), and fairness concerns in personalised pricing.
Improving revenue optimisation:
better demand forecasting (ML on history, events and pace);
price elasticity experiments;
length-of-stay controls (restrictions to fill shoulder nights);
overbooking based on no-show and cancellation forecasts;
channel mix optimisation (shift demand to cheaper, direct channels);
upsell and packages;
faster feedback loops (near-real-time repricing);
measuring RevPAR (revenue per available room) and ADR (average daily rate) with A/B tests.
Q5. How does a rule engine work internally? How do you build a rule engine?
Short answer:
Concept: rules are WHEN conditions THEN actions, kept outside the code, so the business can change them without deployments.
Internally (in production rule systems such as Drools):
facts are inserted into a working memory;
the Rete (or Phreak) algorithm builds a network that shares condition evaluations across rules and remembers partial matches, so re-evaluation on changes is fast;
matching rules go onto an agenda; conflict resolution (salience or priority, then order) decides the firing order;
actions can modify facts, which may trigger other rules (forward chaining).
A simpler custom engine (enough for many needs):
a rule model: an ID, a condition expression, an action, a priority, the effective dates, and a version;
conditions written in a safe expression language (SpEL in a restricted evaluation context, MVEL, JEXL or CEL), or a JSON DSL (field, operator, value, with and/or nesting);
evaluation: load and compile the rules at startup or on change, then evaluate them against a context object; strategies such as first match, all matches, or highest priority;
Q6. Design a rule engine architecture, for a pricing or business-rules platform.
Short answer:
Rule authoring: a UI or DSL for business users, with validation, a simulation or preview ("what would this rule do to last month's bookings?"), and an approval workflow.
The rule repository: versioned rules (a database table plus an immutable version history, or Git-backed rules); effective-from and effective-to dates; scoping (hotel, brand, region, channel).
Distribution: on publish, emit a RulesChanged event. Services hot-reload the compiled ruleset (an immutable snapshot swapped atomically), so evaluation doesn't need a remote call.
Execution: evaluation is embedded in the service (low latency) or a central decision service (simpler governance, but a network hop). Rules must be deterministic and side-effect free; actions return decisions, and the service applies them.
Safety:
sandboxed expressions (no arbitrary code execution: restrict SpEL with SimpleEvaluationContext);
timeouts and complexity limits;
conflict detection between rules;
a rollback to the previous version.
Observability and audit: which rules fired, for which request, and with which version, logged for explainability.
Testing: unit tests per rule, golden datasets for regression, and shadow evaluation of new rulesets against live traffic.
Common trap: evaluating user-authored SpEL or script expressions with the full StandardEvaluationContext allows arbitrary method calls and remote code execution. Use a restricted context, or a purpose-built expression language (CEL), with an allow-list.
Q7. Drools, or a custom rule engine?
Short answer:
Drools (KIE): powerful (Rete/Phreak, complex event processing, decision tables, DMN), with good tooling for large, interacting rule sets. The costs: a learning curve, heavier runtime, and the rule-debugging complexity.
A custom engine or expression language: simpler, lighter, easier to reason about. Best for tens to hundreds of independent rules with simple conditions.
DMN decision tables (Drools, Camunda) are a good middle ground when business users should own the tables.
Choose based on the number and interaction of rules, and on who authors them.
Follow-up questions this topic invites — and their answers
Q: How do you keep search prices and booking prices consistent?
A: Serve both from the same precomputed rate store, issue a price quote with an ID and expiry at selection, and re-validate at booking; if the price changed, show the new price before charging.
Q: What is rate parity?
A: A (contractual or regulatory, depending on the market) expectation that a hotel publishes the same public rate on its own site and OTAs. Pricing systems must propagate changes quickly and consistently to avoid disputes.
Q: How do you test pricing changes safely?
A: Unit tests for each adjuster, golden-dataset regression tests, shadow pricing (compute new prices without serving them and compare), gradual rollout per hotel, and anomaly alerts on price changes.
Q: What is the Specification pattern?
A: Encapsulating a business rule as an object with isSatisfiedBy(candidate), composable with and, or and not. It keeps eligibility logic reusable and testable.