What Feature Teams have changed for iOS team at leboncoin
by Amandine Cousin, (iOS Developer, Scrum Master)
Leboncoin’s iOS team has been growing for a few years now; today we are 9 developers and still recruiting.
We all heard about the concept of The Butterfly Effect, that one little change could make huge differences. That’s how the iOS team works at Leboncoin since we have more than one million active users every day, everything we change could have huge impact for our users’ experience.
About a year ago, the Leboncoin Product & Tech organization evolved from a silo organization to feature teams.
A little explanation: instead of working by domains, we now work in multi-skill teams, each one representing a part of the product. So we still are the iOS development team, but we sit everyday with our feature team, and all the feature team members work on the same project together as independently as possible from the other feature teams in order to be more efficient, more focused and to deliver faster.
What did it change for us? We used to have one product team, dedicated to iOS. It was easy because the requests were sent from one team to another. But now we have a product team and one iOS dev in each feature team so we had to adapt to our new way of working together in order maintain a sound and powerful app.
So, what did we do?
Can you imagine having 9 developers working on the same codebase at the same time? The least we can say is that it is difficult if you don’t have a structured architecture! So the first step was to divide the main project into multiples modules to reduce conflicts and dependencies: 4 functional, 4 technical plus the main app to make everything work together. The functional parts represent the main universes of our application.
How do we deal with merge conflicts? Each module represents a unique repository and have one remote branch. Every time we build the application, a script, from build phases, is launched to get all the needed modules. We use Carthage to generate a framework. Jenkins compiles the frameworks and packages it into a zip file. It represents the last shared version. The shell script checks if the framework already embedded is up-to-date and downloads it if needed.
Every functional module represents for example a universe in the application and each functionality is independant.
Let’s take the search module: every module has a demo application so it can be launched by itself. To do so, Search will need technical modules like Helpers, Network and Storage. This is the stack of compilation from the lower level of independence to the functional application which will be launched.
Ok, but you said single branch? If I’m in the middle of a new feature, with only one remote branch, how do I send a release with it? How do I deal with a rollback?
We are all working on multiple new projects at the same time so it’s hard to wait until the end of the feature. So, we add a toggle handler feature.
In a JSON file, we enable or disable the features we want to use and add the corresponding test within the code so that each new project is toggled. We also add an in-house user interface to do so. It helps the other members of the feature team, like the person in charge of quality control, to test any project they need.
Let’s take the new messaging feature. We had to ask ourselves where to put this new feature and, as it is a whole new functionality, we made a new universe and so a new framework for it. This is how we were able to work on this new feature while continuing to work on the rest of the project.
What about the C.I.?
Okay it’s great, we can deliver anytime, but how to be sure that what we have on our single branch is working properly? What is the journey of a line of code?
- We develop locally.
- We create the commit.
- We send it to Gerrit.
- Jenkins launches our custom linter, builds and launches our unit tests and checks the code coverage.
- The developer waits for a review from another team member.
- If everything’s fine, we merge.
- A pipeline is triggered by Jenkins to ensure the quality. It launches all modules which use the module we just modified. For example, a technical one like storage, which is used everywhere, will relaunch every part of the application to be sure we didn’t break anything.
- If we want to send the new feature, we activate the corresponding toggle.
- A TestFlight version is uploaded to be tested.
- When everything is fine, we send it to Apple for review.
- If they validate it, we launch a progressive release starting at 1% of our users.
Last but not least, we have also improved our delivery process.
With our old multiple-branch process, we had to create a new release branch from the develop branch. For every version, we had to merge it on master branch. The master branch represented the current production state. And we had to repeat the action for EVERY module… Only one or 2 developers in the team knew how to do it, so it was time-consuming for them.
Now with the single branch repositories, we removed a few pain points; it also allowed us to launch a hotfix version whenever we needed to.
Our modular architecture, the single branch development, and the toggle feature make the application ready to be released at any time. Our C.I. helps us guarantee the code quality. And our delivery process is as simple as pressing one button!
Now our next challenge will be the UI tests.