• Encouraging Ownership

    The best teams I’ve been on have displayed high levels of ownership. Ownership is a cultural trait that helps teams avoid failure by providing people the agency to pick up important work. We’ll focus on unmanaged work in this post because that’s the sort of work that is often dropped, and talk a bit about how filling gaps can derail one’s primary work. If you’ve seen people pick up work that needed to be done regardless of whether it was their job, that’s ownership. When people contribute to the overall success of a team, that’s ownership. When they manage what otherwise would have been unmanaged, that’s ownership.

    Several years back a hacker named Geohot shared a Tweet containing a malformed string. That string caused a crash in Apple’s underlying string processing code, which was triggered by JSON deserialization in Twitter’s iOS app. People retweeted Geohot’s Tweet causing it to spread virally, crashing Twitter’s iOS client repeatedly on launch. This happened during my third week at Twitter! Someone dropped a note in the iOS team’s HipChat room. I was on the east coast and one of the few people online at the time, and while it wasn’t necessarily my job to fix this problem I decided to take a look anyway. I noted the quick fix was adding a try/catch around the code deserialization logic, but being new I didn’t know if that was an appropriate fix. An hour or so later a tenured teammate jumped on and put up a patch with the same fix I’d suggested. Someone on Twitter’s backend team added mitigation to stop the problem while the client fix could be released. This is a clear example of ownership, but not every example is so clear.

    With that story in mind, let’s chat about how ownership manifests on teams, how it can wane as teams change, and how to encourage it.

    How is ownership manifested?

    Organizational segmentation is often inversely correlated to a sense of ownership, which is why small teams tend to have higher levels of ownership baked into their culture. People pick up important work that’s outside their purview for many reasons, but two that stand out to me are a desire to be helpful, and simply because no one has discouraged them from doing so yet. This works on young or small teams because everyone fails if nobody picks up unmanaged work, it’s easy to recognize and celebrate such work, and leaders like you trust people to work on the right things. New people joining a team pick up ownership because it’s ingrained in the team’s culture.

    Over time ownership can become diluted, leading teams to optimize for local rather than global success. It takes effort for us to counteract this through both trust and recognition.

    How does ownership become diluted?

    Ownership can become diluted in more than one way. Personnel attrition absent knowledge sharing is a straightforward way to lose your team’s sense of ownership, but a less intuitive way to lose that agency is through growth. Teams tend to grow over time, and with growth come changes and new challenges. Your team may be changing right now, and you may have noticed some of these challenges, like keeping everyone on the same page being harder due to the difficulty of scaling communication. As communication becomes more difficult, teams organize into sub-teams to focus on specific things. Sub-teams are smaller, by definition, and so they have fewer communication issues within them. However, communication can remain difficult between sub-teams.

    Sub-teams still have their own charters and goals: things they own and things they’re supposed to get done. Someone on the team – maybe a manager, or some other leader – is accountable for the work being done and they might, even inadvertently, discourage people from taking ownership by optimizing for local success. 

    But how does organizational change map to work being unmanaged? Imagine a bunch of circles, each representing a sub-team and its charter. Sometimes the circles are far apart, sometimes they overlap a little, but there’s space between many of the circles. The space is unmanaged work, and if it’s important enough it still needs to get done.

    Sub-teams tend to form their own cultures based on their members. These can be managers, folks with tenure, or anyone who brings the team together. If we don’t encourage ownership, then the team’s sense of ownership will be diluted as the team changes, eventually leading to the failure of the broader team. Team leaders would ideally encourage ownership company-wide, but the reality is this varies from team to team and leader to leader.

    It’s an easily defensible position that focus is important, and the important unmanaged work is someone else’s job since it’s not within a given team’s charter. Not only is it a defensible position, it’s actually a correct position so long as some people continue to demonstrate ownership as the broader team scales, and that ownership continues to be encouraged. However, when recognition and trust for ownership are scarce there can be a subtle shift from “it’s not our job” to “it’s no one’s job”, one that’s often not noticed until it’s too late. The broader team fails when something important enough goes undone because it is no one’s job.

    Charters and Goals

    Earlier I mentioned charters and goals are important, and there’s certainly a difference between work that can wait, and work that’s worth setting aside goals for. Completing the former at the expense of team goals is gap filling. There’s a big difference between ownership and filling gaps. Ownership is organization sanctioned and recognized, though you may have to do some work to sell the value of prioritizing specific work. After all, not everyone has the context you have on likely points of failure. Gap filling is picking up work that eventually needs to be done, but isn’t necessarily as important as your primary work. Gap filling can result in unfortunate consequences like your primary work not being completed and your team’s goals not being met as a result.

    How can you tell the difference between work that can wait and work that’s worth setting aside goals for? I like to ask questions like what happens if nobody does this work? What’s the impact on the broader team and the business of completing the work? Does it need to be addressed now or can it wait?

    If it’s work that’s important enough to do now at the expense of local success, then I ask the people ultimately accountable for that success if they agree with my assessment. It’s up to me to convey the importance and value of the work relative to our team’s goals. If they agree, I do the work and it’s up to them to get me the time and space I need to complete it. Sometimes work is important enough, but I can’t personally make the time tradeoff or I don’t have the necessary expertise. In those cases I try to find another person willing to take the work on and hold myself accountable for ensuring it’s completed.

    Like any other prioritization decision there will be several inputs to account for and I may not get the buy-in I need to prioritize that work. Not everyone on a large team needs to take on unmanaged work, but having some percentage of people empowered to do so results in resilience when teams change.

    Encouraging Ownership

    We can encourage ownership by recognizing and making space for the important, unmanaged work their people do and ensuring knowledge about that work is shared. This incentivizes people to share the load and minimizes gaps that would otherwise not come to light until someone leaves the team. This is one way to eliminate single points of failure and while giving people space to learn new things. This is how you can build a culture of ownership over time. In the story above, we fixed the crash because we knew it was critical and we trusted our manager would appreciate our fixing the problem, even if it meant some other work slipped short term.

    I have explicit conversations about expectations and ownership with my team. For instance, I share that I expect our goals to be met, but they can come to me if they need to shift time to unowned work for a little while. I ask them to explain the value of that work and we decide together on the next steps. I also talk about how our primary work will eb and flow (e.g. slower during planning periods and when we’re waiting on experiment results), and how there are natural times in our schedule that allow for taking on unowned work. Finally, I set a rough percentage of how much of my team’s time, over a long time horizon, should go toward core work vs. work our team does not own. That percentage will vary from team to team.

    Ownership is a cultural trait that helps teams avoid failure by providing people agency, though not everyone on a team needs to demonstrate ownership for a broader team to meet its goals. Ownership can be diluted or encouraged as teams grow and change. Encouraging ownership requires us to set expectations, balance local and global priorities, provide recognition, and cover for their teams. Prioritizing unmanaged work requires assessing the impact of the work itself, and making space for people to bring that sort of work to their you.

    You should follow me on Twitter if you enjoy (or maybe don’t enjoy) puns.

  • Hey Amro, what’s on your desk?

    A mostly wire free desk aimed from the right side showing various work equipment: a large monitor, a small laptop to the right and one behind in a stand, two key lights hovering over the desk, a microphone, webcam, keyboard, mouse, trackpad, volume knob, and a notebook, pen, and more
    Amro’s desk

    I shared a Tweet with a photo of my current work setup and a few folks asked what I use so I thought I’d put together a blog post with all the hardware on my desk.

    I have conflicting goals in choosing the gear I use to do my work. For instance, I want to minimize visible wiring and also want a nice, tactile keyboard. There are decent wireless mechanical keyboards out there, but the ones I’ve tried don’t suit my needs (I have tried many).

    I do a lot of video conferencing for work so want my A/V setup to be of high quality. To that end, I use a mirrorless camera, decent lighting, and a good microphone. I keep personal stuff off my work laptop, so my personal laptop needs to be available to me for personal communication.

    Anyway, here’s a list of my gear:

    Desk: Fully Jarvis Standing Desk – A pretty nice standing desk. It’s fairly stable, though a little wobbly. I’d avoid the black desk top if I bought again as it shows dust easily. Wire management underneath is kind of a pain as the metal frame does not have through holes. I like it a lot otherwise.

    Desk mat: Grovemade Wool Felt Desk Pad (Medium/Light) – I like this mat quite a lot: it’s not scratchy, and is a decent mouse pad. My one complaint is it slides around a bit too easily, though my heavy keyboard helps keep it in place. It doesn’t have rubber or silicone on the bottom for friction.

    Monitor: Apple XDR Pro Display and stand – overpriced, but sharp image, and aesthetically pleasing. Nice for photo editing, which I do.

    Keyboard: GMMK Pro with Kailh Box White switches and these keycaps. Some assembly required, but really nice to type on. Clicky, but not so much actuation force that your fingers feel dead at the end of the day. I like a ten-keyless keyboard for its compact size, and I also like having a function row. The volume knob is a nice addition. I did need to modify the firmware slightly to get key mappings right for the Mac. That was kind of a pain, and also fun.

    Mouse: Logitech MX Master 3 – This mouse is comfortable to use for long periods of time and the battery life is great. one downside is the buttons tend to get shiny over time (ABS plastic?). The Mac version lacks Logitech’s Unifying adapter, and Bluetooth support is jumpy in my experience, so you should get the non-Mac version. It works fine with a Mac.

    Trackpad: Apple Magic Trackpad 2 in space gray – super nice, and great for switching between desktop spaces. Excellent battery life, but switching between machines is a little annoying. I put mine to the left of my keyboard.

    Cameras: Fuji X-T30 + 23mm f/2 lens and Logi 4K Pro Magnetic Webcam for Pro Display XDR – I had the Fuji camera laying around after getting a new mirrorless camera, and I bought the lens second hand off a friend to save money. Note this Fuji camera has been replaced by a newer model. If I were buying from scratch I’d probably look at a less expensive mirrorless camera. The Logitech webcam is decent, but the video quality is just so-so, and there’s no bokeh. It’s a nice backup in case something goes wrong, and I appreciate that it attaches to my monitor magnetically and looks nice.

    Camera arm: Elgato Master Mount – A stable camera arm with a nice desk clamp. The side of the clamp is low profile, and the clamp mechanism handle features a ratchet feature that makes it easy to secure in tight places.

    Video capture: AVerMedia Live Streamer Cap 4K – An excellent USB video capture device. While Fuji makes software that lets one use their cameras as webcams via USB, it does not yet work with M1-based Macs, the output is limited to 720p, and it only works in some software (e.g. works in Chrome, not in Slack or Safari). Initially I tried an Elgato Cam Link 4K, which mostly worked, but has issues playing nicely with other high-bandwidth USB devices on the same bus. For instance, video output from the Cam Link 4K freezes after a few seconds when using a Thunderbolt hub connected to an M1-based Mac.

    Lighting: Elgato Key Light Air x 2 – Proper lighting is important for optimal viewing, and key lights are a good place to start. A three point setup would be great, so do some research/planning.

    Headphones:Apple AirPods Max – Expensive, but they sound nice and switch between devices quickly and easily. The battery lasts quite a long time — I burn through about 30% in a typical workday. Amazon sometimes has discounts on these headphones, so I’d check there first.

    Headphone hook: Audio-Technica AT-HPH300 – Soft, inexpensive, and won’t ruin your headphones

    Microphone and interface: Shure SM7B, Elgato Wave XLR, Cloud Microphones Cloudlifter CL-1 – The SM7B is an iconic microphone that delivers excellent sound, but it’s expensive. Check out the Shure MV7 to get a similar sound at a much lower price. You have to put up with branding on the side though. I like the Wave XLR because the cables plug into the back, it has a large volume knob, and a capacitative mute button on top. The SM7B is a notoriously quiet microphone, and the Cloudlifter amplifies it by 20+ db without increasing noise substantially. You won’t need a Cloudlifter if you go with the MV7, and if you use it via USB you won’t need an XLR interface either. Another popular interface is the Focusrite Scarlett 2i2. I went with the Elgato over this as cables coming out the back of the unit makes cable management easier.

    Boom arm: Rode PSA1 – A classic boom arm. This may seem basic, but the boom arm stays where you put it. Others I’ve used tend to sag, etc. The desk clamp is just okay for me. If I were buying today I’d get the Rode PSA1+ instead, which fixes some minor annoyances with the PSA1. Following the trend, the PSA1+ does have prominent branding on it, which I find mildly off-putting. I’d still buy it though.

    Dock: OWC Thunderbolt 4 Dock – A true one-cable solution! All my peripherals plugs into this dock. One cable goes from this dock to my laptop and keeps my laptop charged. This is one of the cheaper Thunderbolt 4 docks on the market, but they all seem to follow the same reference design and OWC makes good hardware in my experience.

    Ethernet: Sonnet Technologies Solo 10G – A Thunderbolt 10gbps ethernet adapter. It’s nice, but no longer made. I’d recommend OWC’s current offering instead.

    Charing cable: Anker Powerline II 3-in-1 Cable – I use a U2F security key and occasionally need to charge my mouse and trackpad. This cable has the 3 most common plugs I need to charge my peripherals, and I can keep my U2F key attached to the USB-C plug for easy access.

    Laptops: Work provided 15″ 2018 Macbook Pro, and a 2020 14″ Macbook Pro for personal use – I won’t bother linking to these. The work machine is getting a bit old/slow, even for my use. The newer personal machine is a dream to use for programming, photo editing, and shitposting on Twitter.

    Laptop stand: OMOTON Vertical Laptop Stand – I use this relatively inexpensive, adjustable laptop stand. My Thunderbolt dock sits behind my laptop in the second groove. The logo is only present on one side so turn it around for a cleaner look.

    Did I leave something out? Lemme know on Twitter and I’ll add it to this list.

  • Approaching Code Review

    Code review is great way to maintain and improve your team’s codebase, and like a lot of team process having an intentional, positive culture around it makes it better. One of my earliest patches at Twitter was adding a short-lived token-based system for logged out requests (think login, signup, etc.). The patch itself wasn’t so large — a few hundred lines of Objective-C plus tests and integration, but it was important enough to require a fair bit of scrutiny in code review. This change was risky, and it wasn’t something we could easily test incrementally in production so we needed to get it right.

    Like all our changes, my patch went through code review and it ultimately took many revisions for me to get it right. Code review can feel intimidating, but in this case I felt supported as my reviewer displayed empathy and patience while helping me find mistakes. That review helped set the tone for how I’d work with others in the future so I thought I’d share a bit of what I’ve learned about approaching code review on large teams.

    My job as a reviewer

    My job as a reviewer is to help make my coworker’s contributions better by helping them catch mistakes as well as helping them improve their technical solution. My job is not to gate keep to get my way. One natural consequence of this is it enables authors to express creativity in their work, which leads to more engaged teammates. Of course, it’s really important to teach others when there’s a better way to solve problems and doing so gracefully is part of providing effective code review. That’s a core part of code review, but it’s not what this post is about.

    Sometimes there are established team norms one needs to follow for the sake of consistency in a codebase — e.g. stylistic things. We’re not here to talk about tabs vs. spaces, sorting imports, or whether braces belong on a new line either though (real talk: spaces, yes, and absolutely not 😇). I’ve found it’s helpful to move these sorts of things to automated linters, where possible, as it removes the need to raise them in code review altogether. This maintains consistency, simplifies and speeds up code review, and removes the potential for animosity over minor stylistic change requests.

    Speaking of norms, your team might consider setting one for the upper bound on the size of a typical patch. Exceptions will be made and that’s okay, but keeping patches small means reviewers don’t need to spend as much time on each code review pass. This is not only considerate of teammates’ time — it also helps make space for their other work. Sticking to reasonably sized patches also makes it easier for multiple people to work on the same project as it reduces the times which one person might be blocked waiting on another. I’ve seen larger patches work out fine on other teams — it depends on your team’s needs.

    Framing feedback

    Framing code review feedback in a way that’s clear and non-confrontational helps avoid the misunderstandings that sometimes result from written communication. Written communication can be devoid of the tone that smooths over mistakes in verbal communication. I ask myself how I might receive a message to help refine the wording so it’s more likely to be understood as I intend. Importantly, the wording I choose critiques the work I’m reviewing and not the person.

    Working relationships can also affect how you communicate in code review. Getting to know your teammates helps you understand their communication style, which helps you give each other the benefit of a doubt when reading, for instance, a terse reply. However, you won’t always have the luxury of knowing how others work and communicate. Whether you’re working with a new teammate or your team is so large that it’s impractical to know everyone, it’s important to consider how framing your feedback might affect how it’s received.

    Another way to approach communication, in code review and beyond, is leading with sincere curiosity. Curiosity helps disarm negative emotion when another person receives your commentary. For instance, something like “did you know we have a utility function for this?” along with a function name is better than sharing only the function name or a link with no additional context because it doesn’t unintentionally imply the author has done something wrong, is lacking expertise, etc.

    Context matters

    When reviewing code I try to keep in mind the author may have context I don’t and vice versa. They may have spent more time on the patch than the number of lines changed indicates, and they might have constraints I’m not aware of like deadlines or other urgent work to get to. I ask questions to gather the context I need to provide helpful code review up front. Do I clearly understand what the patch is supposed to do? Should it have automated tests? Does it have manual testing instructions so I can verify it works? A proper commit message? Documentation where applicable?

    I then leave comments where I think something can be improved. I like to prepend “optional: ” or “nit:” where I have minor suggestions that shouldn’t block merging the patch so the author knows they are non-blocking. The author makes the call on whether to address those comments. The patch is their contribution, after all.

    If a patch requires substantial rework — maybe I feel I’m leaving too many comments, or sense we’re talking past each other in code review, then I take the discussion to a richer communication medium. For instance, if we’re having a discussion on a code review tool, then I’ll send a private message on Slack to ask if we can chat there. If we’re talking on Slack then I’ll ask if we can have an audio or video call. There’s a bit more to this though: Slack is richer than a typical code review tool because it moves the conversation from a public space to a private one, and it enables live communication. The former can help dial down emotions, but it doesn’t necessarily help with tone as that requires intentional word choice, especially in written communication. It’s worth keeping in mind the culture one grew up with may also affect how they interpret your words.

    Earlier I mentioned tone helps bridge understanding. Moving to a richer communication medium is one way to add tone to the discussion. I’d be remiss if I didn’t mention it’s important to note any potential accessibility considerations and working hours for colleagues when switching communication mediums.

    Avoiding wording that make assumptions about the other person’s knowledge is part of understanding teammates may have different context. I try to avoid words like “obviously” or “just” because what’s obvious or simple to me may not be so obvious or simple for someone else. Perhaps they’re new to some part of the codebase, the team, or are earlier in their career.

    Wrapping up

    I’ve certainly done all these things wrong in the past, and I’ve been on the receiving end of code reviews from colleagues who have done the same. It wasn’t always clear to me when I made a mistake as a reviewer until later, but it was usually clear to me when I felt let down by the way someone delivered feedback on my work. Being cognizant of your job as a reviewer, framing, and context you have (and don’t) can help you avoid unnecessary strife with your teammates.

    There are also things you can do as an author to make code review easier on yourself and your colleagues, as well as ways you can defend your time and approach where needed. We’ll talk about those things in a future post.

    Follow me on Twitter at @amdev if you like puns or pizza.

  • Facilitating Instead of Dictating

    Early in my career I thought showing my worth on a team meant having my ideas win. I was far too attached to those ideas, which left me frustrated when they weren’t chosen and worst of all made working with others more difficult. Over time I learned the right approach wasn’t to dictate solutions, but to facilitate problem solving. This approach has benefited everyone involved, led to better solutions, and to better relationships.

    Being a Dictator

    Dictating solutions, be it as an engineering lead or a product manager or from entirely outside a team, creates a single point of failure and stifles the creativity of one’s team. The dictator becomes a bottleneck for the team, which slows down decision making, robs others of a chance to grow and contribute, and ironically stunts the dictator’s own growth as they spend time answering questions for others rather than looking ahead. Dictating solutions also alienates others and harms working relationships when mixed with poor communication or aggression.

    Often this sort of behavior isn’t malicious, the dictator may even believe they’re helping. People inadvertently cause harm in an attempt to be the go-to person, doing their best to remove a bit of the burden from others. That’s what I thought I was doing many years ago, and it wasn’t until I saw others facilitate that I realized the benefits of intentionally making space for others to contribute.

    Being a Facilitator

    Leading as a facilitator means coming to one’s team with a problem instead of dictating a solution, then working with them to find the right approach regardless of whose idea it is. This shows the team their input is valued, which helps them become invested in the work and it makes space for others to learn, teach, and show their creativity.

    Sometimes there’s a lack of ideas because the problem space is poorly defined or too big. In the former case I like to ask questions to help better frame the problem, and in the latter I like to break the problem down into smaller problems to be solved. If there are many problems I like to explicitly delegate finding a solution to each problem to a single person. They don’t have to figure it out on their own, but they’re responsible for coming back to the group with a solution.

    Being a facilitator also means admitting one’s mistakes and making peace with letting go of good ideas in the interest of moving the team forward. In the past I’ve made the mistake of coming to my team with a solution, and even when it was a good solution that action left them wondering what their role was aside from execution. This behavior is sometimes displayed by engineers who transition into management roles—that was certainly the case for me. Solving engineering problems was something I was comfortable doing and it was (and sometimes still is) easy to fall into wanting to overstep in the interest of being helpful. That’s a great way to waste talented minds and prevent people from learning. This isn’t limited to new engineering managers though—I’ve observed this behavior at various levels of leadership.

    These days I’m better at making space for others to thrive, and it comes from one simple question: am I coming to my team with a solution or a problem? I’m still a problem-solver at heart, so when I do have a suggestion I make sure to frame it as an option, one option, something to consider and not a prescription. Of course, sometimes I do need to step in, and it’s a fine line, but I try to do so by asking questions to ensure we consider key problems we need to solve. That’s because, as always, it’s about the problems, not the solutions.

  • Delivering Difficult News

    Earlier this year I was asked if some engineers from our team could help build a newly prioritized project. Doing so meant putting our team’s current work at substantial risk of failure. I knew canceling our project was the right thing to do because the incoming request was more important and because continuing our current work meant spreading our team too thin. Our team had put in a lot of time so I wanted to share the news empathetically, but delivering and receiving difficult news is never easy.

    I started by talking to my product manager. They quickly understood and helped me plan how we would deliver the news to our team. We drafted a list of points to communicate. Having a list was important because we didn’t want to leave out part of the message when speaking in different places or with different people. We detailed what was changing, why, what would happen next, and honestly that we made the decision. It was important to include we made the decision to avoid an “us vs. them” situation with the people who asked for our help. We shared the plan with our managers, then moved on to telling the team. We wanted to tell them as soon as possible out of respect and so they wouldn’t hear an incomplete message through some other channel.

    Having drafted the message we scheduled a 15 minute chat with each person on the team. These meetings took a full day and this process was slower than telling the team all at once, but it gave them a place to ask questions openly without the pressure that might come with speaking up in a bigger meeting. We took two or three minutes to share the message, and spent the rest of the time answering their questions earnestly. We gladly spent more time with people who had additional concerns as we wanted them to know we cared about them and their work. One person requested a retrospective, which we committed to having later in the week.

    Later that day we had a broader team meeting where we repeated the news the same way and answered questions from the group. This let the team settle into the decision together. By now the news wasn’t new to anyone, which meant the collective response was not one of shock. The people that initially requested our help attended the meeting so they could answer questions and explain why the new project was important for our users and product. And of course we held a retrospective later in the week, as promised.

    After the team meeting we sent a brief email with the same message to our team and adjacent people working with us. The idea was to ensure people who couldn’t attend earlier meetings received the message.

    While our team was disappointed that we cancelled the project, they understood the circumstances and took the news well. More than one person told us they appreciated how we communicated the cancellation. We helped with the other project, and worked to deliver a new, well received, project from our team some time later.

    It’s worth planning how you communicate difficult news. Our approach was to be honest, clear, and consistent. We were quick to communicate, accountable for the decision, gave everyone a chance to ask questions in a low pressure environment, repeated the message consistently in more than one place, and gave people multiple chances to share their concerns. We then worked to address them where we were able to do so.

    Follow @amdev on Twitter for occasional management and engineering thoughts, but mostly puns and pizza.

  • It’s About Managing Expectations

    Some time back my team was asked to take up a critical project on short notice. My product manager and I found out about the project on Monday evening and we were told it needed to ship on Friday the same week to 3 client platforms with a small team. We had just four days to make this happen and the date couldn’t move so we had to work with the other two project levers: changing the amount of work we would deliver, and putting more people on the project. The latter famously only helps so much. I want to share how we got many people on the same page and kept them there by managing their expectations.

    What are expectations?
    Expectations are things people believe will happen in the future. Unmet expectations can lead to unfortunate outcomes like duplicate effort, missed opportunities, or worse. Managing expectations is the act of making promises you intend to keep, making sure other people are on board with the plan throughout the process, and bringing them along when things change. Managing expectations helps us avoid unfortunate outcomes, sets a baseline for accountability, and builds trust that can lead to new opportunities.

    Planning
    The first step my PM and I took was to get each other on the same page. That night we discussed our approach and how we’d divide the work. We also decided we would not block on each other when making decisions. This helped us move more quickly and cover for each other when issues came up. We wrote a plan early Tuesday morning detailing our recommended approach and the timeline on which we would deliver the project. We planned just one and a half days for engineering and half a day for QA. That meant we’d ship Friday on one platform and branch for release on the two others that required a longer release process. We chose this approach so we could understand the effect of our change in one market before going global with it. This gave us confidence and helped us get buy-in from a bunch of folks. We deliberately chose the smallest amount of work we could do that would meet our goals. We also let our team know something disruptive was coming and that we’d share more as soon as we could.

    Our plan included other solutions we considered and why we had ruled them out. We did this to avoid spending time going back and forth negotiating what we’d build. In this case our primary stakeholders were a few decision makers farther up and peers from other teams, some of which our change might affect. We made sure we had the go-ahead from the former, but later found out two peers had concerns about the plan. Given the tight timeline we’d gone for the simplest option, which was to turn off their unshipped features temporarily when our temporary change was enabled. We talked with our peers and shared our constraints with them, then agreed to keep their features enabled on a best-effort basis. Their features were complimentary so this was the right thing to do in the end.

    My take-away from this was it’s important to check my assumptions about the expectations others have of me and my team because approval from decision makers isn’t the same as a mandate. I now ask myself what expectations I have of others, and what expectations they have of me and I try to answer those questions before moving forward.

    Sometimes these expectations are clear, but what if they haven’t been said aloud and agreed upon? In my experience this ambiguity leads to misaligned expectations and in turn the sort of unfortunate outcomes mentioned above. I address this by asking questions that might seem silly in my mind and by stating my understanding of our shared expectations aloud. I’ve found others often have the same questions so they end up being not so silly after all. When stating shared expectations I talk about next steps, who is accountable for them, timelines, and expected communication cadence. This gives others a chance to correct our shared understanding. I also ensure decision makers are explicitly onboard with the plan and timeline, not just initially but also throughout the product process, even if the plan does not change. This is important because even if my team’s plan doesn’t change, the plans of adjacent teams or the company might change, and that could affect the relative priority of my work. Another mistake I’ve made in the past is assuming a lack of response to an update was the same as continued approval. I now like to ask for explicit confirmation or feedback on updates so I can work to address concerns if needed.

    With everyone on the same page, we shared the plan with our engineering team and asked if they could meet the timeline. This was mid-day on Tuesday. It was critical to have their okay on the plan because they were the ones that would make the changes we had in mind. They said yes, and we told our stakeholders we were good to go. We were now accountable for the work and the timeline. This wasn’t about taking blame if we failed so much as having an understanding of our goals, and knowing we needed to share progress and roadblocks along the way. We gave regular updates on our progress, which in this case was at least twice a day given the nature of the work. We also shared new decisions we made knowing we could adjust later if needed.

    Managing Expectations
    We’ve talked about how to get everyone on the same page, but we haven’t talked about how to keep them there. That entails communicating with people based on their role with some regularity. Here I ask myself what information the people I’m communicating with need to do their job and how often they need it. In other words, it’s not enough to “manage upward” — you need to manage communication in every direction.

    For instance, a high level decision maker likely doesn’t need to know every detail of my team’s project, but they probably want to understand whether progress is being made, whether it will ship when they expect, and if we need help. Our updates to decision makers on this project were on the order of a few bullet points, which we followed by answering any questions they had. Peers might expect more detailed updates relevant to their role.

    I mentioned we shared updates at least twice a day on this project, but communication cadence depends on the circumstances. I think about sharing updates at a cadence that keeps my work top of mind, but not so often that I’m not conveying meaningful progress. The idea is to make sure stakeholders don’t feel out of touch with your work. Knowing you’re making progress also helps when it comes time for them to shelve projects that aren’t working out.

    You made it all the way to the end of this post so I suppose you want to know how the story ended: we shipped on time with happy stakeholders and our change met the goals we set in our plan.

    Follow me on Twitter if you like puns, pizza, and performance reviews.

  • Software Engineering Isn’t Magic

    The other day I was chatting with a friend who is both very competent and early in their software engineering career. They had noticed it sometimes took others less time to solve problems than it took them, and when they’d ask for help, these folks knew the answer or could often point them in the right direction right away. It felt like magic.

    Thinking this is normal, but it’s not magic. When people come back with an answer quickly it’s because they’re aware of the problem, already understand how a system works, or because they’re pattern matching against previous experiences and happened to guess the issue. But if you’ve ever handed off a difficult bug only to have a peer come back the next day with what seems like a simple fix, it can trigger negative feelings.

    A couple of years ago I was trying to understand a strange bug in my company’s app that broke the theming system with a new beta release of iOS. After several hours I was able to figure out where our theming code now failed, but I was stuck trying to find the root cause of the bug. I asked the person who wrote the theming system for help and they came back the next day with a single, subtle observation about a behavior change in the underlying system libraries that allowed them to fix the problem. In this moment software felt like magic, but in reality this person had invested a tremendous amount of time learning the underlying system libraries. They’d spent hours debugging the problem to understand it. That isn’t magic — that is expertise and putting in the time.

    A year or two before that I was helping improve build times for our team when I hit a segmentation fault in a popular compiler. I remember feeling helpless until a more experienced engineer said something like “it’s just software and we can debug it like anything else[^1].” Ah, just because it’s a black box doesn’t mean it’s magic. It’s a system we can debug because we can observe its inputs and outputs. That gave me the boost I needed to go back to basics: I started commenting out code to see where the compiler failed. After a while I was able to pinpoint the problem (two particular operator overloads) and found a solution that let us roll out a major reduction in full and incremental build times.

    It’s okay for you to take longer than others to find problems or fix things, and it’s okay to need help from others. You’ll get better at what you do over time too. They’re not faster because they work magic — they’ve just seen this before. Sometimes you’ll see something that others haven’t seen, and suddenly you’ll be the magician.

    Thanks to my friend Joe (@mergesort) for helping me edit this post. You should follow him on Twitter. Oh, and I’m on Twitter as @amdev as well.

    [^1]: They meant literally build and debug the compiler

  • Thoughts on Teams

    People have different value systems. Someone may value very expensive headphones, or cameras and you may value sneakers. Someone may value working really hard, and you may value having time to live and travel. It’s best to not push your value system onto others, because it can harm relationships. It’s okay and good that people value different things.

    Related to value systems — how does one’s value system affect how they weigh tradeoffs? Is this refactor more or less important than shipping? Internationalization? Accessibility? Polish? What stage is your company at? What’s the right thing to do? How does one’s value system help define “right”? Can you think of a way to acknowledge a peer’s values and leverage strengths in a way that helps your team succeed and makes them happy?

    Value systems also affect motivations, which vary from person to person. Some people are motivated by public praise, or maybe the rush of shipping something new and exciting, knowing they built something really well, realizing a lot of people will use their work, just helping others, money, or most likely a combination of these things and others. It’s hard to change people’s motivations, but understanding them can help you improve your team’s performance as you work to fulfill them on an individual level. (The team’s needs should come first most of the time, of course.)

    Working hard at your job will sometimes net you rewards, but working hard alone isn’t enough. You have to work on the right things — generally the right things end up being whatever is important to the people making decisions. In my experience it’s easier to burn out working hard on the wrong things, but it can also be really gratifying serving unfulfilled needs. The lesson here is to think about your goals before working hard and choose the right things to work on based on those goals and what you value.

    Be kind, but also direct with others in business to avoid misunderstandings. Sort of related — don’t be a jerk or you’ll harm relationships and people won’t want to work with you.

    Try to be “on” at work. Make a todo list (however you like), and do either the most important thing or the thing you have time to do right now. Mark it off. Remove things that stay on the list for too long without progress and aren’t actually critical.

    Let people know where you’re at regularly and set expectations on when you can get back to them with what they need. This is a lot of what it takes to succeed. Once you’re good at your core responsibilities, look for problems that get in the way of your team’s success. Set out to fix those issues, and get help from your peers. Share credit with and publicly thank the people who help. You’ll build trust and get recognition. Take a broader view of “team” over time and repeat this process to drive bigger change.

    Is this similar to your experience? Lemme know on Twitter.

    People have different value systems. Someone may value very expensive headphones, or cameras and you may value sneakers. Someone may value working really hard, and you may value having time to live and travel. It’s best to not push your value system onto others, because it can harm relationships. It’s okay and good that people value different things.

    Related to value systems — how does one’s value system affect how they weigh tradeoffs? Is this refactor more or less important than shipping? Internationalization? Accessibility? Polish? What stage is your company at? What’s the right thing to do? How does one’s value system help define “right”? Can you think of a way to acknowledge a peer’s values and leverage strengths in a way that helps your team succeed and makes them happy?

    Value systems also affect motivations, which vary from person to person. Some people are motivated by public praise, or maybe the rush of shipping something new and exciting, knowing they built something really well, realizing a lot of people will use their work, just helping others, money, or most likely a combination of these things and others. It’s hard to change people’s motivations, but understanding them can help you improve your team’s performance as you work to fulfill them on an individual level. (The team’s needs should come first most of the time, of course.)

    Working hard at your job will sometimes net you rewards, but working hard alone isn’t enough. You have to work on the right things — generally the right things end up being whatever is important to the people making decisions. In my experience it’s easier to burn out working hard on the wrong things, but it can also be really gratifying serving unfulfilled needs. The lesson here is to think about your goals before working hard and choose the right things to work on based on those goals and what you value.

    Be kind, but also direct with others in business to avoid misunderstandings. Sort of related — don’t be a jerk or you’ll harm relationships and people won’t want to work with you.

    Try to be “on” at work. Make a todo list (however you like), and do either the most important thing or the thing you have time to do right now. Mark it off. Remove things that stay on the list for too long without progress and aren’t actually critical.

    Let people know where you’re at regularly and set expectations on when you can get back to them with what they need. This is a lot of what it takes to succeed. Once you’re good at your core responsibilities, look for problems that get in the way of your team’s success. Set out to fix those issues, and get help from your peers. Share credit with and publicly thank the people who help. You’ll build trust and get recognition. Take a broader view of “team” over time and repeat this process to drive bigger change.

    Is this similar to your experience? Lemme know on Twitter.

  • Resolving Conflict with Empathy

    Note: I wrote the following note over a year ago, but only recently decided to send it to my newsletter. I’m not sure why I sat on it so long. Your [respectful] thoughts are welcome on Twitter.

    Empathy doesn’t always come easy, but it goes a very long way toward building trust with those around you. I’ve seen many well-intentioned people lose the trust of their peers because they fail to communicate with empathy. Perhaps they rush when replying to a message, or they fail to set aside frustration with a decision they believe should have gone a different way. In many cases this is a matter of differently weighed trade offs or a misunderstanding about who is empowered to make a decision. It turns out these things are sometimes unclear.

    It’s important to take a step back in moments like these to get a broader view of the situation: try to understand the position the other person is in, their priorities, how they may be weighing trade offs differently from you, whether they have context you don’t, and who should make the decision. Afterward, think about how to move the conversation forward in a way that’s constructive in the short and long term. Be direct, but diplomatic and try to address their concerns directly.

    I understand using light gray for the text color here is important to you because it matches our logo, but it’s really important that the contrast is high so people with colorblindness can read it.

    This example is intentionally low key — let’s set that aside. We start by showing empathy for our colleague’s point of view. We tell them we understand their concern and attempt to repeat it. This gives them a chance to correct us if there’s a misunderstanding on our part. We then share our proposed decision and the explicit trade offs we’ve made. In the first example above, we posited accessibility was more important than whether text color matches our logo color. Making implicit value judgements about trade offs explicit is key to building understanding.

    If we don’t understand the concern then we ask clarifying questions.

    I want to make sure I understand your concern. Are you worried about whether the text color matches the logo, or are you generally worried about brand guidelines? Is it something else?

    Earlier I said to be “constructive in the short and long term” but what I really mean is if you treat people poorly then they will not want to work with you. If you do this repeatedly then you’ll have a bad reputation and, in turn, your effectiveness will go down over time as more teammates view working with you as a burden.

    You might have been told to “disagree and commit” at some point in your career. Knowing when to fold ‘em, as they say (whoever “they” are), is an important tool in maintaining trust long term and maximizing your effectiveness at work. When deciding whether you should disagree and commit you should consider the long and short term ramifications of a decision[1] and whether you’re the person empowered to make the decision. For instance, if the decision is regarding something like the naming of an internal API then arguing until you get your way is going to erode some amount of trust from your teammate(s). Over time situations like this will cause you to be seen as an inflexible person. On the other hand, advocating for something like maintaining or increasing user privacy is likely really important and is worth fighting for.

    You might also be asked to consult on a decision. This might come in the form of a friend or colleague asking for advice, or some sort of group setting that’s more formal. It’s important to recognize your place in these situations. Are you being asked to give input or are you the decision maker? It’s important to set your expectations accordingly to avoid disappointment if the designated decision maker makes a decision in conflict with your advice. If you don’t understand the decision then ask what trade offs they made or what additional context and inputs they took into account. Remember that they may weigh trade offs differently from you and that’s okay.

    If you’re a decision maker asking others for advice, then set their expectations early. Some people may need you to tell them their input is valuable and appreciated, but you are also taking many inputs into account in some broader scope. These people may also need to be told directly that you may make a decision counter to their recommendation if other inputs need to be weighed more heavily. This approach can help avoid needless escalation and conflict.

    Resolving conflict is more difficult if there’s not a well-defined decision maker. For example, consider the case where you and a peer have conflicting viewpoints on a decision and you’re at a stalemate after going through the process. In cases like this it’s helpful to bring in a third party to mediate or break the tie. It might also be helpful to consider whether the decision is reversible. If it is, then it’s easier to disagree and commit to move forward. If it doesn’t work out the team can take its lumps and change course. Most decisions are reversible.

    A final note: it’s sometimes quite hard to strike the right balance between directness and respectful tone in text. Try talking to your counterpart face to face or video chat if a conversation goes sideways via text chat. It may remind you both there’s a human on the other side of the discussion.

    [1] It’s important to keep ethics in mind here. If you’re fighting to uphold some ethical boundary then it’s always worth continuing to advocate for your position.

  • Finding Dependency Cycles in iOS Builds with llbuild UI

    Did you know the swift-llbuild project contains a debugging tool called llbuild-ui that can find dependency cycles in your iOS build? To try it:

    1. Enable the New Build System (In Xcode it’s under File, Workspace Settings)
    2. Build your project
    3. Open the Derived Data directory on your machine and find the build.db file generated by the build system. This will be in a path similar to /Users/<yourUsername>/Library/Developer/Xcode/DerivedData/iOS-<someIdentifier>/Build/Intermediates.noindex/XCBuildData/build.db. Take note of this path as you will need it later.
    4. Follow the instructions here to install llbuild UI. You’ll need to have Python (and probably some dependencies) installed on your system.
    5. Start llbuild UI by running the command shown on the Github page linked in step 4 above in terminal. FLASK_APP=llbuildui.app venv/bin/python -m flask run
    6. Visit http://127.0.0.1:5000/ in your browser. You should see something like this:
    7. Click on Database Browser, which will show you the database configuration screen:
    8. Paste the path from step 2 above into the field and click “Set Path”
    9. Wait until the page refreshes then change the address in your browser’s address bar to http://127.0.0.1:5000/db/diagnostics. Any cycles found in your build should be shown on screen.