Writing the new iFood for Partners – Part 1: Flutter 2.0

Follow the iFood app migration process for Partners: Part 1: Flutter 2.0.

Hello! My name is Gildásio. I'm a software engineer on the iFood for Partners team, and I'm here to talk about our three-part journey of rewriting, refactoring, and redesigning the app. The goal is to get the app to a state where everyone feels comfortable adding new features while managing existing ones, whether they are new to the team or not.

Prologue

When I arrived at iFood, in May 2021, the team was in a difficult situation after losing a member – and we had some deliveries to make in a short space of time. We knew that we would soon have outside help for a few months, but the app, as it was at the time, wasn't user-friendly enough for a new team to come in like that. We decided on a new architecture based on block for the UI, and we fixed (more or less) our current architecture for the rest. Still, we had a bigger problem ahead of us: we were using Flutter 1.22.6.

The situation was as follows: sprint current/releases to finish, new team and new features arriving, architecture not yet organized, plugins old ones and an old version of Flutter. We couldn't wait, considering that the next Flutter update (which announced 2.2) was about to come out, and that would mean even more work if we weren't already on 2.0.

At the same time, this was the best time to do the migration, considering we were on pause while the next features were ready for the development stage. In other words, the deadline for this was two weeks.

The following was decided:

– Separate one branch of development called develop_2.0 and start the migration there, module by module;

– Continue developing the functionalities of the sprint current separately, to convert them later, as we still needed to finish a version;

– Write and detail a new architecture for the new team, so they already knew what we needed for the new module, already using Flutter 2.0 from the beginning.

One of them definitely didn't work out. Guess which one?

The migration

We only had two weeks to migrate an application with 22 modules before the next feature arrived and, to make things easier, we thought: “migrating each module separately will work fine, right?”

No.

A little context: the iFood for Partners app started two years ago, with a team that was learning Flutter while building the app. This left us with patterns and an architecture that would make our migration work a little difficult – one of those, precisely, related to how the modules were interconnected. We cannot migrate module 1, for example, because it depends on 3, 4 and 7. And these, in turn, depend on others.

Therefore, the module approach was not working. What could we do after ensuring that modules without dependencies were migrated safely? To execute dart migrate in each module at once, right?

Don’t worry, we didn’t do that – but almost.

First, we needed to make sure we were following the correct steps. Fortunately, all the packages we were using at that time already had a version null-safety available on the channel stable or in beta.

Then, we map out the modules with the fewest dependencies and start with them, gradually to gradually make it easier to also pick up the larger modules.

Then, yes, we execute dart migrate! That's what made everything so much easier and safer (considering we only had two weeks). The tool had its shortcomings, but it was definitely what made the feat possible in such a short time.

We discussed a bit whether we would migrate to 2.0 with null-safety deactivated or not, and each article and recommendation left only one choice: it is better to do it now, before it is mandatory and there is even more work left for later. Worth it? For sure!

Coming from Kotlin, I missed the null-safety in the three years I worked with Flutter. It helps us become less prone to mistakes, and makes new plugins and safer packages, with features such as late initialization (which we still don't have the async version that Kotlin allows). You can take a look at the official explanation here.

Lessons learned:

  • Having a tool as good as dart migrate helps with the “dangerous” task of migrating a large project once with few caveats. We really congratulate the work of the Dart and Flutter in that regard;
  • You can do a lot in two weeks! We organized the team in a way that we were not only able to carry out the migration, but we also had time to prepare the ground for new people to work on the code as soon as we migrated;
  • Flutter itself helped with this too. We had many widgets which we feared would stop working after the migration, due to the complexity – but everything worked fine. We only had problems with unforeseen nullable fields, coming from APIs that were incorrectly mapped by the tool;
  • From now on, we will be much more careful with all new modules we write. Mainly, about the addiction problem, which was something years ago and, finally, we had the chance to at least prevent it from hindering us even more in the future.

We hope you enjoyed this first part of our journey, and stay tuned for the next one! We will talk about the new architecture that we defined for the modules: data, domain and UI.

Original text available at Writing the new iFood for Partners — Part 1: Flutter 2.0

Was this content useful to you?
YesNo

Related posts