I am a firm believer that technology should first and foremost solve problems for customers. After a number of discussions with development leaders, product leaders and other stakeholders, we arrived at a set of goals and constraints that focused on the customer's experience with the product.
We wanted to:
- Provide a snappy, fast-feeling user experience that felt like a desktop application
- Accommodate the various screen sizes and devices that our customers use
In addition to the customer-facing goals, we had technology-focused goals, including:
- Be able to rapidly iterate on the product
- Be able to accommodate specialist roles as hiring developers that could work across our full stack was proving to be challenging
- Enable teams to deliver code rapidly by reducing inter-team dependencies
- Invest in monitoring and analytics from the start
While we had ambitious customer and technical goals, we also had constraints from stakeholders that forced us to make pragmatic decisions about how we built the new product.
The first major constraint was time. Our stakeholders wanted an initial prototype of the product in market within six months. This meant we couldn’t start entirely from scratch and would need to leverage or salvage parts of our existing backend systems where possible.
The second major constraint was mitigating disruptions to our customers. We needed to offer a migration path from one platform to the other but it had to be “one click.”
Our New Technology Stack
Taking the various goals and constraints into account, we decided upon the following stack for the new FreshBooks:
- Leverage our existing Python Web-services and schema: By re-using the existing backend web-services, we were able to get a working product much faster, and enable customers to migrate more easily in the future.
- Build out new features using well understood technologies and frameworks: In order to achieve the speed to market that we wanted, we didn't have the luxury to learn several new technologies or frameworks.
Everything Old Is New Again
We have continued to use Python for all the new services we built to power new FreshBooks. Python has proven to be a stable, easy-to-learn-and-read programming language. It has fantastic libraries that cover almost every task. It is also a language that we have experience operating and deploying.
MySQL is currently the mothership where all our data lives. Because we wanted to enable product migration, and reduce risk we decided to continue using MySQL. While Postgres was a very attractive option, we didn't have the operational expertise to confidently run Postgres at the required scale in the timeframes we needed to hit.
RabbitMQ currently powers all the background asynchronous work in FreshBooks. It is a rock solid message broker, and while we entertained using Kafka for a period, the switching cost would be enormous and we couldn't justify that cost with equal amounts of product/scaling benefits. By reusing large parts of our existing stack, we were able to meet the timelines, and manage risk.
New and Shiny Technologies
We've been able to stay very current with Ember and never once needed to do a large rewrite. I’ve heard the opposite from the React and Angular developers I know.
We deployed a number of new backend services to power new features, such as full-text search, projects, online payments and time tracking. Where possible, we created new applications where the problem domain allowed solutions to be split.
For example, our full text search is powered by a standalone backend application and ElasticSearch. By separating applications by domain, we enabled developers to focus on problems, and reduce risk during development and deployment.
In addition to reducing risk, multiple backend services allow us to disentangle teams from each other. In our mothership application, teams would often get blocked from releasing as other teams were constantly merging code in. We addressed this with release schedules, but that is a process we wanted to move away from and more, smaller applications was one of the tactics we used.
Looking Back Now
The Pros: A rich client-side application allowed us to deliver the customer experience we wanted. Smaller services allowed us to mitigate risk and contributed to delivering code faster to production.
The Cons: The complexity of smaller services came at a cost. It became increasingly harder for developers new and old to understand how FreshBooks worked. This forced us to invest heavily in tooling, automation and documentation. The additional platform complexity made building and maintaining acceptance tests harder.
We ended up with technology that shares a great deal with our previous architecture, and we gained a number of new tools and processes. By critically evaluating our technology stack, we were able to reuse what was working well, and replace what was broken.
At the beginning, several members on our team wanted to scrap everything and start fresh. Thankfully, we resisted the temptation and discovered that our existing technology and desired stack had a lot in common. Starting over would have exposed us to more risk and reduced our chances of success.
As developers, we’re often too ready to dismiss ‘legacy’ systems when dreaming about the future, but it is important to remember that these systems are often the ones that give us the freedom to consider re-platforming, and we should always look for ways to integrate the good pieces of the past into our visions of the future.
Mark Story, Principal Developer, FreshBooks (opens in new tab)
Image Credit: Helloquence / Unsplash