How scaled CS can help your team get more done
First of three articles explaining digital CS or scaled customer success, why it's important and how to spot and leverage customer success scaling opportunities in your own organizationLearn More
When we started Ctrl, our mission was to deliver a cutting-edge product with simple usability and a killer user experience.
Our first challenge was to work out what tech to use – and that’s an increasingly hard task nowadays. With so many friends in the ecosystem, and having seen so many startups as a VC, it felt to me like there were endless options to consider. Picking the perfect tools to deliver a high velocity product that’s also scalable and maintainable wasn’t easy, but after a lot of research we finally decided on the options below.
Because Ctrl is so heavily reliant on integrations (and is at its heart an integration-tech product) we had to be as agnostic as possible when developing our core.
We wanted to get to a place where each integration is almost standalone – developed almost as if it isn’t part of the Ctrl ecosystem – so we can approach it as an API ourselves. By doing that, we’ve made it possible to develop new integrations in a matter of hours end-to-end.
We knew from the get-go we wanted to utilize the strengths of GraphQL for Ctrl; as a product, we’re so information-heavy and experience-based that its benefits were crystal clear.
The GraphQL ecosystem offers some huge out-of-the-box upsides, particularly when paired with Apollo: avoidance of over/underfetching issues; strict type definitions; and the fact it allows us to bootstrap an entire microservice/subgraph in a matter of minutes.
Leveraging Apollo’s Supergraph/Federation capabilities, we were able to create a completely agnostic microservice architecture; every integration is only in charge of itself, while the Gateway orchestrates all of Ctrl’s secret-sauce logic.
We will drill down further into how we bootstrap and approach every integration as a GraphQL microservice in a future blogpost, as this is the very core of Ctrl's tech & backend services.
Picking Typescript was a no-brainer; it plays very well in the GraphQL ecosystem and with Apollo, and the typings allowed us to create a unified layer between the frontend and backend without having to worry about specific fields and their structure.
We tried to be as schema-first as possible going over the integrations, translating them primarily into GraphQL schemas. Using GraphQL codegen, we typed the resolvers out of the schemas, and voilà:we had fully typed GraphQL subgraphs, ready to accept our requests.
We knew we needed a simple way to handle databases, and one that played nice with our chosen ecosystem.
Utilizing Prisma and TypeGraphQL meant we were able to fire up another microservice entirely in charge of managing Ctrl’s database on top of GraphQL. Meanwhile, writing custom resolvers using TypeGraphQL allowed us to create the exact endpoints we required for our own custom Ctrl use cases.
We decided we needed to write a common @ctrl/subgraph library so that we could orchestrate, monitor and analyze all our subgraphs. As a result, we decided to work in a monorepo environment, where we can easily and quickly develop new subgraphs and rely on our own library.
Using NX, we’re able to run a thorough CI/CD process, publishing our graphs to Apollo based only on the relevantly changed packages.
Having our frontend and backend in the same repository also allowed us to make horizontal changes very quickly, enabling full-stack development as an inherent basis of our engineering culture.
Our CD is also really straightforward; we’re using NX to identify which packages were changed, building a docker image for the required package and deploying it to our Amazon Elastic Container Service. Finally, using helm we’re deploying the docker image to our K8S cluster.
The synergy between Apollo Client and Apollo’s backend is amazing, and we’re utilizing every single feature of it. The code generation using Apollo CLI, along with the Apollo VSCode extension that allows for auto-completion, is nothing short of remarkable.
Also, because Ctrl is such a content-rich application, we need to rely heavily on cache to provide a slick user experience. Apollo client’s OOTB cache management comes to the rescue here, giving a smooth user experience while keeping data updated under the hood.
Rather than Apollo’s built-in state management, we opted to use MobX. With minimal boilerplate and maximum flexibility, it allowed us to model the data as we saw fit, and to make the UI components as dumb and testable as possible. MobX is like the glue-layer that sits between between Apollo GraphQL and the UI, ensuring every single piece is working independently.
We’ll probably elaborate more on more specific front-end picks like styled-components and Storybook in future blogposts, as well as diving deeper into our MobX architecture.
Building a fully functional, maintainable and scalable product in 2022 may seem easy, but with so many moving parts needing to play nicely together in the same ecosystem, a great deal has to be taken into consideration prior to starting development on the product’s core.
It is obviously easier in hindsight, which is why I’d recommend building something small first. Test the waters, see how things go – and then use what you’ve learned to build a more robust, comprehensive solution.