Throughout the 9 previous months at TransferWise, I’ve worked on building a new feature called payout to cards. Visa and MasterCard are putting together this new way to send money to bank accounts where the sender only needs the card number of the receiver.
This project has gone through countless highs and lows. There were times when we were blocked by our partners, when our market expectations turned out incorrect, when the hard requirements of storing card numbers caused us countless technical issues, when other teams refused our technical or product propositions…
But we’ve also gone through times when we finally had an end to end successful transaction going through our system, when everybody — from our regional teams to our partners Visa, MasterCard and Checkout.com — gave us green lights, when we had our first customers using that new feature…
The whole thing was thrilling. Especially the rollout phase. I decided to share this experience out there, hoping that it could help some to consider new career path, or to better understand how we conceive Product Engineering at TransferWise.
The Minimum Viable Product (MVP)
So add up all of those requirements and we’ve got an extremely complex new product to add to TransferWise. That is still an MVP […]. But for an MVP, that’s a pretty big one!
So when I heard about that task at first, I thought it would be an easy one. After all, through our payment service providers, this was just an API call away, right? And this is TransferWise, so we build quick and powerful MVP overnight, don’t we?
Eric Ries in his “Lean Startup” book defined an MVP as “[..] that version of a new product which allows a team to collect the maximum amount of validated learning about customers with the least effort.”. But “least effort” here, doesn’t mean that you have to get your MVP out overnight. Every new product has different pre-requisites and associated risks. Thus the MVP definition always takes into account what is necessary to include. If a new feature cannot be rolled out without passing a compliance audit, then going through the audit has to be part of the MVP.
So what do we want to validate? We want to know :
- Are those payouts really instant?
- Are the banks really supporting it, or are they just saying so?
- Is the coverage and success rate as good as we were told?
- Are the customers willing to send money to cards?
- Are the customers willing to enter their relative/friend’s card number?
- What operational problems will we encounter? Will they cost us more than with other traditional payout methods?
- What customer issues will we encounter? Will we have a greater contact rate with this payout method?
- Will we lower our conversion rate on the routes where sending to cards is possible?
And many more questions. So there we go, that’s what we want to validate with this MVP.
So what is our MVP then?
Answering all those questions is not trivial at all. To do so, we need to build the full integration of this supposedly simple API call, with all the rest of our product. That’s a lot because after more than 8 years in business our product is complex, wide and requires a lot of integration in a lot of various services, for any new feature to happen.
We’re dealing with real customer money and I am introducing a new way to send money to the end receiver. It is crucial that we know where the money is at any time, that we can have several confirmations that we successfully paid out and so many more requirements that we cannot live without.
Not only that, but we are working with card numbers and thus have to deal with additional security requirements. Our system is built so that creating a new type of recipient requires no client code. We deal with ever-changing banking requirements and extend the product in new regions and markets every day while offering our APIs to businesses. We needed a way to change requirements from one single place where we can describe all of those requirements. We called it dynamic forms and dynamic flows. It’s a set of language-agnostic JSON specifications that our clients and backends understand and can act upon. But that existing JSON specification doesn’t know how to specify encryption requirements. And the underlying recipient domain logic is not aligned with the new tricky requirements we are adding. In fact, card recipients would be the only recipients where we ask the customers something (a card number) that we can’t store in the recipient table.
Sending to cards also raises questions and new requirements regarding financial compliance of our product which we had to cover up before rolling it out.
So add up all of those requirements and we’ve got an extremely complex new product to add to TransferWise. That is still an MVP in the sense that I didn’t build any useless part in there, and avoided as much as I could any overhead. But for an MVP, that’s a pretty big one!
Cross Team Collaboration
This project was completely cross team and that’s where it became interesting, but also particularly challenging for a newbie.
The first thing I realised is that I didn’t understand anything about how TransferWise internal machinery worked. And I had to figure it out more or less by myself.
Before you ask, yes we do have documentation and I would qualify it as good documentation. But the documentation mostly gives a picture of each separate domain, and sometimes, the hard part is not to understand how to fit your feature in a domain, but what domain that feature fits in. And it gets even harder if you don’t even know which domain exist, or what they do.
One might also wonder why I didn’t have help from my team? Well, I had. My team helped me as much as they could. But we deal with the pay-in engine and the card payments, not with payouts. So for most of my questions, I would have to find answers elsewhere.
This project was completely cross team and that’s where it became interesting, but also particularly challenging for a newbie. I had to reach out to other people in other teams and try to understand what they were doing, why and how. I would then fit my logic in their domain and seek their approval. And I would do it over and over again until we were finally ready to roll out payout to cards.
That might sound chaotic, and it feels a bit like that at the beginning. But the whole thing works smoothly and this pattern allows us to keep a maximal efficiency. If you’re still skeptical, you might want to know more about how we work and why. It’s not just this feature, whatever project you’d work on at TransferWise, you’d likely need to work with other teams.
Releasing, Testing, Rolling out the feature
We also have on-demand custom environments that engineers can boot up at anytime from a nice homemade slack bot.
We have our own internal feature locking system that allows us to run experiments like A/B testing, or to deploy some features for just a few test employees to get early feedback.
This is somewhat close to production testing, but not quite much. Engineers at TransferWise test a lot of things before an actual rollout. We rely a lot on automated testing (read more about our Development Driven Testing practices). We also have on-demand custom environments that engineers can boot up at any time from a nice homemade slack bot. In those environments, we can deploy whatever version of whatever service we want. This allows us to do powerful and complete end to end testing without affecting production or even staging traffic.
But at the end of the day, we are testing real money transfers, and some parts of this infrastructure cannot be tested anywhere else than in production.
The feature was rolled out gradually as an A/B testing experiment, on a per-currency basis, or on a per-country basis for currencies like EUR where the coverage of payout to cards is varying a lot.
This is of course just an example, but this is pretty much how we work for each feature:
- Unit testing with our DDD approach
- Custom environment testing / Staging testing
- Releasing the feature silently — Nobody can see it
- Limited Internal rollout — We handpick the testers
- Internal rollout — All Wisers get the feature assigned
- Gradual A/B test rollout across currencies, countries…
- Complete roll out in case of success. Feature deprecation otherwise, or back to the whiteboard for a new Build Measure Learn cycle.
Would have I done things differently?
I’ve worked on this project for 9 months straight (having some other projects in parallel in the meantime). And 9 months is still a lot for an MVP, even after all the reasons I described above. So almost every day I’m asking myself whether I could have used another approach. If we could have tested payout to cards differently. If we could have figured out a more limited yet working MVP somehow.
This is a tough question, I’m still trying to figure out the answer. Especially since we already had existing routes to payout to Russian, Chinese or Ukrainian cards, giving us some early confidence in our work.
Meanwhile, when I started this project, I still had no idea how TransferWise was working, I hadn’t read that book I mentioned a few times (Lean Startup from Eric Ries) and I hadn’t that experience I now have working at TransferWise.
I probably would be spending a bit more time in front of the whiteboard next time I’ll build a new feature.
I’d like to thank all Wisers that helped me achieve this project successfully. Starting from my whole team, with special thanks to Aleksandr who led the project from a product perspective and Dare who greatly assisted Europe team with their local card payouts. Next thanks would go to operation team who did a great job in helping us solving and forecasting operational issues along with Europe team, and particularly Bright, Rasmus, Pradatta and Gina who did an amazing job at preparing the groundwork with their local card recipients in Ukraine and Russia.
My last thanks go to all the other teams I worked with during that project including account details team, Checkout.com which was a key partner in providing us with their payout to card rails and helping us throughout the integration, along with Visa and MasterCard.