- How did the Threads iOS staff preserve the app’s efficiency throughout its unbelievable development?
- Right here’s how Meta’s Threads staff thinks about efficiency, together with the important thing metrics we monitor to maintain the app wholesome.
- We’re additionally diving into some case research that influence publish reliability and navigation latency.
When Meta launched Threads in 2023, it grew to become the fastest-growing app in historical past, gaining 100 million customers in solely 5 days. The app now has grown to greater than 300 million month-to-month worldwide customers, and its improvement staff has expanded from a small group of scrappy engineers to a company with greater than 100 contributors.
Wanting again on the place the Threads iOS app was a yr in the past, a lot has modified: We’ve expanded into Europe, built-in with the Fediverse, launched a public API, developed many new methods for folks to share what’s occurring of their world, and launched new strategies to search out and browse one of the best content material being produced. We even celebrated our first birthday with occasion hats and scratch-off app icons!
To verify the app is straightforward and pleasant to make use of—and to scale with a rapidly rising person base and improvement staff—it needs to be performant. Right here’s how we take into consideration efficiency within the Threads iOS app, what we’ve realized in our first yr, and the way we’ve tackled a couple of of our greatest efficiency challenges.
How Threads measures efficiency at scale
Having a quick and performant app is important to offering one of the best person expertise. We wish Threads to be one of the best place for reside, artistic commentary about what’s taking place now; which means Threads additionally must be the quickest and most responsive app in its class. If the app doesn’t really feel lightning quick, or if it hangs or drains a cellphone’s battery, nobody will need to use it. Our options should work reliably and fail occasionally it doesn’t matter what sort of cellphone somebody is utilizing, or how a lot reminiscence their cellphone has, or whether or not they’re utilizing Threads someplace that has strong mobile protection or a community that retains dropping out.
Some efficiency points are encountered solely not often however nonetheless might be irritating. Because the iOS app’s utilization grew quickly throughout our first yr after launch, we wished to study what the largest ache factors had been for most individuals in addition to the intense efficiency points skilled by a small proportion of customers. We measured how rapidly the app launches, how lengthy it takes to put up a photograph or video, how usually we might expertise crashes, and what number of bug studies had been filed by folks.
%FIRE: Irritating image-render expertise
Along with all of the textual content updates folks share, we now have a whole lot of photographs shared on Threads. When photographs load slowly or by no means, that may trigger somebody to cease utilizing the app. That’s why we monitor an vital metric to alert when there’s a regression in how photographs are loading for our customers. That metric, %FIRE, is the share of people that expertise a irritating image-render expertise, and it’s calculated as proven in Determine 1, beneath.

