How we Built a Bank Account with a Brain

Michael Brew
EPD at Digit
Published in
11 min readJun 15, 2021

The last twelve months have been an incredibly exciting time for Engineering. If you haven’t already heard, we’re launching a new bank account: Digit Direct Powered by MetaBank®. Our mission has always been to make financial health effortless for everyone, and today we’re taking the next step on that journey by offering a modern bank account that intelligently sets asides money for bills, tells you how much you can safely spend, automatically makes progress towards your savings goals, and does it all with style. I encourage you to read through our launch announcement to learn about what makes Direct different and why we decided now was the right time to build it.

In this post, I will walk you through the engineering journey my team and I underwent to launch this product from 0 to 1. Through this, I will share the technical challenges we faced along the way, as well as how we strategically approached product development to maximize lovability. In short, this is the story about how we took initial ideas, wireframes, and user stories to build a new, intelligent bank account.

🏦 Laying the foundation for Direct’s financial backend

The first step in our journey is architecting the foundation for the financial backend that will power Direct. Our focus at Digit is to build products that make achieving financial health effortless, not to be a bank ourselves. For that reason, we partnered with MetaBank [1] to manage our users’ funds while partnering with Galileo [2] to facilitate banking actions over a web-based API (Banking-as-a-Service). By leveraging these financial partners, we can maximize the time spent on developing Digit’s core competencies like personalized, automatic money allocation. After deciding on who our banking partners were, our engineering teams started working on the reference architecture and technical design for our Financial Platform.

Above is a high-level diagram showing how we integrate with our partners to facilitate core banking features. The general motivation behind our Financial Platform Layer is to abstract our Product Layer from implementation details about specific integrations, while ensuring redundancy through a local transaction ledger that’s updated in response to vendor activity (and reconciled on a daily basis). We’ll explore more exciting details about our financial backend in a future blog post, including topics like how we mitigate data consistency issues across distributed systems, how we minimize security risk with secret management (while maintaining Level 1 PCI compliance [3]), why we chose Golang to support our critical banking flows, and how we plan to evolve our platform over time.

📱Starting with a mobile Proof of Concept

Our next challenge as engineers was to figure out how to quickly ship a fully functioning mobile Proof of Concept that was substantial enough to provide a genuine feel of what Direct was like, while the scaffolding (financial platform and vendor integration) was still being designed and built in parallel. Shipping a live PoC quickly was important since at Digit, we pride ourselves on our product culture and believe that a tight, iterative feedback loop is an essential aspect of everything we make. But unlike building a new splash page or interstitial animation, putting a bank account into users’ hands involves a lot of due diligence, security reviews, redundancy precautions, etc. — all which prolong that crucial beta launch. So in order to put our ideas to the test, we aimed to “dog food” Direct (not-so-fancy way of saying “try your own product”) as we were building it.

Another strategic advantage to eager implementation is to iteratively test end-to-end coverage across all relevant services, ensuring any risky or complex areas are highlighted as soon as possible. While writing automated tests against stubbed fixtures and asserting API behavior with tools like cURL and Postman can help show progress, having an app on your physical device that actually sends requests down the production stack and gracefully handles responses solidifies the meaning of “Proof of Concept.” Even without any live connectivity to our partner APIs, we knew we had to build connections across existing Digit services to fulfill future use cases (like querying predicted bills and paychecks so we can show users how Digit would allocate money for their monthly budget), so we wanted to parallelize that work as much as possible.

The first version of Direct that we put into employees’ hands (while clearly taking advantage of the opportunity to be temporary millionaires)

In order to deliver these early PoCs, we started with a “local” version of our financial accounts that had no reliance on external vendors (i.e. the actual bank). These accounts were defined and solely managed within our API backend, maintained a single balance, and did not hold any real money. However, this was enough to allow our employee “users” to initiate fake deposits into their accounts and see how Digit would automatically allocate those funds based on their current account balances, upcoming bills, and predicted paychecks.

With the resulting feedback, we made a series of tweaks to our intelligent budgeting algorithm to ensure that users felt comfortable with how much they had available to spend while still being confident they’d have money to pay their bills. However, there was no historical record of transactions and we weren’t validating any connections with our external partner’s API (one of the hardest parts about building brand new services), so as soon as we got out hands on Galileo’s Client Validation (e.g. staging) environment, we started integrating as fast as we could.

🔌 Connecting the bank

While our Product Engineers were hard at work creating a new “Direct” app to test out the look and feel, our Platform Engineers were doing everything they could to integrate with our new BaaS partner. Given the (potentially) destructive nature of erroneous banking API calls, we were required to test each individual endpoint that we planned to use within their staging environment first. We would validate the response behavior of each call to ensure information was persisted correctly across requests, while Galileo would do the same to ensure we initiated each call as expected. While access to the staging environment was necessary for this required validation process, it also afforded us the opportunity to test out Direct in an end-to-end environment of our own (even if it was in a pseudo-staging funnel).

To the left is what was internally dubbed “the big onboarding” form. It was the first iteration of our signup flow that could actually be used to open accounts at MetaBank with Galileo as the processor. It was far from pretty, but given our primary goal was to test service connectivity and feel for the in-app experience, so we opted for function over for form. This was hosted on the live Digit website (behind manually controlled feature flags) and communicated with staging instances of our Platform Layer services. Once it successfully created accounts in Galileo’s system, we were able to later read those when fulfilling GET requests from our mobile app (including persisted balances and transactions).

In order to interface with different environments across systems, we maintained “environment” attributes for each account within our API server’s datastore, and implemented helpers to dynamically query the “highest priority” account of a given type based on environment (where priority would be defined as production → staging → local in descending order). We also implemented stubs within our mobile app’s API routes to return mocked data if the user was missing any of the expected accounts to ensure their app experience would still behave as expected.

To this day, our implementation of variable account environments allows us to test new product features against Galileo’s staging API in a stack that resembles production as much as possible. While embedding environmental logic within our application layer introduces nonzero complexity, we’ve found that the tradeoff has been worth it as it allows us to more exhaustively test new changes in a safe space before shipping to real users.

👷 Trialing the employee “Alpha”

The first real “beta” users of Direct were (you guessed it) Digit employees. In the spirit of dogfooding our product, we started putting our actual money in the app as soon as we could. It’s one thing to simulate how Digit might allocate your paycheck when you’re playing with monopoly money, but you start to get a true sense of how this bank account feels when real money is moving automatically. Again, not only does this help generate critical user feedback, but also allows us to test system reliability before we start managing any money on behalf of our members.

With that said, we wanted to test everything as fast as possible so that any unexpected bugs could be triaged and prioritized quickly. This meant opening the first real account as soon as we could, which was done directly within Galileo’s admin portal on the web. There was nothing in Digit app, nothing in our internal ledger, but now a Digit “member” had a Direct bank account! We were also issued white, temporary debit cards that had nothing but a magnetic stripe and chip, card number and expiry, and card embosser name. However, now we could move real money into Direct by initiating ACH transfers from external sources (like our previous primary bank accounts) and then spend it with these temporary cards. Again, there was nothing in our internal systems yet but we were now starting to use Direct as an actual bank account.

As much as we enjoyed opening bank accounts on Galileo’s web portal, we had to now assert the end-to-end signup flow through our own systems. The challenges of testing sensitive flows with side effects in production is that “just trying again” can be very hard to impossible. For example, part of the account creation process is running a Customer Identification Program (CIP) verification for each user, which can’t be run multiple times on a single person (where each “person” here is identified by a combination of unique personal details like SSN). So that meant that any partial failure states encountered during the initial signups meant a lot of manual labor for engineering to clean up some data, push some potential bug fixes, then find a new willing employee to complete signup so we could ensure everything would work as expected.

Over the course of 3 months we onboarded many Digit employees onto Direct. More photos were being posted in our internal Slack channel as our next iteration of blue Digit-branded debit cards arrived, and feedback began to accelerate as employees started depositing their paychecks into Direct (and seeing how Digit subsequently set aside money for their bills and savings). In turn, we rapidly iterated on the experience within Product Engineering and posted regular updates for our alpha users in Slack (met with plenty of 🎉 and 🔥 reactions).

With confidence in both the product experience as well as the engineering systems underpinning them, we decided it was time to start onboarding real Digit members.

🚢 Launching our public Beta

By prioritizing the critical flows of the Direct experience, including core banking services and “magical” product features, we had consciously done little to iterate on our “big onboarding form” (remember that scary thing from a few sections ago?). As such, an admirable group of product managers, user researchers, designers, engineers, and even members of executive leadership banded together to help provide a “white glove” onboarding experience to help our beta users through the signup flow and fill in educational gaps that hadn’t been built yet.

While we had done much to fix and mitigate bugs in the signup flow during our alpha release, adding more users into the funnel helped reveal new areas of fragility. After all, a lot of different boxes have to get ticked during the Direct onboarding process: passing the CIP verification step, creating accounts with MetaBank, mailing a physical debit card to later be activated in-app, withdrawing money from existing Digit savings goals, upgrading the app experience to the new “Direct” home screen, etc.

👀 Looking Backwards

The journey of shipping Direct to beta was an exhilarating time for Engineering. We solved numerous complex problems and learned many lessons through overcoming those challenges. The lessons we learned can be summarized into two main themes:

  • KISS: The tried-and-true engineering lesson: KISS (keep it simple stupid). There are certain aspects of software design that are a hard requirement for a use case as sensitive as banking, such as request idempotency, end-to-end secret encryption, graceful error handling, and reliable system monitoring. Other aspects, however, are more dependent on future scale and potential thrash in system requirements, like supporting multiple financial backends or using separate services to distribute read vs. write load. While investing time into the latter initiatives helps hedge your bets, it comes at the cost of system complexity which can affect future development speed and enlarge surface area for new bugs. Finding the balance between building for scale vs. simplicity is always a challenge and we learned a lot from this process.
  • BaaS Integration Is Hard: Second, we learned how complex it is to integrate smoothly with our BaaS partners. There are many dimensions for the integration to go well, from thoughtful engineering design and navigating many constraints to building a strong partnership. Stay tuned for more Engineering blog posts that dive deeper into each one of these experiences and our lessons learned.

🚀 What’s next?

While taking Direct from 0 to 1 has taken over a solid year of hard work, we are just getting started with what Digit can now do for our members. As engineers, we have participated in product ideation and provided consistent user feedback of our own, all while integrating core banking services into the Digit experience and building a brand new way to intelligently budget with limitless possibilities. Next is taking the product to GA and beyond.

It’s a beyond exciting time to be at Digit Engineering! If you’re interested in building the next era of banking with us, check out our current openings at https://digit.co/careers.

💙Acknowledgments

This article was made possible with the support of Tabitha Blagdon (Engineering Manager), Elaine Lin (Software Engineer), and Carlos Rotger (Software Engineer).

References

Direct℠ is a deposit account established by MetaBank®, N.A., Member FDIC. Digit Visa® Debit card is issued by MetaBank, pursuant to a license from Visa U.S.A. Inc. Card can be used everywhere Visa debit cards are accepted.

  1. https://www.metabank.com/
  2. https://www.galileo-ft.com/
  3. https://www.pcicomplianceguide.org/faq/

--

--