Tuesday, July 18, 2017

On Software Engineering with Elm

This blog post explores Elm following software engineering methods. So, get ready for some hardcore engineering talk as opposed to pure software development geeking out.

My entry into Groupon.com as a software engineer in late 2011 coincided with an explosion of JavaScript frameworks, each claiming to be the end all be all that would solve all your JavaScript troubles. We happened to adopt Backbone.js at Groupon, and I, as a software engineer, sadly saw extreme over-engineering of the front-end with Backbone.js going through multiple components just to submit a simple form.

Sadly, nowadays the situation is worse as I have recently worked on a React-heavy app and was surprised by how much code I had to maintain just to submit a simple React form. It had many levels of indirections and too much componentization, definitely swinging the balance in the wrong direction.

In short, I am pretty skeptical when encountering new JavaScript technologies that push developers too far from writing basic JavaScript. For example, I still think jQuery is sufficient for most JavaScript needs on its own or augmented by underscore.js (or something similar). I also think CoffeeScript is close enough to JavaScript to make the jump tiny and worth it.

What is Elm in any case?

Elm: a typed functional programming language that compiles to JavaScript

Features as per their website:
- Compiles to and interops with JavaScript
- No runtime exceptions, friendly hints at compile time instead due to strong static type checking with type inference
- Great performance (fastest compared to Angular, React, and Ember)
- Enforced Semantic Versioning

Now that we had an introduction to what Elm is, let's continue our conversation on software engineering with Elm by studying it from a software architect's point of view.

1. What are the functional requirements or use-cases regarding front-end development with JavaScript?

During my career in business application web development, these are all the use-cases I've encountered as per my memory:
- Highlight or flash an element upon data entry or making a mistake
- Open or close a drawer containing information
- Expand or shrink a text area containing an article
- Animate data graphs like piecharts
- Light boxes and dialogs to give user information or gather input
- Scripting of video and audio
- Parallax, that is ability to scroll page into via with the trackpad or mouse like going through a pop-up book
- Linking user actions to partial views on the screen (think of browsing emails in outlook.com affecting the master email view)

2. What are the non-functional requirements that affect technology decisions on the front-end?

- Productivity: how productive can good developers be at pumping out features that are satisfactory for the customer?
- Maintainability: how easily and quickly can good developers maintain existing code when adding/modifying features and fixing bugs?
- Stability: does the technology facilitate avoiding bugs and enable writing reliable front-end applications?
- Performance: having just enough response time for the user (no delay longer than 2 seconds, with the average being half a second or less)?
- Security: is data protected when submitted over the wire to the server?
- Usability: how user-friendly and effective user-interfaces can be.

That should be enough to get us moving, albeit there might be non-functional requirements like Legal Adherability (e.g. HIPAA Compliance), Accessibility, and Localization/Internationalization, but let's stick with the ones above for now.

3. Discuss Specific Technologies

A. Vanilla JavaScript

So, if I were to rate vanilla JavaScript on the points mentioned above for developing front-ends, here is how it goes:
- Productivity: 7 (JS has improved quite a bit with newer versions, but I still find utility libraries like jQuery and underscore.js more powerful than plain JavaScript)
- Maintainability: 2 (It's JavaScript man! What did you expect?!!!)
- Stability: 2 (Ditto as Maintainability)
- Performance: 9 (for the use cases mentioned above, JavaScript is more than fast enough. I've even seen it run 3D animations quickly enough)
- Security: 8 (decent support in browsers for blocking cross scripting attacks and hiding passwords seems to shield us well enough for today)
- Usability: 5 (it's hard to implement interactive and visual front-ends with vanilla JavaScript)

B. JavaScript DOM/Utility Library (jQueryjQuery UI, underscore.js, and other similar small libraries)
- Productivity: 9
- Maintainability: 4 (jQuery and _ require terser code, thus less code to maintain, still it's JavaScript man.)
- Stability: 4 (less code means less chance for error, still it's JavaScript man.)
- Performance: 8 (still fast enough for the use cases mentioned above, nothing to complain about)
- Security: 9 (jQuery works hard at protecting you when performing Ajax calls via good APIs)
- Usability: 7 (jQuery and _ certainly help in building more usable user interfaces)

C. JavaScript Framework
- Productivity: 8 (productivity takes a dip due to increased complexity, even if the framework was React, one of the simpler frameworks)
- Maintainability: ranges from 2 to 7 depending on the framework and use-case (for most use-cases it takes a dip due to increased complexity)
- Stability: ranges from 2 to 5 depending on the framework and use-case (takes a dip to complexity or improves a bit for highly interactive UIs)
- Performance: ranges from 5 to 10 (badly written framework JS code, which is way more common than you think, yields worse performance, even with React. Otherwise, performance only improves from a framework with well-written code addressing a highly updating UI problem like stock ticking or live news updates)
- Security: 7 (takes a hit in badly written over-complex framework code)
- Usability: 9 (this might be where frameworks shine, as they enable building very intricate user interfaces when needed)

D. JavaScript Interop Language (CoffeeScriptElm, Clojurescript, etc...) (judged independently of B and C, albeit it is additive to them)
- Productivity: ranges from 5 with Elm and Typescript, to 7 with Clojurescript, and 9 with CoffeeScript (haha, this was not researched... just a gut feeling. To explain, Clojurescript maintenance will counter balance its productivity benefits. It's hard to find developers committed to it on top of JavaScript. CoffeeScript is on the other hand close enough to JavaScript not to require much extra commitment. Elm and Typescript defeat the point of JavaScript and bring productivity down with their complexity. Few people will do very well with them I admit, but just few. Not something everyone is interested to invest in and struggle through finding hires for. )
- Maintainability: ranges from 1 to 7 (Ditto as Productivity)
- Stability: ranges from 1 to 7 (Ditto as Productivity)
- Performance: ranges from 9 to 10 (using these languages might improve performance due to taking care of optimizing JS for you)
- Security: ranges from 8 in dynamically typed languages to 9 with statically typed ones (types can improve security since they limit vectors of attack)
- Usability: ranges from 3 to 9 (Ditto as Productivity)

4. Summary

I am not so excited for Elm, Clojurescript, TypeScript or any language that moves too far from basic JavaScript because:
- I want to find hires who are good enough in JavaScript first let alone some esoteric language
- I want to do as little JavaScript as possible and keep web apps focused on serving customers without confusing them with overly flashy features
- I want to avoid over-engineering with something as powerful as Elm that acts like a nuclear device shooting a fly
- I want to avoid pre-mature optimizations. Most of the time, jQuery runs fast enough, and in rare cases, I could optimize a bit and succeed without something like Elm or even React.

Gotcha didn't I!? I didn't include any Elm code, and it's on purpose. Happy software engineering!

No comments: