Creating a Validation Library in JS Using Propositional Calculus
One of the cornerstones of building the bank of the future is ensuring the correctness and validity of our data. Banking is, after all, an industry that demands extreme precision and correctness.
Recently the platform engineering team at Earnest wrote a validation library that developers can then use to easily compose rules around any set of data and, well, validate it. In order to maximize reusability as well as establish confidence in the correctness of our validations we opted to write a validation library.
At its most basic — the validation library we created does what’s known as boolean algebra and it does it using the functional programming paradigm. (We heavily use Ramda.) We model all our rules as predicates, which are simply javascript functions that always return a boolean. This allowed us to introduce the concept of combinators which are simply javascript functions. These combinator functions return predicate functions. This allows us to easily combine predicates making the code readable for non-coders and allows us to avoid massive `if-else` logic.
For example, here is what the `isEqual` and `isAfter` predicates look like.
While these look deceptively simple, they allow for currying which can be incredibly powerful when writing a series of validations that use some of the same values.
To write the basic predicates we use SKI combinators which are simply javascript functions (notice a pattern? 😁). Using these combinators you can build arbitrary functions through combinations of them (there’s a reason they’re called combinators). One example of combining the SKI combinators are what’s known as combinator birds, of which you can see several examples here. For example, we use the Dove combinator bird in a few places which is a Bluebird combinator applied to a Bluebird combinator. (Yes, I realize how ridiculous that sentence sounds, but also this is incredibly fun stuff once you get into it. 🤓)
Moving on to the combinators… to get an idea of the combinators we wrote that can combine predicates check out the `S`, `K`, `I`, `alwaysFalse`, and `and` combinator as well as `and`’s helper method — `variadic` — below.
To insure our combinators are correct we implemented all of the basic and derived laws of propositional calculus via javascript unit testing (we used Mocha for the test runner and JSVerify to write property-based tests.)
Here’s an example of two of the many tests we wrote using the basic and derived argument forms from the propositional calculus table linked to above:
This is ✨super cool✨ because the combination of testing all of these laws and property-based testing insures that our logic is sound even before we write our first validation rule! We are leveraging already proven mathematical laws to ensure correctness in our validation library.
At this point you may be asking yourself, “Why not simply write a bunch of `if` statements? This seems overkill!”. Well, I’m glad you asked. Mathematics has already codified these rules. Calculus has already given us a system to do exactly what we needed to do with this project. If we’d have went the route of writing `if else` statements for every rule we’d have to write *a lot* more tests for each and every rule, have a much higher chance of logic errors, writing rules would take a lot more time, rules would be incredibly less readable, and — finally — because we can reuse predicates we have a much more standardized approach ensuring stuff like `is between two dates` and `this or that` are done exactly the same every time. In short — the rule engine gives us a strictly smaller domain specific language (DSL) where we can reason about rules and validation in a declarative way.
We’re incredibly proud of this library as well as the validations (and ultimately the huge business value) we were able to quickly create as a result of.
And if any of that sounds interesting to you Earnest is always looking for more great engineers to join the team!