Every kind of issues can regress %FIRE, each on the shopper finish and the backend, however not all image-rendering bugs are coated by this metric. For instance, in Threads iOS, we had a bug earlier this yr the place person profile photographs would flicker due to how we had been evaluating view fashions when reusing them. That triggered a irritating person expertise, however not one the place customers would contribute to %FIRE.
Time-to-network content material (TTNC)
How briskly the app begins and how briskly we ship a person’s feed to them can also be vital. We all know if somebody has to stare at an app launch display, exercise spinner, or loading shimmer for too lengthy, they’ll simply shut the app. That is all measured in one thing we name TTNC, or time-to-network content material. Along with having the app begin quick, folks additionally need us to point out them what’s taking place now, so TTNC measures how briskly we’re in a position to load a recent, personalised feed, not simply cached, regionally saved posts.
The Threads iOS staff has additionally improved the app launch time by conserving the app’s binary dimension small. Each time somebody tries to commit code to Threads, they’re alerted if that code change would improve our app’s binary dimension above a configured threshold. Code that violates our binary dimension coverage isn’t allowed to be merged.
We’re proactive, too: To assist scale back TTNC, we now have spent a whole lot of time since Threads launched eradicating pointless code and graphics property from our app bundle, leading to a binary one-quarter the dimensions of Instagram. It doesn’t harm that this can also scale back our iOS app’s construct time, which makes the app extra enjoyable to develop! Threads compiles two occasions quicker than Instagram for our non-incremental builds.
Creation-publish success fee (cPSR)
The place %FIRE and TTNC measure how content material is offered to a person, we now have one different vital metric: cPSR, the creation-publish success fee. We measure this individually for textual content posts, photographs, and video revealed to Threads. When somebody tries to put up a photograph or video, many issues can stop it from succeeding. Images and movies are regionally transcoded into codecs we need to add, which occurs asynchronously as a part of the publishing course of. They each use much more information and take longer than textual content to add, so there’s extra time for one thing to go flawed. A person may background the app after they faucet “Put up” with out ready for it to succeed, which on iOS may give us just a few seconds to finish the add earlier than we’re terminated by the working system.
Later on this weblog put up, we’ll go into among the methods we’re utilizing to enhance cPSR.
Deep dive: Navigation latency
Navigation latency is vital to the person expertise as a result of it’s tied to how briskly the app begins and all the things the person does as soon as the app has launched. After we measure navigation latency, we need to understand how lengthy it takes to complete rendering content material after a person navigates to a part of the app. That could possibly be after app begin, both from launching Threads immediately in your cellphone, or by tapping on a push notification from Threads, or by merely tapping on a put up in your Feed and navigating to the dialog view.
Early in 2024, the Threads Efficiency staff knew we wished to concentrate on a couple of key areas, however which of them? Knowledge from Instagram prompt navigation latency is vital, however Threads is used in a different way than Instagram. Having been accessible to obtain for under six months on the time, we knew that to prioritize areas of enchancment we might first should spend a while studying.
Studying from a boundary take a look at
We began by making a boundary take a look at to measure latency, specializing in a couple of key locations that folks go to after they launch Threads or use the app. A boundary take a look at is one the place we measure excessive ends of a boundary to study what the impact is. In our case, we launched a slight little bit of latency when a small proportion of our customers would navigate to a person profile, to the conversion view for a put up, or to their exercise feed.
Latency injection | Each day Lively Customers | Foreground classes | Likes | Dialog views | |
Exercise: 0.12s Dialog: 0.29s Profile: 0.28s |
In-app navigation | ||||
Exercise: 0.15s Dialog: 0.36s Profile: 0.35s |
-0.68% | ||||
Exercise: 0.19s Dialog: 0.54s Profile: 0.53s |
-0.54% | -0.81% | |||
Exercise: 0.12s Dialog: 0.29s Profile: 0.28s |
App launch | -0.37% | -0.67% | -1.63% | |
Exercise: 0.15s Dialog: 0.36s Profile: 0.35s |
-0.67% | -2.55% | |||
Exercise: 0.19s Dialog: 0.54s Profile: 0.53s |
-0.52% | -0.65% |
Desk 1: Navigation latency boundary take a look at outcomes.
This latency would enable us to extrapolate what the impact can be if we equally improved how we delivered content material to these views.
We already had strong analytics logging, however we didn’t have the power to distinguish between navigation to those views from a chilly app launch and from inside the app. After including that, we injected latency into three buckets, every with slight variability relying on floor.
We realized that iOS customers don’t tolerate a whole lot of latency. The extra we added, the much less usually they’d launch the app and the much less time they’d keep in it. With the smallest latency injection, the influence was small or negligible for some views, however the largest injections had unfavourable results throughout the board. Folks would learn fewer posts, put up much less usually themselves, and typically work together much less with the app. Bear in mind, we weren’t injecting latency into the core feed, both; simply into the profile, permalink, and exercise.
Measuring navigation latency with SLATE

