Some weeks ago, I joined the Mr. Jeff team with the mission of helping them modernize their front-end stack and workflow, in a progressive and incremental way that wouldn’t mean just throwing away what they had and rebuilding everything from scratch. This is the story of how we prepared our boat to leave the calm but dark waters of Symfony, and set sails to the much rougher, yet clearer, shores of Vue.

What we’re sailing away from: The double-backend conundrum

For historical reasons, the tech stack of Mr. Jeff web apps is quite a peculiar one: in the backend there’s a cool microservice-based Java/Spring architecture that serves both our web and mobile apps, while the web frontends are almost-full-stack Symfony projects.

But wait, what do you mean with almost-full-stack?, I hear you say. Long story short, the initial design approach just needed to make some API calls and render some admin panel views, and Symfony was the tool that the developers that started the project knew best, so they just went for it. But because of the backend-oriented nature of Symfony, and the specific mindset and way to do things that it creates on its developers, they relied heavily on Symfony models, controllers, services… which in many cases mostly replicate what the Java backend does now.

So in short, what we ended up with is some kind of a double-backend architecture — our web apps are server-rendered by a backend (PHP/Symfony), which in turn is getting the data from a second backend (Java/Spring API).

As I mentioned, this scenario isn’t really a consequence of bad decisions or anything — it’s just the evolution of a project that started small but fast, and suddenly started growing much bigger and much faster. And that’s a good thing!

But, as the project keeps growing, that means complexity keeps growing too, latency times keep raising, technology requirements change… and also the needs of our users now include better interactivity and performance. So, we decided it was time to abandon the double-backend architecture, and shift our frontend from Symfony to one of those shiny modern Javascript frameworks that are all the rage these days.

And we chose Vue!

Choosing a flag: Vue vs. React

But why Vue? Why not React?, I hear you scream now. We definitely considered React, since technically-wise we surely can do with it everything we can do with Vue. Vue’s popularity is more recent, yet as explosive as React’s was, if not more. It also has a much lower learning curve, mostly due to its cleaner syntax and separation of concerns, but also to its excellent documentation and official packages and project templates. But in general terms, both React and Vue were great candidates for our needs.

So why Vue? Two main reasons:

I was going to be the one in charge of starting the transition. I’ve played a bit with React before, but I have much more experience with Vue. I know, not a very strong reason, but to some extent important since it means we could start the transition faster and more reliably, and also I could assist the rest of the team when they needed help with Vue.

The background of the rest of the frontend team was mainly in Symfony, and with little to no experience with modern Javascript frameworks. And, if you’ve seen React code and Vue code, I think there’s something we can agree on — for someone that has always worked with Twig templates, plain CSS and some jQuery, it will be much easier to jump to Vue’s simple templates and template-script-style separation, than changing to React’s JSX and different ways to handle styles. This was by far the most powerful reason.

And so the first choice was made — let’s do it with Vue then! So, next one in line — how should we do it?

Finding the safest route: The many ways to Vuefy things

One of our requirements was that we couldn’t just start from scratch, replicating the whole project in Vue while maintaining the Symfony one. The project was just too big for doing that with our resources.

Luckily, Vue defines itself as The progressive Javascript framework, so we sure could take advantage of that! After a bit of research, we concluded there were three main ways in which we could tackle the migration, each with its pros and cons.

Approach 1: Independent Vue app + Routing middleware

Or in other words, having a full-fledged Vue app running alongside the Symfony one, and somehow selecting one router or another depending on the route.

Pros:

Being able to implement things like Vue routing, shared state, etc. from the beginning.

Having access to all the amazing Vue tooling and ready-made templates, that can save loads of time and headaches.

The final step of decoupling from Symfony would be very easy, since the app would be almost 100% done.

Cons:

We couldn’t find any good documentation or case studies on how to do that routing selection.

It would force us to replace entire pages at a time, instead of being able to replace only some components or sections of a page.

Approach 2: Several Vue apps rendered in Symfony views

