Stop that DSL!
Yup, hold it right there.
That is a weird saying from a Ruby enthusiast though. So how come?
The DSL pandemic
At some point part of the Ruby community decided we needed a DSL for #everything. Instead of a DSL, I might focus the energy on improving Ruby’s speed or garbage collection but after all I’m just a fervent user of the language and not the entire community.
And to be fair, it can be really easy to build or use a DSL in Ruby (especially in Rails!). Logically many developers use a few of them in their projects. I’ve been guilty of the exact same thing, so don’t worry.
But after the initial shock of learning 4241 new semi languages (‘how do I convert to a machine readable number here again?’ and ‘ow boy I cant access the inner values of a Hash here’) I started to wonder whether all these DSLs were actually useful.
So I have this Turing complete programming language just so I can switch to this constrained language. Which I have to learn. While I have a language available which can already do that. Which all of my devs already know.
The story of JSON
At inventid we develop API first against a test suite. As a result we generate an awful lot of JSON data for our users (and PDF and email), such as very own frontends. Since there is a library for that, we used to use jbuilder to generate this json for us.
Dont be fooled, you need to write your jbuilder files manually, which would hook into your controller to generate output based on the instance variables defined in the method called. So not really a free lunch so to speak.
After a while not working on the json responses (I do devops for our payment gateways) I suddenly had to fix some minor issue. Ironically I found (all of a sudden) the jbuilder syntax extremely annoying.
- I had to google an awful lot
- The assignments to instance variables determined jbuilder input (not the methods return value!)
- A certain serialization was exceptionally slow (taking Ruby’s performance into account)
I did not have a real fun day.
Apart from that, Irealized that knowing a DSL serves very little. Your job as a developer is (in that order):
- Solving a problem
- Using a programming language
- Where you might use a common library
- Which may include some Domain Specific Language.
So all in all, knowing that DSL is not a skill but something you might need ocasionally.
Any alternative then?
Let me introduce you, a Ruby developer, to a language called Ruby. It’s a Turing complete, developer friendly, and very fast (#justKidding) programming language. It is very capable of handling data transformations. As a result you should really check Ruby out!
It may sound lame, but the best tool might simply be the tool in your hands. If that tool suffices, you might not need a more specialised one at that moment (who hasn’t used a voltage tester as a screw driver?!). As an example, jbuilder is the tool you don’t need!
A case study, transformers at inventid
inventid used jbuilder a lot. And we (I) found it annoying.
- Separate files.
- Unusable to send JSON upstream in a request, as it only generates responses.
- Weirdly nested DSL (useless abstraction)
So we (I) decided to run a test with simple Ruby code. Did we really need them? Or could we simply create JSON responses using actual Ruby and Rails code instead?
The actual test was simple. Take the slow endpoint I was trying to work on, and switch its implementation from jbuilder to Ruby. How? Using basic transformers!
Let me show you the code which had to be modified. First of all I did some minor changes to the controller:
Secondly, I added a few lines of Ruby:
And finally I ditched a bit of jbuilder code.
So to evaluate, not a lot of code changes (please do note I had to add quite a number of transformers since this was a descendent model). And strangely enough this few minute test yielded some amazing results:
- It was no worse in terms of code quality compared to jbuilder.
- It was a hell of a lot faster on my machine.
As part of the test, this code was shipped to production to check whether the performance improvement also manifested itself there.
I don’t want to tell you the conclusion. Let me show you the graph showing the view performance of the specified endpoint instead! I’m pretty sure you can figure out when the deployment to production happened.
This raised the question: should we continue to use this dependency? A quick check within the office showed nobody particularly liked the jbuilder syntax. Nobody was really impressed with its speed. And nobody acknowledged they felt it enabled them to develop faster.
So within a few days we started migrating a lot of jbuilder code to plain Ruby. Additionally we also migrated the properties which could be modified to the same file.
All in all, this migration turned out to be a success! It reduced our API response times by over 70%. Definitely worth spending a week on :) Others reported similar improvements.
It raised a question though
Why did we pick these DSLs over simple transformers? Was this actually worth slower code, and learning an additional language to simply manipulate?
To be honest, I feel like the community may often pick some DSL over the plain language. And often, there is not a good reason.
inventid suffered from multiple library abandonments over time as well. We used squeel to make our AR syntax nicer. Then we encountered a bug (which was not fixed). And finally the library was abandoned.
All in all, picking any library (or DSL) introduces future maintainability risks. Things can go wrong with those libraries, and you will be stuck with those or incur huge refactoring costs. And everybody has to learn every single DSL as well. May be even know and recognise pitfalls too.
So is a DSL library useful for your project?
It might be, but definitely check the plain language alternative first! No DSL is a silver bullet which will save you. When in doubt, simply don’t go there. Be plain, be boring, and be stable in the end.