In this post, I will share how PayFit made its codebase evolve through time, from a single repository of code to a set of functional repositories, maintained by different teams.

Introduction

In the early days of any startup, it can be difficult to have in mind the architecture that will perfectly suit your application. At that time, the most important thing is to focus on your product, with the objective to quickly validate that your application will respond to the expectations.

Your product will define the architecture needs of your application, all along its development. Learning from iterations will give you a lot of insights about the architecture you need to put in place.

PayFit at the beginning (2015-2016)

Let’s take a step back in 2015, when our 3 founders started working on the PayFit application. At that time, their motivation was to build a SaaS product to facilitate the payroll process. They quickly decided to develop a custom solution to address this issue. They created JetLang, a low-code platform, to abstract the labour law into code, and to make the payroll process more resilient to changes.

ℹ️ Learn more about low-code platform with the following article written by Thomas Villaren, Staff Engineer & Software Architect at PayFit: https://backstage.payfit.com/how-low-code-platforms-changed-the-course-of-traditional-software-development

ℹ️ Learn more about the reasons behind the creation of JetLang : https://backstage.payfit.com/the-right-tool-for-the-job-how-creating-our-own-programming-language-brought-us-closer-to-success/

The PayFit application is built on top of JetLang, with the objective to automate a lot of tasks and processes (payroll triggers, payslips generations, declarations…)

In the first few years, all the PayFit code was gathered and versioned with Git. You could find JetLang code close to the web application itself.

Back in 2015, the PayFit application was built using AngularJS, i.e. without benefiting from TypeScript's strong typing, and without a satisfying test coverage. The main focus was to quickly build a beta version, in order to get our first feedback. In January 2016, after several months of development, our first beta was available.

The first version of PayFit.

Architecture evolutions (2017-2019)

At first, PayFit was only a payroll solution. But we saw an opportunity to serve our customers with human resources features along the payroll solution. So when HR features started to be implemented in 2017, they forced us to rethink our architecture. Separating the Payroll code from the web application itself was the first step.

It became easier to maintain both parts, with a team dedicated on Payroll, another on the PayFit application. The Payroll solution, developed by a dedicated team was then integrated into the PayFit application, along HR features.

We also decided to rewrite the whole application with ReactJS and TypeScript, in order to reuse components easily between features. This migration was made progressively, and led to a more robust code base. It was also the beginning of a React components library, that we built to gather reusable front-end components.

This migration also led us to performances improvements. The ReactJS virtual DOM (which is able to perform front-end updates on its side before sending them to the browser) made the PayFit application way faster. On development side, React gave us more flexibility and more control over our front-end components.

An old PayFit dashboard.

Up until 2019, the Product team was small enough for all its members to master the entire scope. But as we were growing fast, we quickly faced the limits of our organization. We were working on more and more features, and the time spent appropriating a new subject and making it move forward was becoming disproportionate.

Hence, Mid-2019, we decided to completely reorganize the product teams. Our ambition was to setup an organization with clearly defined roles and missions. It would increase expertise by giving teams more autonomy on small scopes, while making the product evolve faster with more reliability.

That's why we chose to split the product teams into tribes and squads (based on Spotify model), each one with a defined mission and a set of features. Some other teams were created or redefined like the Architecture team or the Design system team.

These changes had a lot of impact :

  • The different squads can now focus on specific features of the web application
  • The Architecture team is able to lead technical evolutions, and accompany teams in their architecture needs.
  • The Design system team can focus on developing a package of React components that every other team can use to build their features
  • And a lot more !

ℹ️ You can read more about this transformation in a dedicated article written by our CPO : https://backstage.payfit.com/the-payfit-product-machine-reloaded/

Isolating features into modules (2019-2020)

In 2017, when we separated the HR features code from the Payroll solution, it was a small application with a few HR features. 2 years later, it became a large repository acting as a runtime for the whole PayFit application, where we developed two different spaces (one for admins, another one for employees)

As the recent reorganization gave us more ownership on domains, we quickly chose to modularize our code. The architecture team proposed that we create packages inside this large repository. The objective was to isolate our features from the rest of the application, and acquire more autonomy.

Hence, we had the opportunity to rewrite some code. But, despite the fact that we were writing isolated modules, we were still working on this large repository. It became difficult to maintain as it was gathering different ownerships and a lot of legacy code.

Some development teams in other companies may choose to keep a single repository. On our side, after our reorganization into feature teams, we quickly saw an opportunity to split our huge repository to gain into productivity and flexibility.

Thinking vertical (2020+)

Historically, we designed repositories around technical scopes rather than functional scopes, and writing modules confirmed to us that we needed to inverse that trend. It would require a package-driven code organisation over a runtime-driven code organisation.

We finally agreed to move on a "vertical" approach. It means that each functional scope will be isolated on a dedicated repository, containing every piece related to its scope (UI, API, SDK, Database…). Then, the runtime can be totally decentralized, and the vertical repository can release NPM packages or Docker images that an external runtime is able to import and execute.

And that’s what we chose to do. Our large repository is currently on a diet, and starts to use external packages. With these decisions, our objectives are to increase the product modularity and team autonomy, plus reducing technical debt.

Exemples of vertical repositories

Acting like the single source of truth for their domain, we sorted out verticals by categories  :

  • Core verticals, gathering transversal features (authentication, documents …)
  • Global product verticals (Absences, Expenses, Payments…)
  • Country-specific product verticals (Payroll, declarations…)

We're only at the beginning of our vertical transformations, but each team now has a clear vision on how to implement their features. This alignment is the pillar of our future technical challenges, and we strongly believe that developing these verticals is only the beginning.

Join the PayFit rocketship!

In the PayFit Engineering department, there’s no time to get bored. We grow fast, we evolve fast, and the way we were working on my first day 2 years ago had changed drastically. There’s always a new tool to test, or a new idea to talk about. Things are constantly moving, and it’s pretty motivating.

Do you feel ready to onboard the PayFit rocketship? We’re recruiting 250 persons in 2021! Grab your spacesuit, and contact us!


Header photo by Yancy Min on Unsplash