Due to the dynamic nature of ad tech, agility has become a basic need for business success. Innovating and adapting our technology to address pain points and benefit our clients is a key goal of PubMatic.
To further improve our platform and more easily deliver agile, robust and stable products to our customers, we recently rolled out a microservices architecture across the enterprise.
Initially, the PubMatic code base was a monolithic application. Most of the engineering teams used to work on a single repository and shared development life cycle. This meant that builds could take hours. New engineers had to dive inside a complex ocean of code and this limited our efficiency. Additionally, on the day of deployment, all teams needed to coordinate for a smooth, stable deployment.
We realized that we needed to update our process and systems. First, our team envisioned the future needs, challenges and growth inhibitors. We then architected a new microservice architecture in an organized and well-planned manner.
During our implementation, five key lessons stood out to me:
1. Thinking in Services Culture
Today, code re-usability is a holy grail and service reusability is paramount. I learned it was important to write common services and not to write common libraries.
Traditionally, the developer community is used to object-oriented programming (OOPs) and component-based architectures. This gave rise to in-process dependencies which can hamper the evolution of applications. In many cases, we wrote generic modules to use as an in- process dependency (or a library) across services. We discovered this really isn’t necessary—just avoid it.
2. Distributed Transactions
For a call spanning across multiple services, it is important to build systems that will handle the failure of one of the services. We design our services and business flows to avoid distributed transactions, or achieve something similar, using a simple state/status management for entities. There are standards for distributed transactions being developed by the community, however, I suggest avoiding them when possible. If you have few specific cases where they are needed, be smart and handle it without breaking architectural constraints.
3. Resource or Entity-Centric End Points
Just making an API and exposing HTTP end points could complicate matters. It is important you choose the right architectural style. Two of the more popular options are Remote Procedure Calls (RPC) and Representational State Transfer (REST). There are benefits and limitations for each, however. Thus, it is important to consider the types and build your API accordingly.
A simple rule to help you build is use nouns as a service names and avoid verbs. I recommend that the resource design not be driven by the UI and UX. This is a common issue and making APIs fit into the page design, rather than the design fitting the APIs can cause long-term problems.
Decentralization is the default and centralization the exception. The code repository, development, release and deployment cycle, or anything associated with a service lifecycle, should be independent.
Create a separate repository for each microservice. Developers tend to create a single repository and then have modules within it for each service. Let each microservice decide what tech stack or supporting components to use. Try to avoid a central database to avoid future fall-out.
5. Service Granularity
Service granularity is dependent on your business flow and practical considerations. It has a direct impact on performance, complexity and effectiveness of an architectural implementation. My recommendation is to keep a service code base managed by a couple of developers.
To keep things simple and create less chatty services, group the services by checking the domain model and their relationship such as inventory management, order management, security, billing, etc. Keeping a service per application would be ideal though it is difficult to achieve and this method increases the complexity of service orchestration.
Aligning architectural principles brings up other challenges and problems. Avoid allowing developers to do resume driven development. Consider your needs and avoid unnecessary framework.
Simply because a tool or framework is public, you might not need it. Do whatever it takes to follow microservice architectural constraints in a simple, cost-effective way. Try to make the service self-dependent and freely evolvable.
At PubMatic, we put the customer first and focus on updating our technology to benefit our partners. The updates to our architecture have prevented many future problems and allowed us to deliver a high-quality, stable solution quickly.