We rebuilt our entire application
We had technical debt, a framework nobody knew and a bottleneck from hell.
October 11, 2022
How did we get here?
As I was creating features, I acknowledged that I was producing technical debt. There were particular moments that stuck out to me when we were building Fathom for the first time.
For example, in the interest of shipping fast, I would duplicate code and not put any thought into the structure of the code. Some areas of the code could've opened up an Italian restaurant with all the spaghetti they had. And, as anyone who has built an application of significant size will know, this would come back and bite us down the road.
The original tech stack
While building Fathom, I worked full-time building software using Laravel and Ember.js. This combination was epic because it brought an opinionated frontend framework to the table, making it easy to build snappy Single Page Applications (SPA) that are much faster than your typical Laravel + Blade applications. It was a no-brainer for us.
Fast forward a few years, Fathom Analytics has spread worldwide, and we're not shipping fast enough to keep up with our growth. After all, one developer can only do so much. And with every new feature we released, we'd have additional ongoing maintenance, and new features were still being requested by customers. Then you factor in all of the other work I'm part of (technical support, high-level finance, enterprise sales, content, social media, etc.), and you soon realize that every minute I spend away from writing code costs us. If I'm not writing code, our product isn't improving, and that is a dangerous place to be.
So I reached out to an old friend of mine to see if he's up for working full-time with us. He's very experienced with Laravel and a solid communicator, and I know he has a great work ethic. Bad news, he's just started another role. The good news is that he's up for the challenge at Fathom Analytics and will hand in his notice. Let's go.
Look what you've done
I introduced him to the codebase and assigned some minor tasks, and things were going great, but one bottleneck kept appearing. He was not familiar with Ember.js, the frontend framework that Fathom was built with. And Ember.js is a very opinionated framework which takes time to learn. And that's fine. We could accept onboarding time. Every developer takes time until they are productive, and you shouldn't rush that, but I started thinking about how Laravel developers don't use Ember.js. And then, I started thinking about our future hiring plans. Thousands of Laravel developers are familiar with Livewire, Inertia.js, Vue, React etc., but only one Laravel developer uses Ember.js, and that developer is writing this blog post.
And for the unaware, the difference between Vue.js and Ember.js is quite significant. It's not just the case of jumping syntax, it's a whole mental shift, and Ember opinions contain you. Those opinions aren't wrong; please don't misunderstand what I'm writing; it's just a steep and unfamiliar-to-most learning curve.
I began thinking back to when I first started using Ember.js. The learning process was huge, and you effectively maintained multiple applications (client-side routing/controllers/models & server-side routing/controllers/models). So every developer we hire in the future would have to learn our codebase and a different framework they've never used.
Could we build our entire application using Inertia.js and Vue.js in the time it would've taken our new developer to learn Ember.js?
Let's find out
A move to Inertia & Vue was a high-risk refactor, but vast amounts of motivation powered it forward. For me, I wanted to future-proof our stack. And our new engineer was excited to code as he suddenly knew the entire stack, as he'd been using Vue for a long time.
We began the project on May 21st and were live by July 28th. During this time, we also worked on other things but spent a good chunk of time on this refactor.
When you have buy-in from your engineering team, things will move faster because there's excitement. I worked on the dashboard while our engineer worked on the other pieces.
How long did it take?
Between May 21st - June 26th, we spent 57 hours working on this. We didn't track the remainder because, honestly, we forgot and were focused on finishing the project. If I recall correctly, though, I spent my time finishing my tickets (including QA) while our engineer was building out our Google Analytics importer (more on that later).
How did you move so fast?
When starting this project, I knew we couldn't afford to get stuck. My co-founder had already agreed to let us put automated tests in place earlier this year (to the tune of 800+ tests), and I'd persuaded him on this refactor, but this couldn't drag on forever as we had other features to build. So I reached out to Claudio Dekker (one of the Inertia.js maintainers) to see if he was available for freelance work. Fortunately, he'd just finished working at Laravel LLC and had some consulting availability.
Claudio was critical to unblocking us. Sometimes I'd spend 2-3 hours trying to do something, and he'd solve the problem in 10 minutes. He is one of the most thoughtful and skilled engineers I've worked with, and I was privileged to have access to him.
So a piece of advice I'd give in general is to temporarily hire an experienced contractor to move quickly through any blockages. Luckily, in this specific case, there are many great Inertia/Vue experts in the Laravel community.
Was it worth it?
At this point, you're wondering what we gained by doing this.
- We no longer have to maintain two router files (client & server); we maintain Laravel's routing file (with appropriate middleware applied)
- Eloquent models still exist, but there's no concept of a model on the client; we pass in parameters, making it super customizable (great for admin interfaces)
- Our builds are UNBELIEVABLY fast. Ember.js builds were so slow before (ask ChipperCI founder Chris Fidao), but our Vite.js builds are super speed.
- We have fewer files and things to remember. We now only have to create a route in Laravel, a controller, and a component file. When building a page in Ember, we'd need to make the following:
- A router.js entry for the route
- A Ember Data model that mimics our Eloquent model
- A route file
- A controller file quite often
- A component handlebars file for each component
- A routes.php entry in Laravel
- A controller in Laravel
- Our engineering team (of two) knows the tech stack very well
- We can hire Laravel full-time employees and contractors much easier
- We have first-party support in the Laravel space. An example is that the Laravel team has multiple people who are deeply experienced with Inertia and Vue.js, so they give first-party support to it (e.g. they launched Vite support as an improvement to Webpack). If we were using Ember.js, we'd have no workflow improvement from this launch.
I will state that the Ember.js framework is actively developed and regularly improves (they have a solid team). So I never used Version 4. One problem I had with earlier Ember.js versions was that computed properties needed defined "dependencies" (aka you had to say which variables it should watch), and Vue didn't require that. But it looks like Ember 4 doesn't need this anymore.
Inertia.js vs Livewire
Inertia.js is not as big as you think it is. Most of our application is Vue.js; Inertia acts as the "glue" between our Laravel application and our frontend. For example, we create routes & controllers in Laravel, return the desired Vue.js component, and it's loaded. There's no client-side routing to maintain like there was in Ember.js.
I should state that, at the time of writing, Livewire has just released a few changes, such as SPA routing and the ability to inject JS and manage the state client side, so I don't have an opinion on which you should choose. I can tell you that Caleb (Livewire) and Jonathan & Claudio are fantastic people and that the Laravel community is spoiled for choice. Try them both and see what feels good to you.
Why didn't you use React.js?
Vue is more common in the Laravel space, and I'm not a massive fan of the React syntax. The team (even Paul, who's not a developer) has lots of experience with Alpine.js & Vue, and Vue felt very natural. I don't have a solid comparison piece; we love Vue.
No regrets? Not even a single letter?
Time to be honest. The only regret I have is timing. At the time of writing, our Google Analytics importer is in the final rounds of development when it should've been launched months ago. By investing in our core infrastructure, we chose not to build new features, which will have harmed us temporarily. Growth hasn't stopped, and Fathom Analytics has grown every month since we started it, but our customers are waiting for some essential features, some of which we've even started building.
The good news is that we're now putting all our energy into building new features for Fathom. I'm thrilled with our decision, and we're already starting to see returns from having a "standard" tech stack. The future is exciting, the spaghetti monster has departed, and nothing can stop us now.
Looking for some Laravel Tips? Check out Jack's Laravel Tips section here.
You might also enjoy reading:
- Why we ditched DynamoDB
- Building the world’s fastest website analytics
- Someone attacked our company
- Does Laravel Scale?