Moving our UI to the next level
This is the first post of our blog series, — the story of building a modern web app.
Building a product is hard, — a balance between writing great code and moving fast. Over the last three years we built Tallie from the ground up. It has the right features, looks good, and used by hundreds of companies on a daily basis. We feel good about where we are at right now.
We are not done yet — perfecting a product is even harder. To deliver the best possible experience we need to solve some exciting engineering problems. For us, this year will be about leveling up our UI and pushing the boundaries of the browsers.
Our current stack
We started with a clean plate. Only had an idea about the product, but no designs or specifications. No front-end codebase or UI engineering team. We shaped the product along the way, and learned a lot.
As a first step, we had to pick our front-end stack.
CoffeeScript
With years of JavaScript experience we understood the strengths and weaknesses of the language. Feeling adventurous, we decided to dive into CoffeeScript instead. The main reason was to help the team write better code by enforcing the right patterns. It also gives us the confidence that the compiled code is syntactically correct. We understood this is a controversial topic, and I’m not saying this is the only, or the right way to build a product. But after 25k lines we are happy with this decision.
Knockout.js
Building a lean startup means not knowing how the product will look in 6 months, especially in the early days. To get started, we built separate pages for the different parts of the product. Later these pages evolved into complex views, handling lots of data and user interactions. To support this multi-page structure, we chose Knockout.js, which is strong at the UI data bindings. Looking back, it made sense to support the lightwave pages and ensure the application UI is always in sync with the data.
Libraries / Tools
We experimented with a lot of open-source libraries to solve smaller problems. Here is a few that proved to be mature enough for production.
- We built our dropdowns and typeaheads on Select2 and Typeahead.js
- We use SignalR to send down real-time messages to the client side
- Our drag and drop and file upload features are built on HTML5 DND and File APIs.
- Bundler compiles, bundles and minifies our CoffeeScript and LESS files
- Grunt.js runs our unit tests with mocha.js
Over time we learned a lot about these technologies, and how to build a complex web app, — which we are planning to share in the future.
But the front-end industry is moving fast, and there is always room for improvement. Here are a few problems we see coming our way this year.
Single Page Application
To create a more-native-app-like product experience, we are converting to a single page application. This will enable us to create more robust features and make the experience faster.
Architecture
We are using Knockout.js — powerful with UI bindings, but the rest of the code is on us. A single page app will need a more robust architecture. Either powering up our current view layer, or introducing a new framework for this. Finding one that plays well with the current codebase will be tricky, but not impossible. Durandal.js feels like an obvious choice, since its built on Knockout. Angular 2.0 looks promising, having the best of Durandal and Angular. Flux has an interesting concept, and the Facebook libraries look really strong. We need to pick the one that fits our current architecture the most.
Dependency injection
JavaScript and CSS adds up, and you can’t just load 50MB of assets on page load. Bundler is breaking these assets to smaller bundles, but we are not happy with it. It is not supporting SourceMaps, hard to fit in our Grunt flow and we will need to load assets on the fly. Our options are moving to RequireJS or Browserify to support the single page architecture.
CSS
Took us 25k+ lines of LESS to get to the current product — a lot of styling. And a customized Bootstrap 2.3. While there are best practices for complex JS apps, not that many for structuring styling. It will be quite a journey to get our LESS codebase under control again, and upgrade to the latest Bootstrap.
Node.js
Our back-end is built on the .NET MVC framework, which serves us well. But on page load you have two options — use Razor templates, or dump the data in a JSON and let Knockout display it. While this second way speeds up development, causes some delay on every page load. With Node.js we could use the Knockout templates to render the HTML on the server side.
Quality
We all strive for a smooth product experience. Quantifying quality is challenging but there are things we can improve on.
Stability
Eliminating all the bugs is hard when moving fast — but better tests can help with it. We already have unit tests for data models; the tricky part will be to expand these to the views. To improve the code coverage we are implementing TDD in the team. We are also adding automated UI tests for more confidence when shipping the code.
Performance
We deal with a lot of data — that is pushing the limits of the browsers. To build a fast product we are constantly working on eliminating the performance bottlenecks. We have work to do on both the CSS and the JavaScript side, — and need to be wise about what data to render.
Tablets
The best way to get receipts into Tallie is to use our native iPhone and Android apps. Our users do everything else on a computer, that’s why we tailored that experience. But we understand this is changing and more people will use it on the road. We need to make sure our tablet experience is as smooth as using it from desktop.
Static Typing
Dynamic typing makes JavaScript a powerful language, but after a certain size, things get tricky. While CoffeeScript is doing a great job hiding some shortcomings of JS, but this one is not solved. Our friends at Facebook just open sourced Flow that brings static typing to JavaScript. Although Flow is not playing well with CoffeeScript yet, this is definitely an interesting area to look into. Introducing static typing in our codebase will result in less bugs.
Team
As the front-end team grows we need to enhance our processes to foster learning and improve productivity.
Environment
Currently Bundler and Grunt split the tasks during the deployment flow. Our plan is to push Grunt.js into the spotlight — compile, minify, bundle and lint code, run unit and automated tests and generate docs. We are also moving all our libraries over to Bower and npm for an easier maintainability.
Code Review
We have regular code reviews but we need to involve them more in our daily routine. We believe code review can turn into the most important step for writing quality code, and learning from each other.
Documentation
As we grow more and more engineers asks the same questions — starting a code style guide will help with this. We are a big fan of productivity: no one has time to write docs — well commented code should speak for itself. That said, there are couple of good tools to auto generate docs from code comments that we need to explore and incorporate.
There is much more cool stuff out there for front-end engineering these days. And even better improvements are coming with ES7.
But we are not there yet. We don’t have time to rewrite code just for the sake of rewriting code. Our story is about to find the right way to make our UI a magnitude better. Please join us for this exciting journey.
Follow me @korsosm for more updates.