diff --git a/src/almanac.js b/src/almanac.js
index 7a9a8e0..ccf684c 100644
--- a/src/almanac.js
+++ b/src/almanac.js
@@ -4,12 +4,8 @@ import Fact from './fact'
 import { UndefinedFactError } from './errors'
 import debug from './debug'
 
-import { JSONPath } from 'jsonpath-plus'
 import isObjectLike from 'lodash.isobjectlike'
-
-function defaultPathResolver (value, path) {
-  return JSONPath({ path, json: value, wrap: false })
-}
+import { defaultPathResolver } from './resolver';
 
 /**
  * Fact results lookup
diff --git a/src/engine.js b/src/engine.js
index 3bd958e..6775113 100644
--- a/src/engine.js
+++ b/src/engine.js
@@ -7,6 +7,8 @@ import Almanac from './almanac'
 import EventEmitter from 'eventemitter2'
 import defaultOperators from './engine-default-operators'
 import debug from './debug'
+import { interpolateDeep, needsInterpolation, defaultInterpolation } from './interpolate';
+import { defaultPathResolver } from './resolver';
 
 export const READY = 'READY'
 export const RUNNING = 'RUNNING'
@@ -21,12 +23,16 @@ class Engine extends EventEmitter {
     super()
     this.rules = []
     this.allowUndefinedFacts = options.allowUndefinedFacts || false
-    this.pathResolver = options.pathResolver
+    this.pathResolver = options.pathResolver || defaultPathResolver
+    this.interpolation = options.interpolation || defaultInterpolation;
+    if(!(this.interpolation instanceof RegExp) || !this.interpolation.global) throw new Error('interpolation option must be a global regexp')
     this.operators = new Map()
     this.facts = new Map()
     this.status = READY
     rules.map(r => this.addRule(r))
     defaultOperators.map(o => this.addOperator(o))
+
+    this.interpolatableRules = new Set();
   }
 
   /**
@@ -49,6 +55,7 @@ class Engine extends EventEmitter {
       if (!Object.prototype.hasOwnProperty.call(properties, 'conditions')) throw new Error('Engine: addRule() argument requires "conditions" property')
       rule = new Rule(properties)
     }
+    if(needsInterpolation(rule,this.interpolation)) this.interpolatableRules.add(rule);
     rule.setEngine(this)
     this.rules.push(rule)
     this.prioritizedRules = null
@@ -62,7 +69,8 @@ class Engine extends EventEmitter {
   updateRule (rule) {
     const ruleIndex = this.rules.findIndex(ruleInEngine => ruleInEngine.name === rule.name)
     if (ruleIndex > -1) {
-      this.rules.splice(ruleIndex, 1)
+      const [old] = this.rules.splice(ruleIndex, 1)
+      this.interpolatableRules.delete(old);
       this.addRule(rule)
       this.prioritizedRules = null
     } else {
@@ -75,21 +83,25 @@ class Engine extends EventEmitter {
    * @param {object|Rule|string} rule - rule definition. Must be a instance of Rule
    */
   removeRule (rule) {
-    let ruleRemoved = false
+    let theRemovedRule;
     if (!(rule instanceof Rule)) {
-      const filteredRules = this.rules.filter(ruleInEngine => ruleInEngine.name !== rule)
-      ruleRemoved = filteredRules.length !== this.rules.length
+      const filteredRules = this.rules.filter(ruleInEngine => {
+        const isRule = ruleInEngine.name === rule;
+        if(!theRemovedRule && isRule) theRemovedRule = ruleInEngine;
+        return !isRule;
+      });
       this.rules = filteredRules
     } else {
       const index = this.rules.indexOf(rule)
       if (index > -1) {
-        ruleRemoved = Boolean(this.rules.splice(index, 1).length)
+        theRemovedRule = this.rules.splice(index, 1)[0];
       }
     }
-    if (ruleRemoved) {
+    if (theRemovedRule) {
       this.prioritizedRules = null
+      this.interpolatableRules.delete(theRemovedRule);
     }
-    return ruleRemoved
+    return Boolean(theRemovedRule);
   }
 
   /**
@@ -211,6 +223,7 @@ class Engine extends EventEmitter {
         debug(`engine::run status:${this.status}; skipping remaining rules`)
         return Promise.resolve()
       }
+
       return rule.evaluate(almanac).then((ruleResult) => {
         debug(`engine::run ruleResult:${ruleResult.result}`)
         almanac.addResult(ruleResult)
@@ -240,12 +253,28 @@ class Engine extends EventEmitter {
       pathResolver: this.pathResolver
     }
     const almanac = new Almanac(this.facts, runtimeFacts, almanacOptions)
-    const orderedSets = this.prioritizeRules()
+    const orderedSets = this.prioritizeRules();
+
     let cursor = Promise.resolve()
     // for each rule set, evaluate in parallel,
     // before proceeding to the next priority set.
     return new Promise((resolve, reject) => {
       orderedSets.map((set) => {
+        set = set.map(rule => {
+          if(!this.interpolatableRules.has(rule)) return rule;
+          rule = new Rule(
+            interpolateDeep(
+              rule.toJSON(true),
+              runtimeFacts,
+              this.interpolation,
+              this.pathResolver
+            )
+          );
+          rule.setEngine(this);
+          return rule;
+        });
+
+
         cursor = cursor.then(() => {
           return this.evaluateRules(set, almanac)
         }).catch(reject)
diff --git a/src/interpolate.js b/src/interpolate.js
new file mode 100644
index 0000000..93ffea7
--- /dev/null
+++ b/src/interpolate.js
@@ -0,0 +1,27 @@
+export const defaultInterpolation = /\{\{\s*(.+?)\s*\}\}/g
+
+export const needsInterpolation = (rule,regexp) => regexp.test(rule.toJSON(true));
+
+const interpolate = (subject = '', params = {}, regexp, resolver) => {
+  let shouldReplaceFull, found;
+
+  const replaced = subject.replace(regexp, (full, matched) => {
+    shouldReplaceFull = full === subject;
+    found = resolver(params, matched);
+    return shouldReplaceFull ? '' : found;
+  });
+
+  return shouldReplaceFull ? found : replaced;
+};
+
+
+export const interpolateDeep = (o, params, regexp, resolver) => {
+  if (!o || typeof o === 'number' || typeof o === 'boolean') return o;
+
+  if (typeof o === 'string') return interpolate(o,params,regexp,resolver)
+
+  if (Array.isArray(o)) return o.map(t => interpolateDeep(t, params, regexp, resolver));
+
+  return Object.entries(o).reduce((acc, [k, v]) => ({...acc,[k]: interpolateDeep(v, params, regexp, resolver)}),{});
+};
+
diff --git a/src/resolver.js b/src/resolver.js
new file mode 100644
index 0000000..fd41455
--- /dev/null
+++ b/src/resolver.js
@@ -0,0 +1,5 @@
+import { JSONPath } from 'jsonpath-plus'
+
+export function defaultPathResolver (value, path) {
+    return JSONPath({ path, json: value, wrap: false })
+}
\ No newline at end of file
diff --git a/src/rule.js b/src/rule.js
index 5db0191..afaf322 100644
--- a/src/rule.js
+++ b/src/rule.js
@@ -4,6 +4,7 @@ import Condition from './condition'
 import RuleResult from './rule-result'
 import debug from './debug'
 import EventEmitter from 'eventemitter2'
+import { needsInterpolation } from './interpolate'
 
 class Rule extends EventEmitter {
   /**
diff --git a/test/engine-fact.test.js b/test/engine-fact.test.js
index 93d9b99..5822d10 100644
--- a/test/engine-fact.test.js
+++ b/test/engine-fact.test.js
@@ -61,12 +61,15 @@ describe('Engine: fact evaluation', () => {
         operator: 'lessThan',
         params: {
           eligibilityId: 1,
-          field: 'age'
+          field: '{{whichField}}'
         },
         value: 50
       }]
     }
   }
+  const runtimeFacts = {
+    whichField:'age'
+  }
   let successSpy
   let failureSpy
   beforeEach(() => {
@@ -94,7 +97,7 @@ describe('Engine: fact evaluation', () => {
           value: true
         })
         setup(conditions)
-        return expect(engine.run()).to.be.rejectedWith(/Undefined fact: undefined-fact/)
+        return expect(engine.run(runtimeFacts)).to.be.rejectedWith(/Undefined fact: undefined-fact/)
       })
 
       context('treats undefined facts as falsey when allowUndefinedFacts is set', () => {
@@ -106,7 +109,7 @@ describe('Engine: fact evaluation', () => {
             value: true
           })
           setup(conditions, { allowUndefinedFacts: true })
-          await engine.run()
+          await engine.run(runtimeFacts)
           expect(successSpy).to.have.been.called()
           expect(failureSpy).to.not.have.been.called()
         })
@@ -131,7 +134,7 @@ describe('Engine: fact evaluation', () => {
   describe('params', () => {
     it('emits when the condition is met', async () => {
       setup()
-      await engine.run()
+      await engine.run(runtimeFacts)
       expect(successSpy).to.have.been.calledWith(event)
     })
 
diff --git a/test/engine.test.js b/test/engine.test.js
index 6030466..34b8b3b 100644
--- a/test/engine.test.js
+++ b/test/engine.test.js
@@ -3,6 +3,7 @@
 import sinon from 'sinon'
 import engineFactory, { Fact, Rule, Operator } from '../src/index'
 import defaultOperators from '../src/engine-default-operators'
+import Condition from '../src/condition'
 
 describe('Engine', () => {
   const operatorCount = defaultOperators.length
@@ -92,8 +93,7 @@ describe('Engine', () => {
       engine.addRule(rule2)
       expect(engine.rules[0].conditions.all.length).to.equal(2)
       expect(engine.rules[1].conditions.all.length).to.equal(2)
-
-      rule1.conditions = { all: [] }
+      rule1.conditions = new Condition({all:[]})
       engine.updateRule(rule1)
 
       rule1 = engine.rules.find(rule => rule.name === 'rule1')
diff --git a/types/index.d.ts b/types/index.d.ts
index b3d39e2..25b1072 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -1,6 +1,7 @@
 export interface EngineOptions {
   allowUndefinedFacts?: boolean;
   pathResolver?: PathResolver;
+  interpolation?: RegExp;
 }
 
 export interface EngineResult {