These past four months have been a rollercoaster ride. In November, we got the news of the merger between Anghami and OSN+, and for our engineering team, it meant a thrilling challenge: rebuilding the OSN+ tech stack from the ground up, all within a 4-month tight deadline.
Why the tight deadline? Well, HBO’s House of The Dragon’s new season comes out early June and we wanted to be production ready, with 4K support before then, giving ample time for all our users to update and for us to iron out the inevitable bugs.
The plan was to have a production ready app by 1st of April, with the same features as the current OSN+ app, then we would implement 4K Playback support, packaged in a premium plan, to be ready by June.
We had a small, talented team – 4 iOS engineers and another 4 on Android. Our goal was to write a brand new video streaming app from the ground up, we needed the app to be on par with giants such as Netflix, Max and Disney Plus.
The odds seemed stacked against us. A small team, an ambitious deadline, and a monumental product to build. But what we lacked in numbers, we made up for in collaboration, efficiency, and resourcefulness. We needed to leverage everything we learned from building Anghami’s Music platform. This story isn’t just about the technical decisions we made, but about the core pillars that fueled our success.
December-April: code, code and some more code
It’s late November, Anghami is stable on both iOS and Android, feature development is frozen until we finish the OSN+ project.
We say our temporary goodbyes to Anghami and create two new projects on Android and iOS, codenamed Microwave – The project name was inspired by one of our product managers joking that we would eventually support Anghami running on a Microwave after integrating our app with yet another car maker.
Development had officially begun. First and foremost, we needed to make sure that backend and frontend teams don’t block each other. Imagine multiple teams working on different aspects of a product, each with their own priorities and timelines. How do you ensure everyone’s on the same page and progress is seamless? Enter our first pillar: a shared contract.
This approach, suggested by one of our engineering leaders, would serve as the technical description for the whole product.
It would be hosted on GitHub for complete transparency and collaboration.
It defined a unified approach to data models (movies, shows, episodes, etc.), API interactions between the apps and backend services (search, home screen, playback, etc.), and even shared constants across all platforms.
We opted to represent this contract using Protobuf (https://protobuf.dev/). We could easily generate source code on all platforms we support, eliminating human error and cutting down manual work.
This contract became the heart of our feature development process. Discussions and decisions regarding features took place here, with clear specifications documented.
Once agreed upon, we would merge the changes to the contract, making them available to all platforms, and each team could independently work on their assigned tasks, confident that everything would seamlessly integrate in the final product.
Let’s take search for instance, we designed the contract early in the development process. Our backend did not yet have any available content, and implementing search on their side was still far off. From a product and frontend perspective, however, we were ready to go.
The above contract describes the feature: given a query, we would get a list of sections to display, and every section is either a Grid or a Carousel of “Long” posters. We built the whole feature client-side, simulated different network environments to tweak the user experience, and added unit tests. A few months later, our backend was ready to go, we hook things up, and lo and behold, it just works.
With the above, we had largely solved backend-frontend friction. We still needed to make sure individual developers on Mobile could work as fast as possible, without blocking or stepping on each others’ toes. This was our second pillar: Modularity and independent work
I’m going to take a deeper dive into the technical decisions we used to solve that problem, feel free to skip ahead a few paragraphs for the general story.
At a high level, both the iOS and Android apps are based on the same general idea. We decided to represent the app, and as a result our dependencies in this hierarchy:
The above is a simplified view but it should give you a general idea about the flow of the application.
A user needs to be logged in in order to subscribe. A user needs to have a profile selected in order to browse Movies and Series, as well as play them.
The boxes represent the dependency containers. The purpose is that you’re not able to access dependencies when you’re in a different part of the hierarchy.
You can’t access playback functionality unless you have a “Logged In + Profile container”, and in order to create this container and its dependencies, you need an account + selected profile. The same idea goes for the “Logged in container”, but you only need an account.
This approach really simplified feature development. Developers don’t need to worry about managing the current user session, or making sure they have a valid session, or a selected profile. All they needed was an instance of the correct container.
For example, our “Add To List” feature is profile-specific. That feature’s module operates on the “logged in + profile selected” container, the developer would inject that container into their feature module, and build the feature with a guaranteed valid session and profile in mind.
If the user gets logged out or the profile is deselected, the container would de-allocate, along with the feature, and the app state would take the user back to Login / Profile selection. However the feature module itself has no visibility on that, it only operates in a state where a valid user session and a profile is available.
With that, every developer or pair of developers could take a feature and build it independently. The final app would essentially be the one “wiring” the features together to create a cohesive experience.
We could run the search feature (and every other feature) without logging in using a Mock “Logged in” container, without having any other part of the app running, and without being connected to the API.
Product managers could test out how it feels and we could iterate and write tests for the feature, and once we were ready to hook it up to the API, all it took was replacing the mocked dependencies with real ones, and the shared contract made that move seamless.
Launch week
Fast forward a few stressful months, launch day was almost upon us. We were tying up loose, making sure everything is in order to start rolling out the updates.
For context, on the Mobile side of things, our priorities were as follows: iOS is our biggest user base and thus our number 1 priority. Apple TV came in second, and then Android last.
Android TV’s user base was almost as significant as our iOS user base, but we had previously decided that the Android TV app would be a wrapper around our Web based TV app, and early experiments seemed promising, so Android TV didn’t fall under the Mobile umbrella.
We started rolling out updates across all platforms on April 1st. On the mobile side, things were looking great – the apps were stable, numbers were looking good. We had a few bugs here and there that we were tackling, but nothing major.
However things have to go wrong around release time, there’s even a law for that.
Our video analytics data wasn’t looking the best on Android TV, and before long, user complaints started pouring in as we were scrambling to figure out the problem. Being a video streaming app, you can imagine how big of a deal playback issues are.
Our web team was hard at work looking for solutions, but ultimately, we decided to rewrite the app to be fully native. It was already Thursday by then. Suddenly, the Android platform had become the highest priority for the mobile team.
The Android phone app was released, but by no means complete. Having been our lowest priority, we were still actively developing the Downloads feature on Android, having accepted that it won’t make it to the 1.0 launch. Now we had to build a fully native TV app on top of that, in two days.
We already had two engineers knee deep in the Downloads feature that users were missing, and another working on fixing bugs that showed up post release. It was pretty clear that going fully native in that timespan was impossible. We needed an immediate solution for our users’ frustrations.
We created an emergency group consisting of myself, my manager, and the last android engineer we had, to work on the Android side of things, and a web engineer to assist on the web side.
Our goal was to, first and foremost, allow our users to have smooth playback, so we opted to only replace the web player with a native implementation, reusing most of the code from our existing Android player.
By Friday night, we had the same Web based app, but upon playing it would launch a native activity, hosting a composable view with a native player.
We had our immediate solution, it wasn’t ideal by any means, but it proved to be a huge improvement, and it would buy us time to further enhance the experience and eventually transition into a fully native app.
Post release
Despite the challenges we faced, the release of the new OSN+ app has been a resounding success. While we encountered several issues – and anticipate more as we continue to grow – our overall performance has significantly improved across all key metrics.
Our focus on quality has paid off handsomely:
- 84% reduction in app crashes: Our new architecture and comprehensive tests have dramatically improved app stability.
- Record low hang rates: Users now experience smoother, more responsive interactions with the app.
- Enhanced observability: With monitoring baked into every platform, we can quickly identify and resolve issues, ensuring a consistently high-quality user experience.
From a product standpoint, The numbers speak for themselves:
- 30% increase in time spent: Users are finding more value in the app, spending significantly more time exploring our content.
- Broader feature adoption: We’re seeing users engage with a wider range of features, indicating a more comprehensive and satisfying app experience.
- 20%+ boost in conversions: More users are transitioning to paid subscriptions, a clear vote of confidence in our improved offering.
Our engineers are not just more productive – they’re more inspired:
Cutting-edge tools: Leveraging modern Swift concurrency on iOS and Kotlin flows with Jetpack Compose on Android has supercharged our development process.
Rapid feature development: Post-release, we’ve already added Chromecast support within a week and 4K streaming within a month.
Continuous innovation: With our new tech stack, we’re poised to roll out exciting features at an unprecedented pace.
This successful release is just the beginning. With our new foundation, we’re well-positioned to:
- Continuously refine the user experience
- Rapidly respond to user feedback
- Innovate with new features that push the boundaries of streaming technology
To sum up
Our success in rebuilding OSN+ wasn’t just luck—it was the culmination of years of experience with Anghami. As a small team, we’ve always punched above our weight, delivering features and fixes at an impressive scale. This project reinforced a crucial lesson: focus on what truly matters and be willing to make tough choices to meet tight deadlines.
We didn’t shy away from our past failures; instead, we used them as stepping stones. While we made mistakes during this project, we ensured they were new ones—learning and adapting rapidly. This hectic ride pushed us to be creative, collaborative, and resilient.
Now, as we continue developing both Anghami and OSN+, we’re more excited than ever. Our goal is simple yet ambitious: to provide the best entertainment experience in both music and video. We invite you to try our apps and join us on this journey. Your feedback will help shape the future of digital entertainment.