If you've ever filled out a commercial form, you know it's just a set of questions frequently broken up into sections, often bordered in some way: Your name, your address, perhaps a former address. For home insurance there might be sections on the existing property, inside and out; previous claims against existing insurance; extra coverage for things like pets or jewelery; bonuses for things like fences, alarms, and fire suppression equipment.
BFDLang is mostly about asking questions. Those questions will be collected into sections, and maybe those sections will be collected into higher-order sections. Each question has the text of the question, the kind of input (free form or multiple-choice, and if multiple-choice, a list of the choices), and then most importantly, a set of rules for whether or not the question should even be asked.
This last is most important; bureaucratic forms are absolutely no fun at all, so the biggest idea in BFDLang is to not ask questions that confuse the customer. If the customer is insuring a mobile home, questions about the basement would only be confusing; if the customer is insuring a condominium, asking if they have a two or three-car garage is annoying.
Those rules describe a program. We have a compiler that converts all of these into the elements of an Abstract Syntax Tree (AST); a question's ruleset is turned into an object with functions ready to execute, and a section's ruleset contains child sections and questions, and so forth.
Finally, we have the interpreter that, interaction by interaction, validates customer input and runs it against that AST, producing a new tree we call the Policy Object Model.
The Policy Object Model is a static tree that's pretty ideal for handing off to a web-based library or framework, which then, keystroke by keystroke, click by click, sends events and input back into the interpreter to produce a new POM. This may add new questions, or whole new sections; it may make other questions or sections go away.
The Interpreter and the Input History together create the POM, which
the user fills out to create new POMs, iteratively, until the POM's
top-level object flags as
valid, at which point the customer's
application is sufficient to be accurate and binding.
Almost ten months ago, we had a demo to put on for VCs, and the number of shortcuts taken inside BFD were terrifying. A big chunk of the compiler actually ended up inside the ruleset; a big chunk of the interpreter intermingled with the compiler and the rules. It was ugly. It was, sadly, what you do for the VC Dog & Pony Show.
To which I say, YES! And that's a good thing!.
I did the refactor in very progressive steps. We had a God
that could both change labels on the fly ("How long have you lived at
this house?" vs "How long have you lived at this condo?") and the
multiple choice answer collection (states have different rules for
some questions, so different answer sets were needed); I statically
label field and made it standalone, then did the same
thing for the choices, then did away entirely with the God Object. I
then removed the compiler layer that was embedded in the ruleset,
creating very strict rules for how they were to flow through the
system, and finally revised the interpreter, giving it a stack to
better manage dynamic answer sets (no more "If you have more than one
pet, please add them on one or more copies of Form XYZ-11111").
Each and every step of the way, I thought about what I was trying to say, how I wanted to say it, and to describe those expressions in types.
I found over 180 bugs in the course of the refactor, all thanks to TypeScript. It's entirely possible most of them were "theoretically impossible," edge cases so edgy you'd have to do something weird to trigger one (we have a pretty good test suite, too), but this is a library we intend for institutions that sell loans and leases to integrate as part of "insuring the leased or purchased product" process, and I want to make it difficult for their developers to make a mistake with my library.
Was it frustrating? Only if you find your own foolishness frustrating, which I frequently do. That's why I have tools. That's why I use TypeScript.
BFDLang 1.6 is in a much better place than 1.2 was a year ago. The next really big initiatives with it go in two different directions: first, to take the customer input and, wherever possible, avoid changing the POM object, re-using as much of it as possible, returning a reference-on-remain, copy-only-on-write version with every iteration. This will allow web frameworks to use basic object comparison to avoid re-rendering whole parts of the display. The other is to finish the refinements to the BFDLang itself (the "include this question" rulepack is still messy) so that we can write tools to describe the forms, rather than teach administrative people the little bits of estorica and land mines within. And it would not be in a place where either of these initiatives were possible without TypeScript.