We’ve been here before. Procedural programming and goto statements was all the rage with BASIC and Fortan. Why the hell should any stop using a perfectly good tool for some new hot fad called OOP?
The last two years I’ve released apps using this new stack. They’ve help me in ways I never could with MVC & OOP. I worry less about breakages because I have less breakages. I spend less time managing test frameworks and more time on enabling and iterating on features to make my app more useful. It’s allowed me to empower designers and research teams to learn and iterate our product faster. The business gains more customers and learns quicker about their customers. Let’s break down the 4 most important tools and why they each make development easier and improve your company’s bottom line.
The most critical piece of new architecture is React. React improves so many issues with traditional UI development it’s hard to be succinct about it’s benefits. The crux of React’s benefit is that it uses pure functions for rendering UI in a component architecture.
Purely functional views allows React to separate business logic from UI. The problem with building a stable front-end application are the users. Users do random things in random order. They don’t understand the UI. The way to save user is to isolate them from the business logic code. React does this by making the entire UI is only a simple reflection of the underlaying static data, ƒ(d)=V. Using pure functions for the view means that given any static JSON it will always render exactly the same way.
The separation of business logic and UI in React makes code bases easier to grok. Collections of complex classes with multiple abstracts turn into just very basic functions in React. The last enterprise React app I build we had a new jr. developer add a new feature, by himself, on the first day and shipped it the second day. Building front end apps isn’t as hard as we all thought; we just had over complicated tools.
Everything has tradeoffs to consider. React is no different. It can be a struggle with existing imperative, mutable-based frameworks. There are migrations paths like using React for a section of an existing app or wrapping the older app inside a React component in order to opt out of DOM management. Lastly if the development team doesn’t see flaws in the existing complex OOP/MVC frameworks it will be a hard sell. React works best when you go all in.
Webpack enables vast improvements in the developer experience while also providing massive improvements in the user experience. Webpack with Babel can be a pain to setup and configure but there’s 2 very good reason why Webpack is critical.
As much Webpack improves the developer experience the bigger impact is for the end user and it will increase your company’s revenue. Google, Microsoft, Walmart and Amazon all have case studies showing that improving page load speed by even 1 second increased their conversion rates and revenue by 2 to 4%. In Walmart’s case that’s $240 millions dollars they would be leaving on the table. Webpack can massively improve page load with just a couple settings. Between code splitting and using responsive image (with the resize-image-loader) we cut page load time from 20 seconds to 2 seconds.
Redux is a state management tool built on functional programming paradigms. It’s a godsend for reducing bugs and reducing wasted hours attempting to repo bugs. The reason why Redux does this is because it strictly controls dependencies. Normally dependencies are thought of as the imports. But a more complete view includes anything that can implicitly mutate state. This would include Class that reference this, objects outside of the local scope, or even creating an object and returning it that someone who is able to can change its state. Simplification and an peace of mind comes from better limits on mutations. Redux uses static data and pure functions that are easier to unit test, lessens the need for mocks and delivers a higher level of guarantee against breakage.
Static data provides us a serializable snapshot of the entire application at any point in time. This lets Redux to record and later replay every bug that happens in production. Stack tracks and a QA’s reproduction steps leave a lot out of how to find the cause of a bug, the app’s state. Fixing a bug in Redux only requires the developer to download the user’s state, replay and rewind to inspect all the state transitions to see the cause of the bug.
The tradeoff with Redux is that it is a very prescriptive way to model business logic. These patterns aren’t always straight forward or practical especially for async, reconciling offline and online, and transparently getting data to/from web services. There are other tools and plugins to help each of these issues. Redux requires a rewrite of your codebase but delivers some huge benefits.
ClojureScript might be the last core tool in the list is also the most powerful one. ClojureScript reduces the fragility that many developers assume is inherent with coding. My primary advice when starting ClojureScript is to hold off all judgement until you’ve had some solid time with it. The common response I hear to ClojureScript is “I don’t like your face/syntax”. There are really powerful reasons for it’s syntax over dot notation if given a chance. Clojure has been around for 10 years. The tooling, language, community is extremely mature. ClojureScript’s combination of great tooling, better core primitives, async patterns and it’s improvements on static typing make it excel over other options.
All the primitives in ClojureScript can not be changed, ever. Mutations cause bug so ClojureScript is immutable by default. Immutable data makes it hard to inadvertently have a bug in one part of the code base affect things elsewhere. The normal push back is that immutable data wastes a lot of memory and is slow. In ClojureScript, it’s faster and uses memory better than most mutable data usage patterns*. Better core primitives offer different performance characteristics. Resist the urge to micro-optimize everything because it’s not like OOP.
The best feature is refactorablity and readability. Functional programing optimizes for improving developer effectiveness. The learning curve is high but the rewards are even higher. In only 3 weeks I went from not knowing a lick of ClojureScript to having a production ready app. The most amazing thing about ClojureScript codebases is that when I go back to it months later I can still read it. Even more astonishingly, I don’t have the dying urge to refactor old code. Typical refactoring in ClojureScript is very simple compared to OOP refactors. Most of my refactors are just moving functions to other files or separating the data structure from transforming data. Clojure codebases I’ve worked on are clear, declarative and concise. They rarely require jumping around to understand the data flow.
Static typing is not a goal; The goal is to have better error handling while writing, compiling and running your code. ClojureScript one ups static typing systems with Spec. Spec* does more than types by looking at the entire issue of data validation. Flow or TypeScript only gives compile time checking. The minute you hit a web service or access a JS lib that you didn’t create you can’t guarantee types. Spec are a uniform way to handle compile time type checking, run time data validation, run time asserting, and handle hard and soft errors. It can also create stub data for your tests, write your tests for you and even when a test breaks tell you exactly where and why it broke in your code. Yes, you read that right: Spec will generate your tests for you. It also will test conditions that you never considered before. After using Spec other testing practices feel like building sand castles; they takes longer to maintain with poorer results.
ClojureScript async is joy to behold. Promises aren’t practical for all types of asynchronous work. ClojureScript core.async is like the green warp tubes in Super Mario Bros. In Super Mario Brothers, one green warp tube can send Mario anywhere else in the level, world or game. This is what ClojureScript’s channels do for your code base’s async work. It uses Go Lang style channels that can be created in one place and easily shared in any other arbitrary place in the code base. It gives you all the benefits of Rx without any of the complicated function coupling and stream merging problems. Channels makes async code read like linear code which reduces the reader’s cognitive overload so there’s less complex abstracts to stumble over. With all those amazing features ClojureScript also one ups Go Lang channels by adding transducers on channels.
It’s a big word but they are basically just a function with one argument. (user) => user.firstName could be used as a transducer. Transducers use simple, agnostic functions to handle multiple transforms in a highly performant manner. Using agnostic functions means reusability is much higher. Anyone who resonates with DRY methodology (Don’t Repeat Yourself) will love transducers.
To understand Transducers better let’s take a second and go back to the Super Mario analogy for channels. In the game we have a function when Mario touches a fire flower he turns into Fire Mario. We can put that same function on a green warp tube “channel” and when Mario come out of the tube he’ll be Fire Mario. It’s the same function but used in a different context. We can also have another place in the code base add a super Tanooki transducer to the same “channel” and Mario will come out as Fire Tanooki Mario. Pump all of Mario’s friends through the channel and they get the same transforms with just simple, agnostic functions.
If you’ve read this far the only thing left is it to take 5 minutes to try out ClojureScript for yourself. One command (see below) will install everything you need to start live coding ClojureScript with the Atom editor. After you’ve seen how great ClojureScript tooling is, check out Reagent’s intro to ClojureScript and Andrew’s tutorial series to go from JS to ClojureScript. If you have any questions check out the Clojurian Slack channel or message me on Twitter @puppybits.
apm install parinfer && \
brew cask install java && \
brew install leiningen && \
lein new figwheel hello-cljs -- --reagent && \
cd hello-cljs && \
atom src/hello_cljs/core.cljs && \
lein figwheel && \
1/ Walmart page load speeds improve conversations
2/ Simple made Easy — Rich Hickey
3/ Immutable data — David Nolen
4/ Spec: Agility & Robustness —Stuart Halloway
5/ Clojure Help
6/ Clojure Docs
7/ Intro to ClojureScript with Reagent
9/ 2 second page loads over 3G networks post
10/ Pure View Functions