Navigation latency is troublesome to measure persistently. In case you have an enormous app that does many alternative issues, it’s important to have a constant means of “beginning” your timer, measuring time to render a view throughout many alternative surfaces with several types of content material and conduct, and at last “stopping” your timer. Additionally, you might have to pay attention to error states and empty views, which must be thought-about terminal states. There might be many permutations and customized implementations throughout all of an app’s surfaces.
To unravel this drawback and measure navigation latency persistently, we developed a brand new software we name SLATE: the “Systemic LATEncy” logger. It provides us the power to watch occasions that set off a brand new navigation when the person interface (UI) is being constructed, when exercise spinners or shimmers are displayed, when content material is displayed from the community, and when a person sees an error situation. It’s applied utilizing a set of widespread elements which might be the inspiration for lots of our UI and a system that measures efficiency by setting “markers” in code for particular occasions. Sometimes these markers are created with a selected function in thoughts. The wonderful thing about SLATE is that it mechanically creates these markers for a developer, so long as they’re utilizing widespread elements. This makes the system extremely scalable and maintainable in a really massive code base similar to Threads or Instagram.
When our iOS builders are creating a brand new characteristic, it’s simple to see if it has an impact on navigation latency. Anybody can allow the SLATE debugger (depicted in Picture 1, beneath) proper within the inner construct of our app, and it’s simple to create a dashboard to allow them to get a report about how their code is operating in manufacturing.
Case research: Utilizing SLATE to validate GraphQL adoption
During the last yr, each Instagram and Threads have been adopting GraphQL for community requests. Although Meta created GraphQL again in 2012, we constructed Instagram on a community stack based mostly on REST, so Threads for iOS and Android initially inherited that technical legacy.
When Threads for Net was developed, it was a recent code base constructed on the fashionable GraphQL commonplace as an alternative of REST. Whereas this was nice for internet, it meant that new options delivered to each internet and iOS/Android needed to be written twice: as soon as to help the GraphQL endpoints and as soon as for REST. We wished to maneuver new improvement to GraphQL, however as a result of the implementation was unproven for Threads, we first wanted to measure and ensure it was able to be adopted. We anticipated GraphQL to lead to much less information that will must be moved over the community, however to parse and retailer the information, the infrastructure to help it’d introduce extra latency.
We determined to run a take a look at the place we took considered one of our views and applied its community supply code utilizing GraphQL. Then we may run the REST and GraphQL implementations facet by facet and evaluate the outcomes. We opted to run the take a look at for the “person listing” views that energy Followers and Following lists and decide if the brand new code that delivered and parsed GraphQL responses was at the very least as quick because the legacy REST code.
This was simple to do utilizing Swift. We created an abstraction that extracted the present API right into a protocol that each the REST and GraphQL code may use; then when the code can be referred to as, a manufacturing facility technique generated the suitable supplier.
As soon as the code was operating, we would have liked to measure the influence on the end-to-end latency of fetching outcomes from the community and rendering the content material on display. SLATE to the rescue! Utilizing SLATE’s efficiency markers, we may simply evaluate latency information for every of the completely different person view community implementations.
Beneath is an instance graph of the latency information (p95) for when a person views the listing of their followers. The blue line compares the REST and GraphQL latency information, that are very comparable. We noticed comparable outcomes throughout all of the completely different views, which gave the Threads iOS staff confidence to undertake GraphQL for all new endpoints.

Deep dive: Publish reliability and latency
As talked about beforehand, cPSR is among the prime metrics we’re attempting to enhance on Threads, as a result of if folks can’t reliably put up what they need, they’ll have a horrible person expertise. We additionally know from studying user-submitted bug studies that posting generally is a supply of frustration for folks.
Let’s dive into two options added to Threads iOS that method enhancing the posting expertise in very other ways: Drafts, and decreasing the perceived latency of textual content posts.
Drafts
In early 2024, Threads launched fundamental saving of drafts on iOS and Android. Along with being considered one of our most user-requested options, Drafts gives resiliency to sudden failures similar to dangerous community connectivity. user-filed bug studies, we had seen that the highest concern was being unable to put up. Usually customers didn’t know why they couldn’t put up. We knew a draft characteristic would assist with a few of these considerations.
These person bug studies had been used to measure the success of Drafts. Drafts doesn’t immediately transfer cPSR, which measures the reliability of posting in a single session, however we theorized it’d lead to both extra posts being created or much less total person frustration with posting. We launched Drafts to a small group of individuals and in contrast the variety of subsequent bug studies associated to posting they submitted in comparison with studies from individuals who didn’t have Drafts. We found that 26 p.c fewer folks submitted bug studies about posting if that they had Drafts. The characteristic was clearly making a distinction.
We rapidly adopted up with a small however mandatory enchancment. Beforehand, if a person ran right into a community subject whereas posting, they’d be requested in the event that they wished to retry or discard their put up, however got no possibility to put it aside as a draft. This meant lots of people who couldn’t ship had been shedding their put up, which was irritating. Sadly, measuring the influence of this resiliency characteristic was additionally troublesome as a result of not many individuals bumped into it.
Then, a stunning factor occurred: A critical bug took down all of Threads for a brief time frame. Although this was dangerous, it had the facet impact of testing a few of our resiliency options, together with Drafts. We noticed an enormous spike in utilization in the course of the brief outage, which confirmed that folks had been benefiting from having the ability to save their posts if there was a major problem.
You may see in Determine 3 beneath the spike in Drafts utilization in the course of the outage round midday on March 31.

Minimizing Drafts’ native storage
After Drafts was launched to the general public, we found an unlucky bug: The typical quantity of storage Threads used was growing dramatically. Folks on Threads observed, too, and posted a whole lot of complaints about it. A few of these folks reported that Threads was taking over many gigabytes of cupboard space. Sustaining a low disk footprint helps efficiency, and addressing this bug supplied a chance to study in regards to the influence of extreme disk utilization in Threads.

