<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Instagram Engineering on Medium]]></title>
        <description><![CDATA[Stories by Instagram Engineering on Medium]]></description>
        <link>https://medium.com/@InstagramEng?source=rss-a4c6efa67fe0------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*8x_1IP3b75o5u9M4LgFBig.jpeg</url>
            <title>Stories by Instagram Engineering on Medium</title>
            <link>https://medium.com/@InstagramEng?source=rss-a4c6efa67fe0------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 01:30:08 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@InstagramEng/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Interview with Tamar Shapiro, Instagram’s Head of Analytics]]></title>
            <link>https://medium.com/instagram-engineering/interview-with-tamar-shapiro-instagrams-head-of-analytics-c81946d02b90?source=rss-a4c6efa67fe0------2</link>
            <guid isPermaLink="false">https://medium.com/p/c81946d02b90</guid>
            <category><![CDATA[analytics]]></category>
            <category><![CDATA[professional-development]]></category>
            <category><![CDATA[team]]></category>
            <category><![CDATA[instagram]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Instagram Engineering]]></dc:creator>
            <pubDate>Tue, 24 Sep 2019 15:51:03 GMT</pubDate>
            <atom:updated>2019-09-24T17:39:21.445Z</atom:updated>
            <content:encoded><![CDATA[<p>Tamar is the Head of Analytics at Instagram. She supports a team of data scientists and data engineers responsible for analytics, <a href="https://en.wikipedia.org/wiki/Data_science">data science</a>, and experimentation. Tamar also leads the Diversity and Inclusion task force at Instagram. She is a passionate advocate for women in tech and has served as a mentor for <a href="https://www.mindsmatter.org/">Minds Matter</a> and <a href="https://www.women-unlimited.com/">Women Unlimited</a>, as a speaker for <a href="https://girlswhocode.com/">Girls Who Code</a>, and as co-chair of the Analytics Collective.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*XnYj-RwRD7bYSrXvb41pGQ.jpeg" /><figcaption>Tamar Shapiro, Instagram’s Head of Analytics</figcaption></figure><p><em>This interview was conducted by Serena, an Instagram engineering manager.</em></p><p><strong>How did you become an engineer?</strong></p><p>When I was in college, the field of Data Science did not yet exist. I studied Statistics &amp; Biometry in undergrad. Later on, I went to graduate school for Engineering.</p><p><strong>What was your first coding language?</strong></p><p>I used Java when I took my first computer science class, but most of my math and statistics classes required SAS, SPSS, and MATLAB.</p><p><strong>What do you listen to while you work?</strong></p><p>Believe it or not, I don’t listen to music at all while working. I think it’s distracting. Plus, these days I’m not at my desk long enough to listen to music anyway.</p><p><strong>What do you do when you get stuck on a problem?</strong></p><p>I like to talk it out with people. Discussing the problem out loud helps me think about the problem differently, and it helps to hear from other people who either have encountered a similar problem before or may look at things from a different perspective.</p><p><strong>What do you do when you feel a lot of pressure?</strong></p><p>I usually like to take a break. If I have time, I’ll take a walk around the NY building’s floors or our office’s amazing rooftop garden. If I don’t have time, I just take a beat — I take some deep breaths to clear my head, and that can be very helpful.</p><p><strong>Please tell us about your favorite project at IG.</strong></p><p>This is like being asked to pick your favorite child! My recent favorite is the private like counts test, a product exploration aimed at shifting the focus from the quantity of interactions to the quality of interactions on your feed posts. What I really love about it is that we are challenging the status quo. We are thinking differently about a part of the product that has been a vital part of the Instagram experience from the beginning.</p><p><strong>What makes working at Instagram unique?</strong></p><p>After two years here, I can confidently say that it’s our culture. One of our values is “people first” — meaning we’re always laser focused on how to create the best experience for our community. We approach every project through the lens of the people who use Instagram.</p><p><strong>How would you describe the engineering culture at Instagram?</strong></p><p>Very collaborative. We put the Instagram community first, and we work together to create the best products. Everyone here is so passionate about building amazing products for our community, and it’s so energizing to be a part of it.</p><p><strong>What makes you excited about coming into work every day?</strong></p><p>Working on a product that impacts the lives of billions of people throughout the world. It’s incredibly exciting. It’s also amazing to think about the potential impact we can have with every new idea and project we work on. It’s a huge responsibility, and one that we and I take very seriously.</p><p><strong>Your favorite place to eat in the city?</strong></p><p>I love sushi. Sushi of Gari is my favorite neighborhood sushi place in New York.</p><p><strong>What is your favorite thing to eat at the office?</strong></p><p>A smoothie from Frozen Palm, our smoothie bar in the New York office, which provides a lot of great (and healthy!) options.</p><p><strong>What’s your favorite Instagram account?</strong></p><p>@<a href="https://www.instagram.com/GirlsWhoCode/">girlswhocode</a>, which is an organization that I volunteer for. They work on (and post about) important research that’s relevant to women in tech, and their ultimate goal is to help young women find career paths in STEM. I like to follow along to see what programs they’re working on and to hear stories about some of the alumni.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/960/1*y2IgyGOkjlF7fByq-dqFlw.jpeg" /></figure><p><strong>Tell us about your happiest day at Instagram</strong></p><p>It was my second Faceversary (second-year mark at Facebook) and my team pulled out all the stops with flowers, cupcakes, etc. My manager, Adam Mosseri, was out of the office, but Nam, our head of engineering, surprised me at a meeting by printing Adam’s face on a piece of paper. We took a selfie with the cutout of Adam’s face, pretending he was there and celebrating with us. It was hilarious, but also very special for me because it was a great way to reflect back on all that’s happened over the past couple of years here.</p><p><strong>What is one of the best things you learned while working at Instagram</strong></p><p>The importance of good communication and context sharing. We work in such a fast-paced environment that often we assume people have the context they need to do their jobs well. Sometimes, it’s important to slow down and take the time to communicate the bigger picture.</p><p><strong>Coolest celebrity sighting at the office?</strong></p><p>The cast of <a href="https://en.wikipedia.org/wiki/Crazy_Rich_Asians_(film)">Crazy Rich Asians</a> visited the office, and many people were excited to see them in real life. We welcomed Henry Golding, Michelle Yeoh, Awkwafina, Ken Jeong, and book author Kevin Kwan for a <a href="https://www.facebook.com/CrazyRichAsiansMovie/videos/235251237324338/">FB Live</a> from our New York office where they answered fan questions, and @evachen went Live with the cast from her Instagram account for a Q&amp;A ahead of the film’s release. If you haven’t seen the movie, you’re missing out :)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RxTEPKyt4g0x1OBiE2-Lng.jpeg" /><figcaption>Tamar’s desk</figcaption></figure><p><strong>What does your desk setup look like?</strong></p><p>It’s super clean. I am a neat freak and a minimalist too. I think a clean space helps me keep my focus. But it’s hard to keep a clean desk here because there’s always some new piece of swag that manages to make its way onto my desk.</p><p><strong>What career advice would you like to give to female engineers who are early in their careers?</strong></p><p>So many — but one I’d like to touch on here is the importance of networking. It’s easy to think that we need to just sit at our desks and do really good work, but that’s only one part of the equation.</p><p>It’s important to build relationships with people who are not in your immediate team, or company. Networks can open up opportunities for you in the future, and it’s also important to communicate your work with a broader audience. As women, I don’t think we advocate enough for ourselves, and we need to do a better job communicating our accomplishments.</p><p>Another is confidence. Women, especially those early in their careers, are often prone to self-doubt. It ends up being our biggest enemy as we second-guess ourselves too much. It prevents us from pursuing opportunities to be challenged and grow. How can we overcome it? For me, what really worked was the “fake it until you make it” mindset. Observe the behavior of your confident colleagues and try to mirror it. On this topic, I really recommend <a href="https://www.theatlantic.com/magazine/archive/2014/05/the-confidence-gap/359815/"><em>The Confidence Gap</em></a><em>.</em></p><p><strong>How do you achieve work-life balance? Do you believe in work-life balance at all? :)</strong></p><p>I do! It’s all about setting boundaries and not apologizing for these boundaries. We need to be very diligent about where we draw that line. For me, this might mean that I leave the office by a certain time so I can have quality time with my kids in the evening, and for others it might be something else. The key is sticking to these boundaries and carving out the time that you need to achieve that balance.<br> <br><strong>Do you think work-life balance is harder to achieve as you become more and more senior in the company?</strong></p><p>I don’t think so. The demand definitely becomes greater, but you become better at ruthless prioritization.</p><p><strong>Most of the time, I don’t feel hindered by being a female engineer as I don’t have a problem speaking up or leaning in. But there are still times — for example, when I am pregnant — I worry that people will think less of me, and I worry whether I will still be able to meet my colleagues’ expectations. How can we overcome these thoughts?</strong></p><p>It’s a really great question and one that goes back to the confidence piece I mentioned earlier. Creating a human being is something to be very, very proud of — it’s amazing!</p><p>We need to think about success in our lives in a broader way than just our work. You are building a family, you are a talented engineer, and all of these things are what make you you.</p><p>I find that concerns around performance during a pregnancy are often a manifestation of our own self-doubt. While you might not feel as good or sharp as you’re used to, others are probably unaware, and you are your own worst critic. Everyone has good days and bad days, when they’re feeling distracted or not performing their best. We need to be kind to ourselves and be confident in our abilities.</p><p><em>If you want to learn more about this work or are interested joining one of our engineering teams, please visit our </em><a href="https://www.facebook.com/careers/jobs/?q=instagram"><em>careers page</em></a><em>, follow us </em><a href="https://www.facebook.com/instagramengineering/"><em>on Facebook</em></a><em> or </em><a href="https://twitter.com/instagrameng"><em>on Twitter</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c81946d02b90" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instagram-engineering/interview-with-tamar-shapiro-instagrams-head-of-analytics-c81946d02b90">Interview with Tamar Shapiro, Instagram’s Head of Analytics</a> was originally published in <a href="https://medium.com/instagram-engineering">Instagram Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From FBU to SWE — My Journey of Proving Myself to Myself and How Instagram Helped Me Do It]]></title>
            <link>https://medium.com/instagram-engineering/from-fbu-to-swe-my-journey-of-proving-myself-to-myself-and-how-instagram-helped-me-do-it-ea4a531a4a57?source=rss-a4c6efa67fe0------2</link>
            <guid isPermaLink="false">https://medium.com/p/ea4a531a4a57</guid>
            <category><![CDATA[impostor-syndrome]]></category>
            <category><![CDATA[growth-mindset]]></category>
            <category><![CDATA[instagram]]></category>
            <category><![CDATA[internships]]></category>
            <category><![CDATA[team]]></category>
            <dc:creator><![CDATA[Instagram Engineering]]></dc:creator>
            <pubDate>Fri, 23 Aug 2019 18:52:01 GMT</pubDate>
            <atom:updated>2019-08-23T20:51:27.718Z</atom:updated>
            <content:encoded><![CDATA[<h3>My Journey of Proving Myself to Myself and How Instagram Helped Me Do It</h3><p><em>Written by Amy Huyen, 2-time intern at Instagram.</em><br> <br>On my first day of intern orientation last summer, I walked into Facebook wide-eyed and in awe of all the talent that surrounded me. I saw my peers networking with one another, discussing past internship experiences, and using technical jargon that I couldn’t understand. I felt like an impostor.<br> <br>I was an inexperienced freshman who had only been coding for 10 months. I had no industry experience, was only versed in Python, and couldn’t even <em>begin</em> to comprehend what tech at a big company was like. Unlike most software engineering interns, I had been admitted into Facebook’s freshmen-sophomore <a href="https://www.facebook.com/careers/students-and-grads/students">Facebook University program</a> (“FBU”). Instead of working directly on company products, FBU interns spend three weeks in an intensive mobile development bootcamp, then five weeks developing their own mobile apps in teams of three. <br> <br>I should’ve been ecstatic to be surrounded by such brilliant individuals, but instead, I felt guilty. <strong>Like I had stolen someone’s spot — someone more qualified and more experienced than me.</strong><br> <br>I spent the entirety of my 8-week internship battling these feelings of incompetence and fear. I worried that other interns would look at me and think, “How did she scheme her way into this company?” or “How did they let someone like <em>her</em> into the company?” Nevertheless, I shut these thoughts away and focused on creating the best Android app I could with my team. At the end of my internship, I walked away with an app that matched skilled volunteers to non-profit organizations, a return software engineering internship offer, and memories that would last me a lifetime.<br> <br>However, the <a href="https://en.wikipedia.org/wiki/Impostor_syndrome">impostor syndrome</a> lingered. Now, more than ever before, I felt like I had to prove to my peers at school that my SWE internship offer was legitimate — that I had earned it because of my abilities, not because of my gender. I had to prove to the world that I was truly qualified for the role I had been offered. Moreover, I had to prove to <em>myself</em> that I was enough.</p><h3>Coming back</h3><p>I accepted my return offer with the determination to prove others wrong. In May of this year, I started my second internship at Facebook. I would work on the Instagram Growth team as an iOS intern, which was daunting because, for the second summer in a row, I was working on something completely foreign to me.<br> <br>During the weeks leading up to my internship, I worried that my decision to pursue an iOS role in the company was a mistake. I’d never coded in <a href="https://en.wikipedia.org/wiki/Objective-C">Objective-C</a> before and was barely more experienced than I was last year. The imposter syndrome crept back in, and it was <em>paralyzing</em>.<br> <br>Fast forward 12 weeks, and now I’m at the end of my internship here. I’m a different, more confident version of myself. This summer, I spearheaded Instagram’s invites project for empty state surfaces on iOS, developed lasting relationships with the members of my team, and created real impact in the company.<br> <br>I learned about industry coding standards and workflows, immersed myself in Facebook’s culture. First, a quick summary of my work, and then the lessons I learned.</p><h3>What did I build?</h3><p>My work this summer involved adding an invite unit to Direct and Search’s empty state using <a href="https://github.com/Instagram/IGListKit">Instagram’s IGListKit framework</a>. In Direct, I introduced two different user experiences: 1) adding an invite card to the end of the existing collection of activators, and 2) converting all the cards into rows to increase visibility of each of the calls to action:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mQNomR0jt2IH2UaC4gWByg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fNNsauyyC0tWdcpTNCXHVQ.png" /></figure><p>I also surfaced an invite row in Search’s empty state. When a user searches for an account that doesn’t exist, they are prompted to invite that friend to Instagram:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OvR4FWppl6X-oXPIACpEIw.png" /></figure><h3>What did I learn?</h3><h3>1) Be unapologetic</h3><p>I used to apologize profusely for mistakes in my code or for “wasting” others’ time with my stupid questions. However, apologies fall flat when you keep repeating the same mistakes. <strong>Stop apologizing and start acting on feedback instead</strong>. <br> <br>As I was building the invite unit to show to Instagram users in various empty state surfaces across the app, I encountered numerous challenges. My code quality was far from perfect, and my code reviewers often pointed out different areas where I could improve. During my first couple of weeks, I would apologize constantly for these nits, but I soon realized that words are just empty promises if they aren’t backed by actions. <br> <br>The best way to thank your peers for their time and patience is by <em>showing</em> them your progress. Stop apologizing and, instead, start proving to your peers and managers that their time wasn’t wasted on you.</p><h3>2) Ask questions — but care enough to listen to the answers</h3><p>During my first few weeks, the thought of asking another engineer for help terrified me. Fear of judgement crippled me from the start, and the thought of being considered “annoying” held me back even more. I was so obsessed over the idea that everyone would be too busy to entertain me that I found myself writing, deleting, and rewriting messages to my manager when I needed help. I wasted so many hours of my life drafting these messages only to never send them out.<br> <br>Ultimately, I was so fixated on my own perception of myself that I never even considered how other engineers truly saw me. <br> <br>It wasn’t until I finally mustered up the courage to ask an iOS engineer on another team for advice that I realized just how eager everyone at Instagram is to help. Never did they give off the impression that I was a nuisance to them or interfering with their everyday work. In fact, they welcomed these opportunities to understand my project and get to know me as a coworker. From that day forward, I stopped drafting and redrafting my messages to my peers and manager. Instead, I just hit send fearlessly because I realized my time was just as valuable as theirs. <br> <br>However, another crucial thing I learned from asking questions is that asking questions isn’t enough. <strong>Listening is the most important part</strong>. As humans, we often prepare ourselves to respond while others are still speaking. We don’t take the time to acknowledge others’ words because we’re too adamant that our solutions and thoughts are the only ones that can be right. <br> <br>That’s an awful way of thinking. Not every conversation is a battle, and not every one of your solutions is the most optimal. The moment I decided to drop that mindset and truly <em>listen</em> to the words of my peers, I learned more than I ever could’ve imagined. So yes, ask your questions, but listen to those answers too.</p><h3>3) Expand your comfort zone instead of stepping out of it</h3><p>From the time I was young, I’ve been told to step out of my comfort zone, but it’s never spurred me into action. No one willingly throws themselves into uncomfortable situations, and I’m no different. For the first 8 weeks of my internship, I focused primarily on iOS client-side development. Although I had no previous experience in this area, I slowly but surely grew familiar with the codebase, and gained confidence in my abilities.<br> <br>However, when my manager Vivian encouraged me to try some server-side work towards the end of my internship, I was intimidated and hesitant to accept. A part of me was fearful of the new learning curve that I’d have to overcome again, this time during my ninth week, when I <em>theoretically </em>should be the most productive. I grew uncertain of my abilities again and was certain that I’d fail and disappoint my team.<br> <br>As it turns out, working on server code wasn’t all that different from learning the client code. As I did before in my first weeks, I analyzed the existing codebase, contacted other engineers at Instagram for assistance, and threw myself headfirst into the new project. Soon, without realizing it, the server-side code became a part of my comfort zone.<br> <br>What I’ve discovered is that adopting the mindset of <strong>conquering new obstacles instead of abandoning your existing accomplishments</strong> has done wonders in helping me tackle new challenges. If we all slowly chip away at the unknown one piece at a time, we can and will prevail. (Even writing this blog post required a lot of courage from me, as I am by no means a professional.)</p><h3>4) Prove your own worth to yourself first</h3><p>Perhaps my biggest takeaway from my internship at Instagram is that I <em>am</em> worthy of my position here. From working on the Growth team, I’ve gained a sense for how Instagram products are conceived, built, and delivered. I’ve developed a deep appreciation for all the steps that go into the delivery of a new feature onto our users’ screens. I’ve gained full stack development experience, had measurable impact on the product, and collaborated with a variety of professionals: designers, data scientists, product managers, and more. I’ve taken ownership of a feature and watched as this idea evolved from a design on my computer screen to a fully functioning feature on users’ devices.<br> <br> None of this would’ve been possible if I didn’t deserve my position here. None of this would’ve been possible if I had only gotten into Facebook because I was a girl. None of this would’ve been possible if I didn’t show my team that I had what it took to succeed as an intern here.<br> <br> So to all the hopeful interns out there who are confronting the same struggles as I did: <strong>stop trying to prove yourself to others when you haven’t even proven yourself to <em>you</em></strong>. Step boldly into your internships and know your own worth because once you silence that nagging voice of doubt inside your head, everything else will fall into place.<br> <br> And if you can’t quite do that just yet, take a summer to unapologetically expand your comfort zone and surround yourself with people who celebrate your successes with you as I did this summer at Instagram. You’ll be amazed how much you’ll accomplish when you stop doubting your own worth and start believing the people who believe in you.</p><h3>Thank you!</h3><p>Ultimately, I’ve grown so much over the course of these past twelve weeks, both technically and personally. Thanks to everyone on the Instagram Growth team for making my summer here an unforgettable one, and special thanks to Vivian Yang and Serena Jiang for their unwavering support. You’ve all helped me believe in myself, and for that I’m eternally grateful. <br> <br> Amy Huyen was a summer 2019 intern at Instagram. If you want to learn more about this work or are interested in joining one of our engineering teams, please visit our <a href="https://www.facebook.com/careers/jobs/?q=instagram">careers page</a>, or follow us <a href="https://www.facebook.com/instagramengineering/">on Facebook</a> or <a href="https://twitter.com/instagrameng">on Twitter</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ea4a531a4a57" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instagram-engineering/from-fbu-to-swe-my-journey-of-proving-myself-to-myself-and-how-instagram-helped-me-do-it-ea4a531a4a57">From FBU to SWE — My Journey of Proving Myself to Myself and How Instagram Helped Me Do It</a> was originally published in <a href="https://medium.com/instagram-engineering">Instagram Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ San Francisco!]]></title>
            <link>https://medium.com/instagram-engineering/san-francisco-856cbf69397?source=rss-a4c6efa67fe0------2</link>
            <guid isPermaLink="false">https://medium.com/p/856cbf69397</guid>
            <category><![CDATA[san-francisco]]></category>
            <dc:creator><![CDATA[Instagram Engineering]]></dc:creator>
            <pubDate>Thu, 17 May 2018 14:28:39 GMT</pubDate>
            <atom:updated>2018-05-17T14:57:00.965Z</atom:updated>
            <content:encoded><![CDATA[<p>Today we’re excited to announce that Instagram is opening our third full-stack office in San Francisco.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DcHVoC0cXpaXAoI5sxctqg.jpeg" /></figure><p>Kevin Systrom and Mike Krieger founded Instagram in San Francisco in 2010, and now — over seven years and 800 million Instagrammers later — we’re thrilled to be back. The Bay Area has incredible talent, which has helped us create a great and growing team in Menlo Park. Now that Instagram has returned to San Francisco, we’re excited to tap into more talent from across the region. <br> <br>To start, the San Francisco office will focus on product development from the Creation and Communication team, whose goal is to inspire people to share what matters to them. This is the team behind some of Instagram’s most impactful products, including Stories, Live, Direct, and Profile. Some of the <a href="https://instagram-press.com/blog/2018/05/01/new-ways-to-share-and-connect-on-instagram/">new work we announced at F8</a>, including Camera Effects, Sharing to Stories, and video chat, will be based in San Francisco.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2yid6avRIdhK2TVXXj-ZdA.png" /></figure><p>To build these teams, we recruit diverse talent across engineering, product management, design, research, data engineering, and analytics. Our engineering teams are full-stack, and span iOS, Android, and server. Our development teams include graphics experts, and mobile and server infrastructure specialists who build robust systems to power our products at scale, like Live and Direct. The teams in San Francisco are responsible for some of Instagram’s biggest bets, including low-latency video, camera and AR effects, and other rich new user experiences.<br> <br>Our San Francisco office will continue to expand over the coming months. It’s designed to embody Instagram’s core values, putting community first through art from local and global Instagrammers, inspiring creativity, and simplicity — but with a distinctly San Francisco spin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tqHkKTf8LSnxP0sDfwVPUQ.jpeg" /></figure><p>Instagram has always operated with a lean team, and we’ve taken a careful approach to growth. Opening a new engineering hub in San Francisco is a big step and something we took very seriously. We know that successful new offices need top, full-stack talent tackling big projects. For example, in our New York City hub, which was established in 2015, we colocated the product and infrastructure teams behind experiences like Home, Discovery, and Shopping, and have since shipped some of Instagram’s most impactful features. Now that we’re in San Francisco, continued collaboration across offices will be more important — and exciting — than ever.<br> <br> I’m <a href="https://www.instagram.com/p/BifHYivBX0I/">personally incredibly excited</a> to be growing our team in San Francisco. I grew up in the city and have seen it change so much over the years. It’s part of Instagram’s history too, and it’s great to see us take part in the vibrant local community, tap into its creative talent, and be a part of the ever-changing city landscape. <br> <br> If you want to build the next generation of amazing products at Instagram San Francisco, please <a href="https://www.instagram.com/about/jobs/">join us</a>! <br> <br> <em>Eddie Ruvinsky is the Engineering Director with the Creation and Communication teams at Instagram San Francisco.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=856cbf69397" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instagram-engineering/san-francisco-856cbf69397">👋 San Francisco!</a> was originally published in <a href="https://medium.com/instagram-engineering">Instagram Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Instant Feedback in iOS Engineering Workflows]]></title>
            <link>https://medium.com/instagram-engineering/instant-feedback-in-ios-engineering-workflows-c3f6508c76c8?source=rss-a4c6efa67fe0------2</link>
            <guid isPermaLink="false">https://medium.com/p/c3f6508c76c8</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Instagram Engineering]]></dc:creator>
            <pubDate>Mon, 23 Apr 2018 18:17:41 GMT</pubDate>
            <atom:updated>2018-04-23T18:25:30.047Z</atom:updated>
            <content:encoded><![CDATA[<iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F266169191%3Fapp_id%3D122963&amp;dntp=1&amp;url=https%3A%2F%2Fvimeo.com%2F266169191&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F696358517_1280.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="1920" height="1168" frameborder="0" scrolling="no"><a href="https://medium.com/media/3fc288abab0d15dc2515d47fb88568e6/href">https://medium.com/media/3fc288abab0d15dc2515d47fb88568e6/href</a></iframe><p>At Instagram and Facebook, we’ve seen how adopting React Native in product development has allowed our engineers to move and iterate faster on products. Viewing the effects of our changes is now just a single tap of ⌘R away! But working with native code doesn’t provide the same experience. Instead, engineers need to recompile, link, install, and launch the application they’re developing every time they want to see the results of their changes.</p><p>Even on a small application, that process drops people out of their workflow. And at our scale, the result is that engineers spend a significant amount of time waiting for their computers to complete work, and often end up context-switching away to other applications, losing even more time.</p><p>Late last year we set out to bring the advantages of React Native’s developer experience to engineers working on our native iOS codebase, written in Objective-C, with the goal of allowing engineers to see their changes in seconds. To achieve this, we skip most of the steps necessary to restart the application by loading the new code while it’s still running.</p><h3>How Native Code Reloading Works</h3><p>To reload native code, the first step is to turn the modified code into something that we can load into a running application: a dynamic library. This operates on a file-level granularity: we compile only the files that have changed, then link them into a dynamic library. This is very fast — engineers using the tool are primarily iterating on a single file.</p><p>An iOS device won’t permit an application to load dynamic libraries from outside of its bundle. Additionally, it won’t allow an application to write to its own bundle. Therefore, all loadable binaries must have been in the application’s bundle when it was installed, and loading our new library is impossible.</p><p>Fortunately, neither of these limitations apply to the iOS simulator. Each simulator’s filesystem is simply a directory in host Mac’s filesystem. Additionally, the dlopen function in the iOS simulator doesn&#39;t filter by file location, which makes the ability to write directly to the application&#39;s bundle irrelevant anyway.</p><p>Within the application code, we’ve added a startup function that stars a small, debug-only background task that watches a specific directory. After creating the dynamic library, the tool locates it and moves it into that directory. This triggers a callback in the application, which takes these steps:</p><ul><li>Load the new dynamic library.</li><li>Determine which Objective-C classes have been added.</li><li>Find the original versions of these classes. Objective-C classes are themselves objects, and they’re easy to look up by name.</li><li>Use the Objective-C runtime APIs to replace the implementations of methods on the original versions of the classes with the new implementations. This applies to <em>all</em> methods of the class, as we don’t have any way to determine which have changed and which haven’t.</li><li>Write a response back to the same directory for the tool to read.</li></ul><p>Our new code is now running in the application, and will be called whenever one of these methods is next invoked. Of course, this is only available while in development with the iOS simulator, not on the device or in the production app.</p><h3>Building a Developer Experience</h3><p>Reinstalling and restarting an application to see new changes isn’t fast, but it’s simple and reliable. Click a button or press a keyboard shortcut in Xcode, and eventually, the results of your code will appear on-screen. We match this in our tooling: we place a button in Xcode’s toolbar, and add a keyboard shortcut to trigger the loading of new code.</p><p>We have an another advantage over full restarts: we can keep all of the current application state. A restart will reset the application back to its entry point — for our apps, that’s generally a feed. Engineers working on other products will have to click their way back to their products’ views before they can see if their changes worked correctly.</p><p>However, after new code has been loaded, nothing happens: all of the previous state that has been built up is unaffected by the engineer’s changes. Fortunately, <a href="https://componentkit.org/">ComponentKit</a>, a declarative iOS UI framework used at Facebook, has a useful method for performing an instant relayout: <a href="https://github.com/facebook/componentkit/blob/b16daea3265f4eaf933a1f50d4188a2df2b7d74c/ComponentKit/Debug/CKComponentDebugController.h#L42">+[CKComponentDebugController reflowComponents]</a>. Views built with ComponentKit can invoke it after new code is loaded, causing their new layout code to be evaluated and the on-screen views to update immediately. The result is that when working on UI code written with ComponentKit, engineers can make changes to their view layouts, and see them within seconds. Previously, engineers would need to batch their updates and think about when they wanted a full restart. They can now work interactively and iteratively.</p><h3>Conclusion</h3><p>Our tool has become popular with our teams here, especially among engineers working with ComponentKit. In larger apps, it has allowed engineers to see the results of their changes up to twenty times faster. The combination of fast performance and tight integration with existing developer tools and frameworks has made native code reloading an important part of many iOS engineers’ workflows here at Instagram and Facebook.</p><p><em>Nate Stedman is an iOS engineer at Instagram New York.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c3f6508c76c8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instagram-engineering/instant-feedback-in-ios-engineering-workflows-c3f6508c76c8">Instant Feedback in iOS Engineering Workflows</a> was originally published in <a href="https://medium.com/instagram-engineering">Instagram Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building Type Mode for Stories on iOS and Android]]></title>
            <link>https://medium.com/instagram-engineering/building-type-mode-for-stories-on-ios-and-android-8804e927feba?source=rss-a4c6efa67fe0------2</link>
            <guid isPermaLink="false">https://medium.com/p/8804e927feba</guid>
            <category><![CDATA[ui]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[ui-design]]></category>
            <dc:creator><![CDATA[Instagram Engineering]]></dc:creator>
            <pubDate>Thu, 12 Apr 2018 17:39:35 GMT</pubDate>
            <atom:updated>2018-04-14T16:12:39.032Z</atom:updated>
            <content:encoded><![CDATA[<p>Instagram recently launched <a href="https://instagram-press.com/blog/2018/02/01/introducing-type-mode-in-stories/">Type Mode</a>, a new way to post creative, dynamic text styles and backgrounds to Stories. Type Mode was an interesting challenge for us because it is the first time we were going to create a way for people to post Stories without photo or video components — and we wanted to make sure Type Mode was still a fun, customizable and visually expressive experience.</p><p>Making Type Mode function seamlessly on both iOS and Android had its own set of challenges, including dynamically resizing text and custom background fills. In this post we’ll take a look at how we approached this work on both iOS and Android platforms.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*B_eL2GjOQGhd_OxC3nEXKA.jpeg" /></figure><h4>Dynamically Resizing Text Input</h4><p>With Type Mode, we wanted to create a text input experience that let people emphasize certain words or phrases. One way to do that was to build fully justified text styles that dynamically resize each line to fill the available width (used in Modern, Neon, and Strong).</p><p><strong>iOS</strong></p><p>The main challenge on iOS was to render dynamically resizing text in a native UITextView, which would let people enter text in a quick and familiar way.</p><p><strong>RESIZING PRE-TEXT STORAGE COMMIT</strong></p><p>While you input text on a line, the size of text on that line should scale down until it hits some minimum font size.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/764/1*Chw3Adea66Me49A2wPGR-g.gif" /></figure><p>To accomplish this, we use a combination of UITextView.typingAttributes, NSAttributedString, and NSLayoutManager.<br> <br>First, we need to calculate what font size our text will be rendered as. We can grab the range of the line we are currently typing on using [NSLayoutManager enumerateLineFragmentsForGlyphRange:usingBlock:]. From that range we can create a sizing string to calculate a minimum font size.</p><pre>CGFloat pointSize = 24.0; // arbitrary<br>NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:string attributes:@{NSFontAttributeName:[UIFont fontWithName:fontName size:pointSize]}];<br>CGFloat textWidth = CGRectGetWidth([attributedString boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NULL context:nil]);<br>CGFloat scaleFactor = (textViewContainerWidth / textWidth);<br>CGFloat preferredFontSize = (pointSize * scaleFactor);<br>return CLAMP_MIN_MAX(preferredFontSize, minimumFontSize, maximumFontSize) // Clamps value between min and max</pre><p>In order to actually draw the text at the correct size, we need to use our new font size in our UITextView&#39;s typingAttributes. UITextView.typingAttributes are attributes that apply to new text being typed by the user. A good place for this is [id&lt;UITextViewDelegate&gt; textView:shouldChangeTextInRange:replacementText:].</p><pre>- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {<br>    NSMutableDictionary *typingAttributes = [textView.typingAttributes mutableCopy];<br>    typingAttributes[NSFontAttributeName] = [UIFont fontWithDescriptor:fontDescriptor size:calculatedFontSize];<br>    textView.typingAttributes = typingAttributes;<br>    return YES;<br>}</pre><p>This means that as the user types, the font size will shrink until we hit some specified minimum. When this happens, the UITextView will wrap our text as it normally would.</p><p><strong>FINALIZE TEXT POST STORAGE COMMIT</strong></p><p>After our text has been committed to text storage, we may need to clean up some sizing attributes. Our text could have wrapped, or the user could have “emphasized” text by manually adding a line break to write larger text on a separate line.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*DNzHUA7Mo_yYSA4kCnk7TA.png" /></figure><p>A good place to put this logic is in [id&lt;UITextViewDelegate&gt; textViewDidChange:]. This happens after the text has been committed to text storage and initially laid out by the text engine. <br> <br>To get a list of character ranges for each line, we can use NSLayoutManager:</p><pre>NSMutableArray&lt;NSValue *&gt; *lineRanges = [NSMutableArray array];<br>[textView.layoutManager enumerateLineFragmentsForGlyphRange:NSMakeRange(0, layoutManager.numberOfGlyphs) usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) {<br>    NSRange characterRange = [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL];<br>    [lineRanges addObject:[NSValue valueWithRange:characterRange]];<br>}];</pre><p>We then need to manipulate the NSTextStorage by setting attributes on ranges that have the correct font size for each row.<br> <br>There are three stages to editing NSTextStorage, which is itself a subclass of NSMutableAttributedString.</p><ol><li>Call [textStorage beginEditing] to indicate we are making one or more changes to the text storage.</li><li>Send some editing messages to NSTextStorage. In our case, the NSFontAttributeName attribute should be set with the correct font size for that row. We can use a similar method for calculating the font size as we did earlier.</li></ol><pre>for (NSValue *lineRangeValue in lineRanges) {<br>    NSRange lineRange = lineRangeValue.rangeValue;<br>    const CGFloat fontSize = ... // Same font size calculation from earlier<br>    [textStorage setAttributes:@{NSFontAttributeName : [UIFont fontWithDescriptor:fontDescriptor size:fontSize]} range:lineRange];<br>}</pre><p>3. Call [textStorage endEditing] to indicate we are done editing text storage. This invokes the [NSTextStorage processEditing] method which will fix attributes on the ranges we changed. This also invokes the correct NSTextStorageDelegate methods.<br> <br>TextKit is a powerful and modern API that is tightly integrated with UIKit. Many text experiences can be designed with it, and new text APIs are being released in most new iOS versions. With TextKit, you can do anything from creating custom text containers to modifying the actual generation of glyphs. And since it is built on top of CoreText and integrated with APIs like UITextView, text input and editing continues to feel like a native iOS experience.</p><h4>Android</h4><p>Android doesn’t support full width justification out of the box, but the framework APIs give us all the tools we need to implement it ourselves.<br> <br>The first step is to lay the text out at the minimum text size. We’ll scale things up later, but this will tell us how many lines we have and where the line breaks are:</p><pre>TextPaint textPaint = new TextPaint();<br>textPaint.setTextSize(SIZE_MIN);<br>Layout layout =<br>    new StaticLayout(<br>        text,<br>        textPaint,<br>        availableWidth,<br>        Layout.Alignment.ALIGN_CENTER,<br>        1 /* spacingMult */,<br>        0 /* spacingAdd */,<br>        true /*includePad */);<br>int lineCount = layout.getLineCount();</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*rKHCLpYSf-VZ_2yhyqzZCQ.png" /></figure><p>Next we need to go through the layout and resize each line individually. There’s no direct way to find the perfect text size for a particular line, but we can easily approximate it with a binary search for the largest text size that doesn’t force a line break:</p><pre>int lowSize = SIZE_MIN;<br>int highSize = SIZE_MAX;<br>int currentSize = lowSize + (int) Math.floor((highSize - lowSize) / 2f);<br>while (low &lt; current) {<br>  if (hasLineBreak(text, currentSize)) {<br>    highSize = currentSize;<br>  } else {<br>    lowSize = currentSize;<br>  }<br>  currentSize = lowSize + (int) Math.floor((highSize - lowSize) / 2f);<br>}</pre><p>Once we’ve found the right size for each line, we apply it as a span. Spans let us use different text sizes for each line instead of a single text size for the entire string:</p><pre>text.setSpan(<br>    new AbsoluteSizeSpan(textSize),<br>    layout.getLineStart(lineNumber),<br>    layout.getLineEnd(lineNumber),<br>    Spanned.SPAN_INCLUSIVE_EXCLUSIVE);</pre><p>Now each line of text fills the available width! We can repeat this process each time the text changes to get the dynamic resizing behavior we’re looking for.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*zVc-ioRas9b8TRmhrESIHg.png" /></figure><h3>Custom Backgrounds</h3><p>We also wanted Type Mode to let people emphasize words and phrases with text backgrounds (used in Typewriter and Strong).</p><h4>iOS</h4><p>Another way we can leverage NSLayoutManager is for drawing custom background fills. NSAttributedString does have a NSBackgroundColorAttributeName, but it is not customizable nor extensible.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*0oPlID5rtrmqtHRUZdbIkQ.png" /></figure><p>For example, if we used NSBackgroundColorAttributeName, the background of the entire text view would be filled in. We could not exclude whitespace, have spaces between the lines, or draw the fill with a corner radius. Thankfully, NSLayoutManager lets us override drawing the background fill. We need to create a NSLayoutManager subclass and override drawBackgroundForGlyphRange:atPoint:</p><pre>@interface IGSomeCustomLayoutManager : NSLayoutManager<br>@end </pre><pre>@implementation IGSomeCustomLayoutManager</pre><pre>- (void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origin {<br>    // Draw custom background fill</pre><pre>    [super drawBackgroundForGlyphRange:glyphsToShow atPoint:origin];<br>}<br>    <br>}];<br>@end</pre><p>With our drawBackgroundForGlyphRange:atPoint method, we can once again leverage [NSLayoutManager enumerateLineFragmentsForGlyphRange:usingBlock] to grab the glyph ranges of each line fragment. We can then use [NSLayoutManager boundingRectForGlyphRange:inTextContainer] to get the bounding rectangle of each line.</p><pre>- (void)drawBackgroundForGlyphRange:(NSRange)glyphsToShow atPoint:(CGPoint)origin {<br>  [self enumerateLineFragmentsForGlyphRange:NSMakeRange(0, self.numberOfGlyphs) usingBlock:^(CGRect rect, CGRect usedRect, NSTextContainer * _Nonnull textContainer, NSRange glyphRange, BOOL * _Nonnull stop) {<br>       CGRect lineBoundingRect = [self boundingRectForGlyphRange:glyphRange inTextContainer:textContainer];<br>       CGRect adjustedLineRect = CGRectOffset(lineBoundingRect, origin.x + kSomePadding, origin.y + kSomePadding);<br>       UIBezierPath *fillColorPath = [UIBezierPath bezierPathWithRoundedRect:adjustedLineRect cornerRadius:kSomeCornerRadius];<br>       [[UIColor redColor] setFill];<br>       [fillColorPath fill];<br>  }];<br>}</pre><p>This allows us to draw a background fill around any arbitrary text with our own specified shapes and padding. NSLayoutManager can also be utilized to draw other text attributes like strikethroughs and underlines.</p><p><strong>Android</strong></p><p>At first glance, it feels like this should be simple to implement on Android. We can add a span that modifies the text background color and be done:</p><pre>new CharacterStyle() {<br>  @Override<br>  public void updateDrawState(TextPaint textPaint) {<br>    textPaint.bgColor = color;<br>  }<br>}</pre><p>That’s a good first attempt (and was the first thing we built), but it comes with a few limitations:</p><ol><li>The background tightly wraps the text and there’s no way to adjust the padding.</li><li>The background is rectangular and there’s no way to adjust the corner radius.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*o6uBmTEniyyrNh5qWgCv_Q.png" /></figure><p>To address those issues, we tried using LineBackgroundSpan. We were already using it to render the rounded bubble background on Classic text, so it seemed like a natural fit for the new text styles as well. Unfortunately our new use case uncovered a subtle bug in the framework Layout class. If your text has multiple LineBackgroundSpan instances on different lines, then Layout will not iterate through them properly and some of them may never be rendered.</p><p>Thankfully we can sidestep the framework bug by applying a single LineBackgroundSpan to the entire string and then delegating to individual background spans ourselves:</p><pre>class BackgroundCoordinator implements LineBackgroundSpan {<br>  @Override<br>  public void drawBackground(<br>      Canvas canvas,<br>      Paint paint,<br>      int left,<br>      int right,<br>      int top,<br>      int baseline,<br>      int bottom,<br>      CharSequence text,<br>      int start,<br>      int end,<br>      int currentLine) {<br>    Spanned spanned = (Spanned) text;<br>    for (BackgroundSpan span : spanned.getSpans(start, end, BackgroundSpan.class)) {<br>      span.draw(canvas, spanned);<br>    }<br>  }<br>}<br><br>class BackgroundSpan {<br>  public void draw(Canvas canvas, Spanned spanned) {<br>    // Custom background rendering...<br>  }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/360/1*J3cTb7oZpyE4jukQi0_mfA.png" /></figure><h4>Conclusion</h4><p>Instagram has a very strong prototyping culture, and the design team’s Type Mode prototypes let us get a real feel for the user experience with each iteration along the way. For example, with the Neon style, we needed a way to take a single color from our palette and then generate an interior color and a glow color for the text. A designer on the project played around with some approaches in his prototype, and when he found one he liked we essentially just copied his logic on Android and iOS. This level of collaboration with the design team was a special part of this launch and made the development process really efficient. <br> <br>If you’re interested in working with us on Stories, check out our <a href="https://m.facebook.com/careers/teams/instagram/">careers page</a> for roles in Menlo Park, New York, and San Francisco. <br> <br><em>Christopher Wendel and Patrick Theisen are iOS and Android engineers at Instagram, respectively.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8804e927feba" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instagram-engineering/building-type-mode-for-stories-on-ios-and-android-8804e927feba">Building Type Mode for Stories on iOS and Android</a> was originally published in <a href="https://medium.com/instagram-engineering">Instagram Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Open-sourcing a 10x reduction in Apache Cassandra tail latency]]></title>
            <link>https://medium.com/instagram-engineering/open-sourcing-a-10x-reduction-in-apache-cassandra-tail-latency-d64f86b43589?source=rss-a4c6efa67fe0------2</link>
            <guid isPermaLink="false">https://medium.com/p/d64f86b43589</guid>
            <category><![CDATA[instagram]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[rocksdb]]></category>
            <category><![CDATA[cassandra]]></category>
            <dc:creator><![CDATA[Instagram Engineering]]></dc:creator>
            <pubDate>Mon, 05 Mar 2018 18:03:23 GMT</pubDate>
            <atom:updated>2018-03-05T18:03:23.510Z</atom:updated>
            <content:encoded><![CDATA[<p>At Instagram, we have one of the world’s largest deployments of the Apache Cassandra database. We began using Cassandra in 2012 to replace Redis and support product use cases like fraud detection, Feed, and the Direct inbox. At first we ran Cassandra clusters in an AWS environment, but migrated them over to Facebook’s infrastructure when the rest of Instagram moved. We’ve had a really good experience with the reliability and availability of Cassandra, but saw room for improvement in read latency.<br> <br>Last year Instagram’s Cassandra team started working on a project to reduce Cassandra’s read latency significantly, which we call Rocksandra. In this post, I will describe the motivation for this project, the challenges we overcame, and performance metrics in both internal and public cloud environments.</p><h3>Motivation</h3><p>At Instagram, we use Apache Cassandra heavily as a general key value storage service. The majority of Instagram’s Cassandra requests are online, so in order to provide a reliable and responsive user experience for hundreds of millions of Instagram users, we have very tight SLA on the metrics. <br> <br>Instagram maintains a 5–9s reliability SLA, which means at any given time, the request failure rate should be less than 0.001%. For performance, we actively monitor the throughput and latency of different Cassandra clusters, especially the P99 read latency. <br> <br> Here’s a graph that shows the client-side latency of one production Cassandra cluster. The blue line is the average read latency (5ms) and the orange line is the P99 read latency (in the range of 25ms to 60ms and changing a lot based on client traffic).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Scn1Nm33oukOJpUd4Ukszw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ItBORNwCXce82ZNX6qf6Vg.png" /></figure><p>After investigation, we found the JVM garbage collector (GC) contributed a lot to the latency spikes. We defined a metric called GC stall percentage to measure the percentage of time a Cassandra server was doing stop-the-world GC (Young Gen GC) and could not serve client requests. Here’s another graph that shows the GC stall percentage on our production Cassandra servers. It was 1.25% during the lowest traffic time windows, and could be as high as 2.5% during peak hours.</p><p>The graph shows that a Cassandra server instance could spend 2.5% of runtime on garbage collections instead of serving client requests. The GC overhead obviously had a big impact on our P99 latency, so if we could lower the GC stall percentage, we would be able to reduce our P99 latency significantly.</p><h3>Solution</h3><p>Apache Cassandra is a distributed database with it’s own LSM tree-based storage engine written in Java. We found that the components in the storage engine, like memtable, compaction, read/write path, etc., created a lot of objects in the Java heap and generated a lot of overhead to JVM. To reduce the GC impact from the storage engine, we considered different approaches and ultimately decided to develop a C++ storage engine to replace existing ones. <br> <br>We did not want to build a new storage engine from scratch, so we decided to build the new storage engine on top of RocksDB. <br> <br>RocksDB is an open source, high-performance embedded database for key-value data. It’s written in C++, and provides official API language bindings for C++, C, and Java. RocksDB is optimized for performance, especially on fast storage like SSD. It’s widely used in the industry as the storage engine for MySQL, mongoDB, and other popular databases.</p><h3>Challenges</h3><p>We overcame three main challenges when implementing the new storage engine on RocksDB.<br> <br>The first challenge was that Cassandra does not have a pluggable storage engine architecture yet, which means the existing storage engine is coupled together with other components in the database. To find a balance between massive refactoring and quick iterations, we defined a new storage engine API, including the most common read/write and streaming interfaces. This way we could implement the new storage engine behind the API and inject it into the related code paths inside Cassandra.<br> <br>Secondly, Cassandra supports rich data types and table schema, while RocksDB provides purely key-value interfaces. We carefully defined the encoding/decoding algorithms to support Cassandra’s data model within RocksDB’s data structure and supported same-query semantics as original Cassandra. <br> <br>The third challenge was about streaming. Streaming is an important component for a distributed database like Cassandra. Whenever we join or remove a node from a Cassandra cluster, Cassandra needs to stream data among different nodes to balance the load across the cluster. The existing streaming implementation was based on the details in the current storage engine. Accordingly, we had to decouple them from each other, make an abstraction layer, and re-implement the streaming using RocksDB APIs. For high streaming throughput, we now stream data into temp sst files first, and then use the RocksDB ingest file API to bulk load them into the RocksDB instance at once.</p><h3>Performance metrics</h3><p>After about a year of development and testing, we have finished a first version of the implementation and successfully rolled it into several production Cassandra clusters in Instagram. In one of our production clusters, the P99 read latency dropped from 60ms to 20ms. We also observed that the GC stalls on that cluster dropped from 2.5% to 0.3%, which was a 10X reduction!<br> <br>We also wanted to verify whether Rocksandra would perform well in a public cloud environment. We setup a Cassandra cluster in an AWS environment using three i3.8 xlarge EC2 instances, each with 32 cores CPU, 244GB memory, and raid0 with 4 nvme flash disks. <br> <br>We used <a href="https://github.com/Netflix/ndbench">NDBench</a> for the benchmark, and the default table schema in the framework:</p><blockquote>TABLE emp (<br> emp_uname text PRIMARY KEY,<br>emp_dept text,<br>emp_first text,<br>emp_last text<br> )</blockquote><p>We pre-loaded 250M 6KB rows into the database (each server stores about 500GB data on disk). We configured 128 readers and 128 writers in NDBench.<br> <br>We tested different workloads and measured the avg/P99/P999 read/write latencies. As you can see, Rocksandra provided much lower and consistent tail read/write latency.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Mpvc-jd61xmcrE4aEth4NA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zZO7xeU8fsWosWbkev873g.png" /></figure><p>We also tested a read-only workload and observed that, at similar P99 read latency (2ms), Rocksandra could provide 10X higher read throughput (300K/s for Rocksandra vs. 30K/s for C* 3.0).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*E-2efj-mMo0dQWEvZyxn1g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*d5gs5SJzq6laocevBqA1Bg.png" /></figure><h3>Future work</h3><p>We have open sourced our <a href="https://github.com/Instagram/cassandra/tree/rocks_3.0">Rocksandra code base</a> and <a href="https://github.com/Instagram/cassandra-aws-benchmark">benchmark framework</a>, which you can download from Github to try out in your own environment! Please let us know how it performs.<br> <br>As our next step, we are actively working on the development of more C* features support, like secondary indexes, repair, etc. We are also working on a <a href="https://issues.apache.org/jira/browse/CASSANDRA-13474">C* pluggable storage engine architecture</a> to contribute our work back to the Apache Cassandra community. <br> <br>If you are in the Bay Area and are interested in learning more about our Cassandra developments, join us at our next meetup event <a href="https://www.meetup.com/Apache-Cassandra-Bay-Area/events/248376266/">here</a>.</p><p><em>Dikang Gu is an infrastructure engineer at Instagram.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d64f86b43589" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instagram-engineering/open-sourcing-a-10x-reduction-in-apache-cassandra-tail-latency-d64f86b43589">Open-sourcing a 10x reduction in Apache Cassandra tail latency</a> was originally published in <a href="https://medium.com/instagram-engineering">Instagram Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Copy-on-write friendly Python garbage collection]]></title>
            <link>https://medium.com/instagram-engineering/copy-on-write-friendly-python-garbage-collection-ad6ed5233ddf?source=rss-a4c6efa67fe0------2</link>
            <guid isPermaLink="false">https://medium.com/p/ad6ed5233ddf</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[python3]]></category>
            <category><![CDATA[python-programming]]></category>
            <dc:creator><![CDATA[Instagram Engineering]]></dc:creator>
            <pubDate>Wed, 20 Dec 2017 18:32:35 GMT</pubDate>
            <atom:updated>2017-12-20T18:34:25.232Z</atom:updated>
            <content:encoded><![CDATA[<p>At Instagram, we have the world’s largest deployment of the Django web framework, which is written entirely in Python. We began using Python early on because of its simplicity, but we’ve had to do many hacks over the years to keep it simple as we’ve scaled. Last year we tried <a href="https://engineering.instagram.com/dismissing-python-garbage-collection-at-instagram-4dca40b29172">dismissing the Python garbage collection</a> (GC) mechanism (which reclaims memory by collecting and freeing unused data), and gained 10% more capacity. However, as our engineering team and number of features have continued to grow, so has memory usage. Eventually, we started losing the gains we had achieved by disabling GC. <br> <br>Here’s a graph that shows how our memory grew with the number of requests. After 3,000 requests, the process used ~600MB more memory. More importantly, the trend was linear.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F5yeIOm1R1uVueuZyAkgCQ.png" /></figure><p>From our load test, we could see that memory usage was becoming our bottleneck. Enabling GC could alleviate this problem and slow down the memory growth, but undesired Copy-on-write (COW) would still increase the overall memory footprint. So we decided to see if we could make Python GC work without COW, and hence, the memory overhead.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/826/1*et6BVu81jjKUxWk3QnjDSQ.png" /><figcaption>Red: without GC; Blue: calling GC collect explicitly; Green: default Python GC enabled</figcaption></figure><h3>First try: redesign the GC head data structure</h3><p>If you read our last GC post carefully, you’ll notice the culprit of the COW was ahead of each python object:</p><pre>/* GC information is stored BEFORE the object structure. */<br>typedef union _gc_head <br>{<br>    struct {<br>        union _gc_head *gc_next;<br>        union _gc_head *gc_prev;<br>        Py_ssize_t gc_refs;<br>    } gc;<br>    long double dummy; /* force worst-case alignment */<br>} PyGC_Head;</pre><p>The theory was that every time we did a collection, it would update the gc_refs with ob_refcnt for all tracked objects — but unfortunately this write operation caused memory pages to be COW-ed. A next obvious solution was to move all the head to another chunk of memory and store densely.<br> <br>We implemented a version where the pointer in the gc_head struct didn’t change during collection:</p><pre>typedef union _gc_head_ptr<br>{<br>    struct {<br>        union _gc_head *head;<br>    } gc_ptr;<br>    double dummy; /* force worst-case alignment */<br>} PyGC_Head_Ptr;</pre><p>Did it work? We used the following script to allocate the memory and fork a child process to test it:</p><pre>lists = []<br>strs = []</pre><pre>for i in range(16000):<br>    lists.append([])<br>    for j in range(40):<br>        strs.append(&#39; &#39; * 8)</pre><p>With the old gc_head struct, the child process’s RSS memory usage increased by ~60MB. Under the new data structure with the additional pointer, it only increased by ~0.9 MB. So it worked!<br> <br>However, you may have noticed the additional pointer in the proposed data structure introduced memory overhead (16 bytes — two pointers). It seems like a small number, but if you consider it applied to every collectable Python object (and we usually have millions of objects in one process, with ~70 processes per host), it could be a fairly big memory overhead on each server.</p><blockquote>16 bytes* 1,000,000 * 70 = ~1 GB</blockquote><h3>Second try: hiding shared objects from GC</h3><p>Even though the new gc_head data structure showed promising gains on memory size, its overhead was not ideal. We wanted to find a solution that could enable the GC without noticeable performance impacts. Since our problem is really only on the shared objects that are created in the master process before the child processes are forked, we tried letting Python GC treat those shared objects differently. In other words, if we could hide the shared objects from the GC mechanism so they wouldn’t be examined in the GC collection cycle, our problem would be solved. <br> <br>For that purpose, we added a simple API as gc.freeze() into the Python GC module to remove the objects from the Python GC generation list that‘s maintained by Python internal for tracking objects for collection. We have upstreamed this change to Python and the new API will be available in the Python3.7 release (<a href="https://github.com/python/cpython/pull/3705">https://github.com/python/cpython/pull/3705</a>).</p><pre>static PyObject *<br>gc_freeze_impl(PyObject *module)<br>{<br>    for (int i = 0; i &lt; NUM_GENERATIONS; ++i) {<br>        gc_list_merge(GEN_HEAD(i), &amp;_PyRuntime.gc.permanent_generation.head);<br>        _PyRuntime.gc.generations[i].count = 0;<br>    }<br>    Py_RETURN_NONE;<br>}</pre><h3>Success!</h3><p>We deployed this change into our production and this time it worked as expected: COW no longer happened and shared memory stayed the same, while average memory growth per request dropped <strong>~50%. </strong>The plot below shows how enabling GC helped the memory growth by stopping the linear growth and making each process live longer.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ORJ8196J7eRZL39f77wEHw.png" /><figcaption>Blue: is no-GC; Red: auto-GC</figcaption></figure><h3>Credits</h3><p>Thanks to Jiahao Li, Matt Page, David Callahan, Carl S. Shapiro, and Chenyang Wu for their discussions and contributions to the COW-friendly Python garbage collection.</p><p><em>Zekun Li is an infrastructure engineer at Instagram.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ad6ed5233ddf" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instagram-engineering/copy-on-write-friendly-python-garbage-collection-ad6ed5233ddf">Copy-on-write friendly Python garbage collection</a> was originally published in <a href="https://medium.com/instagram-engineering">Instagram Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Let your code type-hint itself: introducing open source MonkeyType]]></title>
            <link>https://medium.com/instagram-engineering/let-your-code-type-hint-itself-introducing-open-source-monkeytype-a855c7284881?source=rss-a4c6efa67fe0------2</link>
            <guid isPermaLink="false">https://medium.com/p/a855c7284881</guid>
            <category><![CDATA[python3]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Instagram Engineering]]></dc:creator>
            <pubDate>Thu, 14 Dec 2017 16:51:04 GMT</pubDate>
            <atom:updated>2017-12-16T19:53:16.210Z</atom:updated>
            <content:encoded><![CDATA[<p>Today we are excited to announce we’re open-sourcing <a href="https://github.com/Instagram/MonkeyType">MonkeyType</a>, our tool for automatically adding type annotations to your Python 3 code via runtime tracing of types seen.</p><h3>Motivation</h3><p>At Instagram we have hundreds of engineers working on well over a million lines of Python 3. Every day we have new engineers joining the team from other projects and other languages who need to ramp up quickly and get productive in our codebase. And we’re constantly shipping new code into production, every few minutes, all day long, every day. So we’re keen to make our code easier for new developers to read and understand, as well as more amenable to static analysis that shrinks the domain of possible bugs. Type annotations and static type checking fit that bill.<br> <br>Writing new code with type annotations is easy enough; most of our engineers are eager to do that. But the returns on static type checking are low until we reach a critical mass of type-annotated code, especially our core frameworks and libraries. In other words: we have a <em>lot</em> of existing code that needs type annotations added.<br> <br>Our first forays into manually adding type annotations were discouraging. It can take hours to annotate a single module, sometimes painstakingly tracing through multiple layers of function calls and objects to understand the possible types at some call site. (This is, of course, the same pain that anyone trying to maintain that function might experience; that’s why we want to add type annotations!)<br> <br>So we built MonkeyType. Instead of guessing or spelunking for the right types, let your test suite or (better!) your production system tell you what the real types are.</p><h3>Usage</h3><p>Sounds great! I’ve run pip install monkeytype. What&#39;s next?<br> <br>Before MonkeyType can tell us anything useful, we need to let it trace some function calls. The simplest way to do this is with monkeytype run, which runs any Python script under MonkeyType tracing. For instance, you can easily run your test suite under MonkeyType:</p><pre>$ monkeytype run runtests.py</pre><p>(or monkeytype run `which pytest`, or whatever your preferred flavor.)<br> <br>While your tests are running, MonkeyType inspects the argument types and return/yield type of every function call, recording them in a database. (By default it keeps them in a local SQLite database, but like just about everything MonkeyType does, <a href="http://monkeytype.readthedocs.io/en/latest/configuration.html#monkeytype.config.Config.trace_store">this is configurable</a>.)</p><p>Of course, your test suite may not provide the best type information — sometimes tests use fakes instead of the real types, and we’ve found plenty of cases where type checking revealed that our tests were accidentally passing in different types from production. So if you don’t want to annotate based on your test suite, you can record call traces from production runtime. For this use case, MonkeyType provides <a href="http://monkeytype.readthedocs.io/en/latest/tracing.html#trace-context-manager">a context manager API</a>:</p><pre>from monkeytype import trace</pre><pre>with trace():<br>    # ...</pre><p>If you need even more flexibility, you can <a href="http://monkeytype.readthedocs.io/en/latest/tracing.html#calltracer">create your own</a> CallTracer, install it with sys.setprofile(), and remove it when you&#39;re ready.<br> <br>Once you&#39;ve got some call traces recorded, you can generate a stub file for any module:</p><pre>$ monkeytype stub some.module</pre><p>If the stub looks reasonable and you want to apply the annotations directly to your code, MonkeyType will do that too:</p><pre>$ monkeytype apply some.module</pre><p>Review the now-annotated code in some/module.py, correct the annotations if needed, and commit it!<br> <br>Because of the <a href="http://monkeytype.readthedocs.io/en/latest/stores.html">backing-store design</a>, you don&#39;t have to record traces and annotate all at one go. You can collect traces into the database over a long period of time and gradually annotate more and more modules from the collected data as you&#39;re ready to do so.</p><p>With <a href="http://monkeytype.readthedocs.io/en/latest/generation.html#type-rewriters">configurable type rewriters</a>, you can easily tweak MonkeyType’s generated type annotations for your preferred type annotation style or specific edge cases in your codebase.<br> <br> For lots more details on customization options, refer to <a href="https://monkeytype.readthedocs.io">the documentation</a>.</p><h3>Open Source</h3><p>Check out <a href="https://github.com/Instagram/MonkeyType">the code</a>, give it a spin, and let us know what you think! We’re looking forward to your bug reports and suggested improvements. If you’d like to contribute, we have a <a href="https://github.com/Instagram/MonkeyType/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22">list of suggested starter tasks</a> in the issue tracker.<br> <br>MonkeyType does require Python 3.6+, and generates only Python 3 style type annotations (no type comments). If you aren’t quite there yet, you may want to start with <a href="https://www.youtube.com/watch?v=66XoCk79kjM">our PyCon 2017 keynote</a> on how we migrated those million lines of code to Python 3 last year.</p><h3>How we use MonkeyType at Instagram</h3><p>We choose a small random sample of production web requests and turn on MonkeyType tracing via a Django middleware. Traces are stored and retrieved from <a href="https://www.facebook.com/notes/facebook-engineering/under-the-hood-data-diving-with-scuba/10150599692628920/">SCUBA</a>, Facebook’s data analysis platform, using <a href="http://monkeytype.readthedocs.io/en/latest/stores.html">a custom CallTraceStore</a>. We run tracing constantly, so we are always adding new type traces from production. Since production code changes frequently, this keeps our traces up to date.<br> <br>Whenever an engineer wants to add type annotations to a module, they just run monkeytype stub and then monkeytype apply, fix any type errors revealed by the new annotations, and submit a diff!</p><h3>Results</h3><p>With MonkeyType’s help, we’ve already annotated over a third of the functions in our codebase, and we’re already seeing type-checking catch many bugs that would have otherwise likely shipped to production. Race you to 100%!</p><p><em>Carl Meyer is an engineer on Instagram’s infrastructure team.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a855c7284881" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instagram-engineering/let-your-code-type-hint-itself-introducing-open-source-monkeytype-a855c7284881">Let your code type-hint itself: introducing open source MonkeyType</a> was originally published in <a href="https://medium.com/instagram-engineering">Instagram Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Profiling CPython at Instagram]]></title>
            <link>https://medium.com/instagram-engineering/profiling-cpython-at-instagram-89d4cbeeb898?source=rss-a4c6efa67fe0------2</link>
            <guid isPermaLink="false">https://medium.com/p/89d4cbeeb898</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[instagram]]></category>
            <dc:creator><![CDATA[Instagram Engineering]]></dc:creator>
            <pubDate>Fri, 17 Nov 2017 17:04:48 GMT</pubDate>
            <atom:updated>2017-11-17T20:28:07.117Z</atom:updated>
            <content:encoded><![CDATA[<p>Instagram employs Python in one of the world’s largest settings, using it to implement the “business logic” needed to serve 800 million monthly active users. We use the reference implementation of Python, known as CPython, as the runtime used to execute our code. As we’ve grown, the number of machines required toserve our users has become a significant contributor to our growing infrastructure needs. These machines are CPU bound, and as a result, we keep a close eye on the efficiency of the code we write and deploy, and have focused on building tools to detect and diagnose performance regressions. This continues to serve us well, but the projected growth of our web tier led us to investigate sources of inefficiency in the runtime itself.</p><h3>Getting started</h3><p>To determine what we needed to optimize and which techniques we should use, we had to figure out:</p><ul><li>What the Instagram workload looked like from the interpreter’s perspective, and</li><li>Where the interpreter spent its time when it was running our code.</li></ul><p>We started by collecting various bits of data from CPython as it executed Instagram server code. Unsurprisingly, from the interpreter’s perspective, the Instagram workload looks like an object-oriented, web server workload:</p><ul><li>90% of instructions by the interpreter dealt with operand stack manipulation, control flow, and attribute access.</li><li>Our workload wasn’t loopy — 94% of loops terminated after four or fewer iterations.</li><li>Function call overhead was quite high and accounted for roughly 30% of the time spent by the interpreter.</li><li>Attribute access was the next most resource-intensive category of opcodes, and accounted for another 28% of time spent in the interpreter. Attribute loads were not particularly polymorphic; 85% of loads occurred at monomorphic sites.</li></ul><p>Based on the data above, we decided to work to reduce the computational cost of function calls and attribute access.</p><h3>Data Collection Methodology</h3><p>We collected all of our data by instrumenting an interpreter running in our lab environment, InstaLab. InstaLab replays production traffic to web servers that are running in an isolated environment using a request mix taken from the top five views (by CPU instructions). It provides us with a way to collect representative performance data without fear of negatively impacting user experience. For each different data set, we instrumented CPython, built a new interpreter, and collected the relevant data from InstaLab.</p><h3>Bytecode frequency distribution</h3><p>From the perspective of the interpreter, Instagram is just a sequence of bytecode instructions to be executed. As a first step in understanding what the interpreter was doing, we instrumented CPython to collect bytecode execution frequencies. The top ten instructions by frequency are shown in the graph below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PaVxVUG89fMe9KTc-L8dew.jpeg" /></figure><p>Immediately, we could see that LOAD_FAST dominated. In fact, LOAD_FAST, STORE_FAST and LOAD_CONST accounted for roughly 40% of all opcodes executed. This was expected — CPython is a stack machine and each of these instructions moves values to/from the operand stack. Although these instructions are cheap, they are executed quite frequently, and generate memory traffic. This suggested techniques to eliminate loads and stores (e.g. switching to a register based bytecode) would be an effective optimization.<br> <br>When we excluded the opcodes above, we could get a better sense of the “real work” done by the interpreter when it executed Instagram code. The following graph shows the distribution of execution frequency for the top twenty opcodes (of 120 total).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eujkUEfi7I_jYGsGOnYrEg.jpeg" /></figure><p>These instructions accounted for 90% of the remaining distribution. We grouped them broadly into categories:</p><ul><li><strong>Attribute access</strong> — LOAD_ATTR, STORE_ATTR, LOAD_GLOBAL</li><li><strong>Control flow</strong> — CALL_FUNCTION, RETURN_VALUE, POP_JUMP_IF_FALSE, COMPARE_OP, FOR_ITER, JUMP_ABSOLUTE, POP_JUMP_IF_TRUE, POP_BLOCK, JUMP_FORWARD, YIELD_VALUE, CALL_FUNCTION_KW, SETUP_EXCEPT</li><li><strong>Container Manipulation</strong> — BINARY_SUBSCR, BUILD_TUPLE, UNPACK_SEQUENCE</li><li><strong>Operand Stack Manipulation </strong>— POP_TOP, LOAD_DEREF</li></ul><p>This conformed to what we expected to see from an object-oriented, web server workload (as opposed to a numeric workload).</p><h3>Time spent executing bytecode</h3><p>Looking at execution frequencies only told part of the story. In addition to knowing what the interpreter was doing, we also wanted to know where the interpreter spent most of its time. To that end, we measured the number of CPU instructions that were retired while executing each opcode using the <a href="https://l.facebook.com/l.php?u=http%3A%2F%2Fman7.org%2Flinux%2Fman-pages%2Fman2%2Fperf_event_open.2.html&amp;h=ATPwpMZirt68YREULfqe5u-kEDsCH50kDaqMRS7ZzZuTil3HdQ-jGjgGEu7lvqgeyEjg4BHXyc_sjnPUsSJzRu0nkcHcxyDltMeWn_IrocZkepLkRalSLgvE8Lm9AVS_mfTmnFauNMpXmDjwnxbqw6UY6EVKcA">perf_event</a> APIs. We bracketed each opcode body in the interpreter dispatch loop with a read of a hardware instruction counter, then subtracted the two values to compute the “time” spent executing the opcode. For cases where calls were made back into the interpreter, or out to C functions (e.g. the CALL_FUNCTION family of opcodes), we deducted the amount of “time” spent in the callee from the cost attributed to the opcode.<br> <br> The graph below shows the top 10 opcodes by percentage of cumulative CPU instructions retired.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R3vKkw2xm18rYqEC37_y4w.jpeg" /></figure><p>This data painted a slightly different picture, as function calls and attribute loading jumped to the head of the distribution. In terms of “time spent,” the most resource-intensive categories of opcodes were:</p><ul><li><strong>Control flow </strong>— CALL_FUNCTION, CALL_FUNCTION_KW, COMPARE_OP</li><li><strong>Attribute loading </strong>— LOAD_ATTR, STORE_ATTR, LOAD_GLOBAL</li><li><strong>Operand stack manipulation </strong>— LOAD_FAST, STORE_FAST, LOAD_CONST</li></ul><p>Based on the data above, as a first step we decided to spend time optimizing attribute access and reducing function call overhead.</p><h3>Accelerating attribute access</h3><p>One time-honored technique to accelerate attribute access and method dispatch in dynamic language runtimes is the <a href="http://www.selflanguage.org/_static/published/pics.pdf">polymorphic inline cache</a>. Inline caches, in combination with dense attribute storage (using something like <a href="https://github.com/v8/v8/wiki/Design%20Elements#fast-property-access">hidden classes</a>), can dramatically speed up attribute lookup. However, their efficacy depends on the degree of polymorphism seen at the call sites.<br> <br>We instrumented CPython to record the types seen for each execution of LOAD_ATTR in order to gauge the potential of inline caching. The graph below shows the distribution of LOAD_ATTRs by degree of polymorphism.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nZE87tEcJ7WClpIST7A2dQ.jpeg" /></figure><p>Based on the data above, inline caching looked like it would be an effective technique to accelerate attribute access. Roughly 85% of LOAD_ATTRs occured at monomorphic sites; an inline cache of size four should have been able to handle roughly 96% of attribute loads.</p><h3>Workload loopyness</h3><p>The final question we wanted to answer was: “how loopy is our code?” Answering this question helped us determine how effective optimizations targeted at reducing loop overhead would be at accelerating our workload.<br> <br>We instrumented CPython to record the number of iterations performed by each instance of a loop. The graph below shows the distribution of loop instances grouped by the number of iterations performed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0h_Sx6VhZfDV7mgiP5GXPw.jpeg" /></figure><p>As we can see, our workload was not loopy. In fact, 94% of loops terminated after four or fewer iterations.</p><h3>Conclusion</h3><p>As a result of this exercise we identified two major sources of inefficiency for our workload in CPython: function call overhead and attribute access requirements. The data we’ve collected suggests that well-known techniques should be effective at mitigating the inefficiency. As a next step, we’re using this information to guide our efforts to optimize CPython.</p><p><em>Matt Page is a software engineer on Instagram’s efficiency and reliability team.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=89d4cbeeb898" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instagram-engineering/profiling-cpython-at-instagram-89d4cbeeb898">Profiling CPython at Instagram</a> was originally published in <a href="https://medium.com/instagram-engineering">Instagram Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[App modularization and module lazy loading at Instagram and beyond]]></title>
            <link>https://medium.com/instagram-engineering/app-modularization-and-module-lazy-loading-at-instagram-and-beyond-46b9daa3fea4?source=rss-a4c6efa67fe0------2</link>
            <guid isPermaLink="false">https://medium.com/p/46b9daa3fea4</guid>
            <category><![CDATA[android]]></category>
            <category><![CDATA[instagram]]></category>
            <dc:creator><![CDATA[Instagram Engineering]]></dc:creator>
            <pubDate>Fri, 03 Nov 2017 17:02:32 GMT</pubDate>
            <atom:updated>2017-11-03T17:02:32.139Z</atom:updated>
            <content:encoded><![CDATA[<p>Instagram prides itself on having a lean app. But as the number of engineers and features grows, so do the challenges to size. We start to face issues like:</p><ul><li>increase in application size downloaded from an app store</li><li>increase in cold start time, and more generally, increased time to interaction with different surfaces inside the app</li><li>increase in disk space usage</li><li>decrease in developer velocity from increased complexity of the app or build time</li></ul><p>To tackle these issues, we recently enacted an Instagram-wide code modularization effort to create strict isolation between features. We do this on both Android and iOS, but this post will primarily focus on Android. Once a feature is modularized, we can take the feature out of the primary executable file and compile it to the dedicated file and lazily load it during app runtime on-demand. Lazy loading can bring a combination of benefits like improved cold start time (especially on Dalvik) and overall runtime, decreased disk footprint, and better developer velocity if module hotswapping is implemented. This post will walk through how we approach modularization and lazy loading at Instagram, and how you can implement it in your own app with our new open-sourced framework.</p><h3>App modularization</h3><h4>What is modularization</h4><p>Modularization is the process of separating and creating clear boundaries between logical components of code. For Instagram, we’ve been focused on modularizing feature code since it represents a large part of the codebase, and each feature has clear boundaries. Reducing cross-feature dependencies with modularization helps us solve the challenges of a growing app in a couple of ways. One benefit is that it helps us protect our app’s start time from growing as the amount of code in the app increases. Before modularization, a chain of code references from one feature to another might have risked loading in all of that code during app start time. But by breaking apart these references with modularization, we can wait to load code when it’s actually needed using methods like Lazy Loading (see below). <br> <br> In addition, modularization has other great benefits for overall codebase sanity, build times, developer velocity, Instant Apps, etc.</p><h4>How to modularize</h4><p>The goal of modularization is to have a clearly defined interface that separates the implementation of the feature from outside code. Start by auditing all the incoming dependencies to the feature. For each dependency, figure out if it should be removed or kept. Since each case is different, there is no simple way of making this evaluation — but here are some considerations to make:</p><ul><li>Is the dependency a result of misplaced code? Sometimes a dependency exists because there’s a shared library method or misplaced method in the feature that should actually live outside of the feature. If so, move the referenced code out of the feature and into the appropriate place.</li><li>Is there a way to rewrite the dependency to be more generic rather than have it reference feature-specific code?</li><li>Does the dependency exist because we’re accessing some feature-specific logic earlier than necessary? If so, try to defer this logic until later.</li><li>Is the surrounding logic overloaded? If so, simplify the logic into the feature-specific part to make it simpler to reason about the necessity of the dependency.</li></ul><p>If the dependency is still necessary after making these considerations, then it belongs as part of the feature’s interface. If we’ve done the modularization well, the feature’s interface should contain just a few critical methods. Some examples are:</p><ul><li>Methods that allow navigating to the feature</li><li>Lifecycle methods that listen for app-wide lifecycle events</li><li>Methods that expose core functionality in the feature.</li></ul><h3>Module lazy loading</h3><h4>What is lazy loading</h4><p>While modularization will decouple a feature from the outside world and create a clean interface to the feature, lazy loading will take this further and compile the feature out of the main executable (dex) file to a separate dex file. Each lazily loaded feature will live in a separate file, which will bring benefits like:</p><ul><li>allowing the feature to load in memory only when really needed instead of on every cold start. It offloads code from the main executable file, which remains smaller and guarantees better cold start time. On Dalvik it offloads methods off the main dex file, decreasing performance penalty of multi dex.</li><li>clustering feature code together in memory, as it lives in one file and provides most optimal execution in terms of memory access</li><li>using less disk space if some modules remain unused because feature code will never be uncompressed</li><li>possibility of having an adaptable app with different sets of features shipped to different users (e.g. non-business users don’t need business features), thus decreasing initial app size</li></ul><p>And for developer velocity, we added support for hotswapping of lazy loaded modules, which means developers will be able to keep coding and seeing changes without restarting the app.</p><h4>When we trigger lazy loading</h4><p>Generally speaking we load a module when we expect it to be used in the near future. Once the module is loaded it will remain in memory until the next cold start. Module loading may incur a small latency (depending on the size of the module), so different tactics may be applied:</p><ul><li>Loading a module in the background when the user is one click away from the module. This may mean that the module remains unused if the user doesn’t decide to click into that feature or navigates back. But if there’s high probability of clicking into that module, then it’s a good solution.</li><li>Loading a module once the user navigates into the module. If loading latency is small (below 50ms) for the majority of cases (e.g. p99), then we could simply block on loading, and once it’s done navigate into the feature. Otherwise a simple spinner or a progress bar can be displayed so that the app does not appear as frozen.</li><li>Some modules are by nature asynchronous, which eases lazy loading since it will be the part of the asynchronous loading. An example is a video player that runs in a secondary process. Instagram initially shows a screenshot of the video while it loads in the background (often times fetched from the network). Lazy loading would happen in that secondary process and be completely transparent to users.</li></ul><p>We open sourced our framework for lazy loading at <a href="https://github.com/Instagram/ig-lazy-module-loader">ig-lazy-module-loader</a> so that other developers can use it in their apps, or simply learn from our implementation. It can be beneficial if you’re hitting a performance penalty of multi-dex on Dalvik or have many features which may remain unused by some users and you want to save disk space.<br> <br> <em>We recently moved our mobile infrastructure engineering teams (iOS and Android) to New York City. If this blog post got you excited about what we’re doing, we’re hiring — check out our </em><a href="https://www.instagram.com/about/jobs/"><em>careers page</em></a><em>.</em><br> <br> <em>Mona Huang and Julian Krzeminski work on Android at Instagram.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=46b9daa3fea4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/instagram-engineering/app-modularization-and-module-lazy-loading-at-instagram-and-beyond-46b9daa3fea4">App modularization and module lazy loading at Instagram and beyond</a> was originally published in <a href="https://medium.com/instagram-engineering">Instagram Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>