That is, building a number of Vue mini-apps that could be anything from a very small component to a full page, and maintain them separately as independent apps that can be rendered anywhere we need in the Symfony templates, getting data from Twig views if needed.

Pros:

Probably the most flexible and balanced approach, in terms of complexity and development speed.

Relatively well documented, including a few case studies of other people starting similar projects (well, most of them use React, but everything is applicable to Vue).

Cons:

If the amount of independent apps grows, managing things like builds and dependencies could end up being hard and time-consuming.

The final step of completely removing Symfony would be a bit more involved, including the replacement of the router, the management of all app-wide state, etc.

Approach 3: Vue components embedded in Symfony views

If the heading sounds similar to the previous one, it’s just because I couldn’t find a better way to word it. I simply mean using Vue in its most basic form — instead of working with build tools and single-file components, we could just use Javascript and template strings for replacing small bits of HTML and jQuery, as demonstrated in so many create your first component articles through the web.

Pros:

No config needed — just download Vue (or get it from a CDN) and start coding. No webpack, no build step, no tooling headaches.

Cons:

As a consequence of the pro above, no webpack and no build step means not having access to all the modern javascript toolbox: ES6, linters, CSS preprocessors, hot reloading… So maybe not that good of a pro!

Also, no access to module bundling, so dependency management could become a nightmare.

It wouldn’t do much towards our final goal of removing Symfony. The final step of moving the whole app to Vue would include many extra tasks such as converting all string templates to single-file components, replacing the <script> tags with imports, incorporating all the tooling, etc.

We quickly discarded Approach 3. While it might be a nice and quick way to incorporate some Vue bits to a project, it doesn’t scale well for bigger apps, and it would mean a lot of extra work in later stages of our transition.

So a choice had to be made between options 1 and 2… Except we weren’t 100% happy with any of them. Both required sacrifices that could potentially slow us too much or give us unnecessary headaches. What if there was some middle ground between them?

Turns out, there was! During our last round of desperate research, we stumbled upon an option that we had seen before but for some reason we completely overlooked: Webpack Encore.

Trusting our compass: Choosing safety over adventure

Webpack Encore is the official way to handle assets in Symfony 4, and it’s essentially a Webpack wrapper. It was created by the Symfony team to replace Assetic, and it lets you build your Symfony frontends using modern tools like React, Vue or TypeScript right from the project bootstrapping.

We had previously discarded Encore as an option for our project because we wrongly assumed it required Symfony 4 — we were using Symfony 3.4, and obviously upgrading wasn’t an option because the goal was removing Symfony. So, when we found out that Encore also works with Symfony 3, hooray! Our hearts were filled with light and hope again.

So in short, Webpack Encore is that middle ground we were looking for. Since it’s just a nice API for webpack, it offers many of the tools that we wanted from the official Vue tools. And at the same time, it fully integrates with Symfony’s project structure and way to manage assets, eliminating some of the issues we could have found while trying to manage multiple Vue apps living inside our project.

Encore isn’t perfect yet though. It still lacks some important features like hot reloading of styles or out-of-the-box support for tests. So there will be things that we won’t be able to have from the beginning, like easy configuration of tools like Styleguidist or a proper testing system. But it’s an official tool, and, at least for our scenario, it looked like the most balanced solution in the short term. And anyway, in the longer term, the plan is to remove Symfony and Encore too and migrate to a standalone Vue project, so we’ll be able to tackle those downsides then. Let’s see how it turns out!

Setting sail: Off to new shores!

We decided to set up Encore in our projects and start using Vue for implementing any new features the products could need, as well as for rewriting some of the existing features whenever big or complex changes were needed. In the meantime, we could start migrating other existing functionality to Vue, little by little, leveraging that progressiveness that Vue is so proud of.

That way, at some point (hopefully not too distant) in the future, we will have a scenario where the biggest chunks of our apps are already migrated to Vue, so we’ll be able to quickly and easily cut ties with Symfony, and finally extract them to standalone Vue apps.

The journey will be long and probably bumpy, but certainly rewarding, so all aboard! Ahoy!