The offender was Drafts. Within the iOS app, we use PHPickerViewController, launched in iOS 14, to energy the picture and video gallery offered within the Composer.
PHPickerViewController is a pleasant element that runs out of course of and gives customers with privateness and security by permitting them to offer an app entry to precisely the media they need. When a photograph is chosen, an app receives a URL that factors to the picture asset on the gadget. We discovered, nonetheless, that entry to this picture is simply momentary; between classes, Threads would lose permission to learn a picture that had been hooked up to a draft. As well as, if a person deleted a picture from the gallery, it could additionally disappear from a draft, which was not very best.
The answer was to repeat photographs and movies to an space within the utility container that was particular to Drafts. Sadly, copied media wasn’t being cleaned up solely, main disk utilization to develop, generally dramatically, over time.
Cleansing up this extreme disk utilization had dramatic ends in areas we didn’t count on. App launch grew to become quicker (-0.35%), our each day energetic customers grew (+0.21%), and other people posted extra unique content material (+0.76%)—fairly much more.

Blazing quick textual content posts
Just like doing the navigation latency boundary take a look at, the efficiency staff had beforehand measured the influence of latency on textual content replies and knew we wished to enhance them. Along with implementing enhancements to scale back absolute latency, we determined to scale back perceived latency.
A brand new characteristic in Threads’ community stack permits the server to inform a shopper when a posting request has been absolutely acquired, however earlier than it’s been processed and revealed. Most failures occur between the cell shopper and Threads’ servers, so as soon as a request is acquired, it’s very more likely to succeed.
Utilizing the brand new server-acknowledgement callback, the iOS shopper may now current the “Posted” toast when a publish request was acquired, however earlier than it was absolutely created within the backend. It will seem as if textual content posts had been publishing a little bit quicker. The result’s a greater person expertise that makes the app really feel extra conversational.
Adopting Swift Concurrency for extra secure code
Migrating the Threads iOS publishing code from a synchronous mannequin to an asynchronous one additionally revealed the potential for race circumstances. Along with the asynchronous transcoding step talked about beforehand, there have been some new ones associated to administration of the add duties and media metadata. We observed some mysterious malformed payloads that turned up solely sometimes in our analytics and dashboards. Working at large scale tends to show up some uncommon edge circumstances that may have unfavourable penalties on efficiency metrics and provides folks a nasty person expertise.
Top-of-the-line issues about working within the Threads code base is that it’s principally in Swift. Among the publishing code was written in Goal-C, although. Whereas Goal-C has a whole lot of advantages, Swift’s sturdy data-race protections and kind security can be an enchancment, so we determined emigrate Threads’ publishing code to Swift.
iOS groups all through Meta are adopting Swift’s “full concurrency” in preparation for shifting to Swift 6. On the Threads staff, we’ve been migrating older Swift code and utilizing full concurrency in new frameworks that we’re constructing. Shifting to finish concurrency might be the largest change to iOS improvement since Computerized Reference Counting (ARC) was launched means again in iOS 4. Once you undertake full concurrency, Swift does an important job at stopping pesky information races, similar to some that had been inflicting points with our optimistic uploader. Should you haven’t began adopting Swift’s strict concurrency by enabling full concurrency in your code, you may discover that your code is extra secure and fewer liable to hard-to-debug issues brought on by information races.
The way forward for Threads iOS efficiency
As Threads continues to scale in its second yr and past, the iOS app should adapt to fulfill new challenges. As we add new product options, we’ll preserve monitoring our time-worn metrics similar to %FIRE, TTNC, and cPSR to ensure the person expertise doesn’t degrade. We’re updating the code that delivers posts to you, so that you see content material quicker and expertise fewer loading indicators. We’ll proceed to benefit from essentially the most trendy language options in Swift, which is able to make the app extra secure and quicker to construct and cargo into reminiscence. In the meantime, we’re going to iterate and evolve instruments like SLATE that assist us enhance our testing and debug regressions.
As a part of the Threads neighborhood, you may as well contribute to creating the app higher. We talked about earlier that user-submitted bug studies had been used to determine areas for the event staff to concentrate on and confirm that options like Drafts had been really fixing person frustrations. In each Threads and Instagram, you may long-press on the House tab or shake your cellphone to submit a bug report. We actually do learn them.