Morozov Blog

Contract-Based Development

Speeding Up Software Development at Scale: Lessons from Warner Bros Discovery

Introduction

At Warner Bros Discovery, the Direct-to-Consumer Ad Tech teams stumbled upon a way to significantly speed up software delivery. Supporting the Max streaming service, Discovery+, BT Sport apps, and more, these teams rely on a set of monetization-focused client-side libraries written in BrightScript, C++, Kotlin, Swift, and TypeScript (why no cross-platform?) as well as backend services to deliver high-quality ad experiences.

This article takes a deep dive into the approach that helped streamline software development across teams—focusing on technical collaboration, quality-first principles, and smart work division and prioritization —all while tackling complex challenges. Buckle up! 🚀

The Challenge: Complexity at Scale

Delivering global streaming services comes with its own unique set of challenges. Requirements for these services can vary depending on device, user, or even region, not even mentioning supporting multiple streaming services at the same time, like out teams do - Max and Discovery+, for example. Add multiple device types to the mix, and things get really fun—features must work seamlessly across a wide array of platforms. On top of that, building features involves collaboration between several teams, each bringing something different to the table.

All this contributes to a highly intricate development environment. And it's not just about shipping features fast—it's about doing it at scale, across teams, while maintaining the highest standards for the user experience, performance and reliability. No pressure, right? 😅

The Solution: Contract-First Development

To tackle these challenges and speed up development, the teams adopted a "contract-first" approach. Let's unpack what this means:

1. Establishing Clear Contracts

The first step is defining the contracts for each component involved—essentially, the "rules of engagement." For example, if a new feature requires changes to both the front-end and back-end, the teams will define the API endpoints and data structures upfront. The front-end team can then work with mocked data while the back-end folks do their magic.

However, this strategy works beyond just backend APIs! It can be used anywhere there’s an interface or even a set of public methods - for example, between Model, View, and ViewModel layers in an app that's using MVVM architecture, or interfaces/abstractions used in any code following SOLID principles internal to a backend service, or even mapping layers between data streams transformations.

Once all stakeholders agree on these contracts, teams can work independently, which means no one is waiting around for someone else to finish their part. 🙌

Bonus points for using assert functionality that's available in most languages nowadays. It allows throwing exceptions in non-release builds to enforce contracts/expected behavior at every touchpoint, but is automatically stripped away in release builds by the tooling, limiting impact in case of bugs.

assert documentation, in no particular order:

Most laungagues have it nowadays!

2. Breaking Dependencies with Mocks and Feature Flags

Mocks are our best friends here. 🐍 By simulating functionality that's not yet available, different teams can develop in parallel without being blocked by other teams. This is a bit like a play rehearsal—you use stand-ins until the actual actors arrive.

Feature flags also play a crucial role. They let us safely roll out features in controlled phases and run A/B tests—and if things go sideways, we can easily turn them off. Nothing to see here, folks! 🚦

3. Parallel Development with a Focus on Quality

With contracts and mocks in place, teams can work in parallel—a huge time-saver. Quality is maintained through comprehensive unit and integration tests that ensure everything behaves as expected. Feature flags help incrementally release new code, minimizing risk. Think of it as dipping a toe in the pool before diving in—safer, smarter, and definitely less shocking! 🏊

Example: Customizing Ad UI for Sponsorship Ads

Traditionally, when adding features involving both front-end and back-end changes, a sequential workflow caused delays. The front-end team would have to wait until the back-end was fully implemented. 🤦‍♂️

Enter contract-first development:

  • Back-end Development: The back-end team defines and adds metadata indicating whether an ad is standard or sponsored. This metadata is clearly outlined in the API contract, and an endpoint with a mocked contract can be created.
  • Front-end Development: With the backend contract ready, front-end developers modify the contract between relevant internal components as well: ex. the component that fetches the data and the UI layer, using mocked responses/mappers to develop and test their changes. The real data can replace the mocked data once the back-end work is complete.
  • Testing and Integration: The teams use a set of unit and integration tests to validate the contracts and confirm everything works as expected, mocking functionality that's not implemented. Integration testing ensures the system behaves when real and mocked data come together.

The beauty of this approach? All teams work concurrently, speeding up development and improving quality. Win-win! 🏆

Drawbacks of Contract-First Approach

  1. Quality of Upfront Work: Since contracts are defined early, they need to be pretty accurate. Otherwise, re-working contracts deep into the implementatino phase can be costly. It takes experience to get it right, but it's a worthy skill to hone. 🤷

  2. Communication: Effective communication is crucial. With multiple distributed teams, syncing up and adjusting to changes can be tricky and in the beginning, can feel not unlike trying to herd cats! 🐈

Benefits of the Contract-First Approach

  1. Reduced Bottlenecks: Defining contracts upfront means teams can work independently, minimizing waiting and avoiding bottlenecks. Nobody likes getting stuck in traffic! 🚗
  2. Improved Quality: Contracts and mocks enable early testing, catching issues before full integration.
  3. Faster Delivery: Parallel development means faster feature delivery—speed wins the day! ⚡
  4. Flexibility: Feature flags provide flexibility for incremental releases and easy rollbacks—like having an undo button for your features. 🔄

Conclusion

The contract-first approach has enabled Warner Bros Discovery’s Direct-to-Consumer Ad Tech teams to move fast while maintaining quality across multiple platforms. By defining contracts upfront, breaking dependencies with mocks, and managing feature rollouts with flags, teams can deliver high-quality features—quickly and efficiently.

Of course, it takes discipline, a lot of communication, and alignment—but once teams are on the same page, the benefits are huge. Have you tried a similar approach in your teams? Share your experiences (and war stories)! 💬

Notes

There are specific video-streaming considerations behind the decision to avoid cross-platform client-side solutions, which we won't dive into here. 😉


Sergey Morozov

Written by Sergey Morozov who lives and works in New York City building useful things.