Instamojo 1.0 (Oh so 90s)
Backend Driven Development — A Django WebServer rendered and served the templates pre-populated with all data that might be required by the front-end. Every single GET/POST request was handled via a browser-level redirect. Our early engineers loved Web 2.0 with a no-AJAX approach to all problems, and to be honest, our users loved the simple, elegant interface that just worked for them. Full page reloads were not a problem for our users and we didn’t want to spend time-solving problems that didn’t exist back then. Our Payment Pages, still, are rendered and served by the Django webserver.
If it ain’t broke, don’t fix it.
Then came Q2 2015 and we wanted to ship a bunch of features for thousands of our merchants who used our dashboard on a daily basis. This was also the time when we started moving to smaller single page applications within the dashboard. Which meant moving to Design Driven Development. Every feature began with talking to real users which contributed to our ongoing UX research, which followed by Wireframes, Information Architecture, prototyping and finally got a visual layer which put it all together.
The revamped KYC (Know Your Customer) Flow and Phone Verification were 2 such features that were written in Vanilla JS using RequireJS for assets management, dependency injection and bundling.
Every such feature turned out to be a stand-alone application in itself, which was a boon for maintainability but a bane for Getting.Shit.Done as every app needed its own architecture and yet another thought process. KYC was given a Event Driven Application Design while Phone Verification took a more Functional Programming approach. This didn’t scale well as the code wasn’t DRY for similar goals.
Instamojo 2.0 (Oh so glamorous!)
Facing difficulties with shipping features on time, we took a bold call of re-writing our entire merchant side client codebase to be under one architecture to enable same mental model for everyone in the team. We set on to create a replica of existing features, route by route, investing heavily in the choices we make as it would define how code would be written months down the line. Code written by anyone should be understandable, editable and fixable by anyone else in the team. Only a dream in the mainstream Javascript world, isn’t it?
Choosing the Architecture
Choosing the right architecture is important. However, there’s no silver bullet to it. Defining what’s “right” for you and your organisation is more important to begin with.
Some questions we tried answering, (back then front-end being a 1 member team) were –
- Which framework would require least amount of time to onboard someone onto our codebase?
- Which framework offers reasonably small API surface area so that we don’t end up with broken builds with every upgrade?
- Which framework would give us the building blocks and allow plugging in in-house or 3rd party extensions making it more powerful?
- Which framework would let us build our architecture around our opinions instead of forcing it on us?
ReactJS
Our in-house engineers had experience with AngularJS, ReactJS and BackboneJS. Unanimously, we decided to go ahead with ReactJS as that answered positive to all our questions.
Getting used to “The React Way” of doing things takes a while. If you’re not too comfortable/productive on Day 1, give it some time. Don’t let the low API surface area underwhelm you. React is perhaps the only library which has gathered aggressive momentum and community involvement post the jQuery era. Give it 5 minutes and let it sink in. At the same time, don’t choose it because it’s hot. Let the choice be justifiable. (Yes, the jQuery era is past us)
Back to React, the component lifecycle provides all necessary hooks to manage data, bind/unbind events which help in keeping apps memory consumption in browser in check. Working directly with the DOM, although highly discouraged, is possible if you depend on libraries that depend on the DOM. This React JS Component demonstrates how we use React’s “refs” to disable/enable form controls when a network call is initiated/completed.
Initial Decisions
With half baked understanding of FluxJS and the dispatcher that ships along with it, we started using it for our first couple of features. (Believe me we did try understanding it well. It just wasn’t that simple.) As more and more Data Stores started depending on each other, the waitFor’s kept increasing. Something didn’t feel right about it as “flux cannot dispatch in the middle of a dispatch” kept popping up every now and then. These waitFor’s didn’t belong to our codebase. But we had to write it to please the framework lords. It reminded me of prototype for a newbie to the Javascript world. “It should be abstracted from me”, I kept telling myself. But how can the smart folks at Facebook build something a little off, right? Wrong.
Redux
Dan Abramov’s Redux was picking up a lot of interest in the community. After a bit of usage, it seemed like it could be React’s new best friend.
If Redux ever has a book, it should be called Flux: The Good Parts.
Redux: The Best Parts
- Middleware: Backend developers and front-end developers aware of ExpressJS would have used middlewares. Redux brings this to mainstream front-end Javascript 👏. We route all our API requests through a couple of in-house middlewares. We open sourced one of our middlewares (stripping out Instamojo specific code that won’t be useful otherwise) that demonstrate how we handle app-wide 401s, 403s and 404s in exactly one place. Everybody knows where to add support for a new response status from our APIs.
- Top level Smart Components: Smart components are React components aware of Redux Store. react-redux brings this magic to React and Redux. The idea is to make a pyramid by having an entry point into the app which Provides the store to all connected/smart components. Each such Connected component then takes the responsibility of passing down relevant props and functions to several tiny dumb components that are not aware of redux.
- A Very Clear Mental Model: Functional programming. No side-effects. Encourages using Immutable data structures. Functions calling functions which do something, one thing at a time. No race conditions, no dealing with dispatchers and waitFor’s. Just pure functions doing their thing.
Looking back
It took us 3 and half months to re-write our entire codebase. With 1 developer working full time on it and 1 joining halfway down the line. This new developer, having very little experience with JS frameworks, was able to ramp up his skills in couple of weeks and was productive from Week 3. Looking back, I think it was a very wise choice to invest in re-writing our codebase. We are now able to ship features at a very rapid pace as we end up reusing a lot of code focussing only on the feature at hand with no grunt work to be done.
What’s Next
- First Render on Server: We’ll very soon be relieving Django from the responsibility of dealing with templates. Offloading that responsibility to a NodeJS server which will be the front facing server for all incoming requests. This will enable us to pre-render the React Components on the server and send it down the wire for that server side feel to it.
- Real Time Push Notifications: This NodeJS server will now also send Push Notifications on the browser and mobile devices informing of the payment you receive, in real time. We’ll even add a cash register sound to it, if we receive enough requests for it.
-
- Offline first: Very soon this dashboard will work offline, thanks to Service Workers. If you haven’t heard about them yet, I urge you to read Addy Osmani’s blog on Progressive Web Apps. We’ll be taking a network first approach for all our API calls and caching all static resources to be served offline via Service Workers. This would be a huge behind-the scenes-feature for Indian Merchants dealing with flaky Internet Connections operating out of tier-2 and tier-3 cities on mobile devices.
- Add to Home Screen: for App-like-interactions (beginning with Chrome and Firefox beta, both on Android). This has been a long standing demand from a lot of merchants. An Android app which will be way faster than loading the website as the core shell will always be cached. Thanks to service workers and the manifest file, this would be possible without us having to build a real mobile app.
Building immersive apps using web technology no longer requires giving up the web itself. (https://t.co/6fpu9WhsUk)
- More Features: The frequency at which you hear from us will go up as we’ll be building tons of features at a very rapid pace now.
All this is possible and simpler only because of the approach we took for the entire re-write.
As for now, brace yourselves as we get ready to unleash the mojo.
P.S. This article was originally posted on medium: