Stitch Full Stack Engineer James Foster walks us through the Stitch tech stack and shares some of the technologies and engineering patterns that our engineering team relies on as we work to build the financial graph for Africa.
At Stitch, we strive to build and maintain an API platform that is robust, responsive and scalable, so we can provide our clients and their customers with a secure, seamless user experience. To achieve this, our technology choices need to be regularly evaluated, ensuring we stay up to date with best practices and accommodate for new products.
We created the radar below, inspired by Thoughtworks and built upon Zalando’s efforts, to visualise and record our technology choices as well as share knowledge and experience using these technologies amongst our engineering teams. We often trial new technologies to fit product requirements, stay abreast of emerging security threats or to improve internal developer experience. There are, however, a set of core technologies that we rely on as essential building blocks. These help us create products and develop features that can enable us to work faster and more efficiently toward our vision of building Africa’s financial graph.
These core technologies, found in the Adopt ring of our radar, form the foundation of a few patterns that we have used successfully at Stitch. Below I will dive into three of those patterns, some of the technologies that support them and the benefits they provide.
Monorepo of Microservices
Managing a rapidly-growing codebase can be challenging, but it’s vital to streamlining product development. One of the earliest decisions to be made is how to structure your codebase to accommodate that growth. Two of the most common approaches are the monorepo and polyrepo architectures.
In a monorepo architecture, multiple applications or services are stored in a single repository, while in a polyrepo, logically separate application code such as web, mobile, or server projects are split into isolated repositories.
At Stitch, we use a monorepo architecture. We chose this approach as it eases refactoring, enables code sharing and improves visibility, helping us to get ahead of the tech debt that often accumulates in startups. Tools such as Turborepo and pnpm help to improve our repository management and accelerate the build time for our codebase.
After deciding how to structure the codebase, another critical design decision is how to architect the system as a whole. There are a number of system architectures to consider, such as an event-driven architecture in which a central unit accepts all data and passes it off to separate modules for processing, or a microservices architecture, in which, instead of building one large application, the system is made up of multiple independently scalable programs.
At Stitch, we favour a microservices architecture for its scalability and reliability benefits. While its advantages have been proven, we have found that combining it with our monorepo approach has meant that common code between microservices introduces challenges with conflicts and coupling. We use Github Actions to minimise these challenges through deployment and validation workflows.
While adopting a microservices architecture, we realised that deploying the fleet of services is a critical component in ensuring we are able to iterate and release rapidly. Kubernetes, a platform to manage the deployment and scaling of our containerized services, has allowed us to effectively meet demand on our platform. Meanwhile, Helm, a tool for packaging up and installing our services, has proven successful for deploying to our various cloud environments.
Local development tools are also essential for managing and testing changes to microservices running in developer’s Kubernetes clusters. They need to allow for code changes to be reflected quickly as well as orchestrate services to optimise resource usage and development productivity. Tilt has recently replaced Skaffold as our local development tool of choice, as Tilt offers fast code reloading, grouping of services and an intuitive user experience that is beneficial when onboarding new hires.
Deciding how to structure your codebase and architect your system to best suit your organisational and domain needs are essential parts of building an adaptable and modular platform for future growth. Time should be spent auditing existing architectures and trialling new ones to enable that growth.
Actors and State Machines
Equally important to system and codebase architectures is the need to utilise design patterns when building services. Design patterns are repeatable solutions to common software problems that, when adopted, can speed up the development process and promote reusability.
The Actor Model is a design pattern that has been fundamental to the way we develop concurrent applications since the early days in Stitch’s journey. The Actor Model is a conceptual model in which a computational process (Actor) stores data and asynchronously sends and receives messages to other Actors, while also being able to create child Actors.
At Stitch, we use this pattern by modelling each bank session as an isolated Actor. This pattern provides a number of benefits, including the ability to encapsulate state and gracefully handle any errors during its execution. Care must be taken when implementing it, however, as we have found it can add unnecessary additional execution layers, leading to messy code. It’s susceptible to deadlocks, a state in which further execution of the application is blocked.
To implement the Actor model, we use Nact, a Typescript framework created by our CTO Natalie Cuthbert, which, along with Koa, a popular NodeJs framework, form the core components of many of the services we build.
Alongside the Actor model, we have adopted Finite State Machines (FSM), a model which defines the various states a system can be in and how it can transition to other states. We’ve found this approach to be very useful for defining and controlling the flow of our services. FSMs work very well with the Actor Model as they are both event-based models of behaviour, and a number of frameworks leverage the Actor model to implement State Machines. One such State Machine framework is XState, which we use in both our hosted pages and bank integration layers.
The Actor Model and State Machines work together to provide a solid foundation, allowing us to layer new features on top of existing functionality in a way that promotes easy maintenance and flexibility.
Security approaches: defence in depth
Security is of paramount importance at Stitch. As such, we assess both our security practices and technology choices regularly to verify that they conform to standards set by the industry. Our OAuth2 and OpenId Connect framework is currently based on Identity Server 4, which provides the foundation for our auth protocols and forms a central part of the service that issues tokens to clients and users, which are used for both authorization and authentication (read more on this from our CTO here).
Securing the traffic flowing between our microservices is another large part of achieving defence in depth. We use our identity server to issue tokens to distributed services with limited scopes so that we have fine-grained control over which resources each service is authorised to access.
A key element in securing our infrastructure is encrypting the restricted set of credentials they use to authenticate with one another. We need to encrypt these credentials so that they can be securely managed and stored in our codebase’s source control. The secrets are encrypted locally, stored and then decrypted by a controller within our Kubernetes clusters. We are currently assessing Sealed Secrets as a replacement to our existing encryption toolchain, as it follows the principle of least privilege and fits well into our existing GitOps approach.
Defence in depth requires that multiple independent defensive methods are layered to protect sensitive data. Layering complementary or redundant controls across our end user, developer, application and infrastructure levels contribute to our overall security posture. These are regularly audited for compliance and questioned through threat modelling to ensure that our assumptions are constantly challenged.
These patterns and the technologies supporting them are just a few of the technology choices we’ve made at Stitch. Our tech stack will continue to change as we investigate and experiment with new technologies to solve deep technical problems within the fintech landscape. We plan on releasing updates of our radar so you can see which technologies we recommend and which to evaluate further.