Debugging Hard Things: Safari Edition

Some problems can be hard for us to debug because we think it’s too difficult to understand. Signs that this may be happening is if we start making wild guesses, we try the same thing over and over again, or copy and paste a solution we don’t understand in the hopes that it will go away.

Hint: Adding more zeros usually won’t help…

I’ve seen this in practice from many smart and capable folks (including myself!) with concepts like z-index (like did you know there are multiple stacking contexts, not a single global one?), CSS specificity (it really is just counting!), spotting memory leaks, puzzling through concurrency issues, or trying to work around browser bugs.

So what can we do instead, when we notice something that’s hard for us to debug? Plenty!

  • Take a pause with the beginner’s mindset. It’s okay to not know how something works. We can take some time to learn some of the basics to help us move forward.
  • Remember that computers aren’t magic! 🪄 There’s a reason why this is happening. If we’re having trouble figuring it out, it might be for a reason that’s on a layer that we’re not familiar with. (browser, compiler, DB, OS, API, network, a bad physical cable, and so on).
  • Know that we can break big problems into smaller ones and work systematically to rule out or narrow down theories.

So that all sounds great, but how do we apply that?

Let’s take a closer look at what this process can look like in practice. The following is a real example of a browser bug I hunted down and some techniques I have found helpful.

A Real Example: Flickering Elements in Safari

One issue that piqued my interest while working full time on the WordPress Block Editor was some puzzling behavior in Safari when scrolling post content in the WordPress block editor.

The text was flickering on image captions and there was a black flashing when scrolling quickly.

Cursory searching said that most simply promoted more elements to their own compositing layer (more on this below) via some CSS like transform: translate3D(0,0,0); and called it a day.

While it’s pretty tempting to copy paste a CSS rule like that, what made me pause on accepting a PR that did just that is:

  1. We don’t understand why this was happening.
  2. We don’t understand why this maybe fixed it.
  3. We don’t understand what the consequences of doing so would be.

With that in mind, I dug in to try and provide an alternative solution. Here’s where I started:

Reproduce the Issue

A great first step to start when looking at a bug is testing to see if we can reproduce it.

Reproducing a bug lets us iterate on our theories and potential fixes in an ideally speedy test-a-fix and see-if-the-bug-is-still-there loop. If we can’t reproduce an issue, it doesn’t mean the problem doesn’t exist. It’ll just be much more difficult to iterate on. When things are not reproducible, sometimes we end up needing to test ideas by chatting with those who can reproduce the issue, or bulletproof and verify if an error or issue goes away with production instrumentation 😭.

Observe and Ask

In this Safari example, it was straightforward to reproduce. The issue already had a few videos attached to the issue, so I could verify which editors were being used by 🔍 looking at the UX elements and I could spot types of block content being used in the post as a starting point (paragraphs, cover block, gallery block and more). Creating a quick test post and scrolling in Safari confirmed that this was an issue.

If there’s not enough information in a bug report, we can ask the reporter for more information. Screenshots or full videos can help a lot too when folks don’t understand how to describe a behavior or technical terms of what’s actually happening.

Reduce What’s Needed to Reproduce

A bug report might have hidden assumptions or ideas on why something is happening. These assumptions aren’t always correct, so it helps to confirm or rule these out ourselves. One of the first things I did was also verify that this wasn’t showing any visual issues in FF or Chrome with the same content.

If we can reduce what’s needed to reproduce an issue, this can also help speed up our testing loop. With the flickering elements, I narrowed down post content to contain a single cover block and gallery.

Great so we can reproduce the issue! What’s next?

Assemble Our Known Clues

Like reading a murder mystery, it can help to keep our known facts or hints assembled together. It makes it easy to revisit when thinking of new ideas to investigate or if we need to go back and challenge our assumptions.

Initial Clues List

  • Glitches only display while scrolling in Safari?
  • The black flashing is from a bad browser paint?
  • Maybe related to images (since it’s easier to reproduce with that content)?
  • From quick internet searches: possibly related to layer compositing issues?

Pick a Clue to Investigate or Challenge

Sidenote: sometimes we can use past experience (intuition) to skip through some of these discovery steps. I personally have some experience working on some reasonably complex canvas apps, so compositing was something that came to top of mind when looking at the symptoms of this bug. For the purposes of this section let’s assume that we have zero experience to work with.

Okay so searching on the internet for “Safari flickering scroll” gives us an answer of something like add this magic CSS rule that does nothing: transform: translate3D(0,0,0); (move this element nowhere in 3D space) or something like backface-visibility: hidden (toggling this value is invisible in 2D space) with zero explanation.

Very suspicious, right?

At this point, it might be easy to give up and think “This is weird! Just paste that answer and move on with our lives!” but let’s remain curious and dig deeper. It’s okay if we’re never encountered a browser bug like this before or never had to dig more deeply into understanding browser rendering.

Be persistent and keep searching (or asking folks) and refine our questions!

  • Adding a follow up search like “transform: translate3D(0,0,0);” we can see that we’re trying to get the browser to do something around hardware acceleration.
  • Another search on hardware acceleration hints at a rendering process called compositing.

Bingo! There’s something new to learn or refresh our memories about! Let’s look at browser compositing.

A great way of gaining a basic understanding is finding multiple (hopefully reputable resources), read it, synthesize it and try to explain it again to someone else.

Here’s are the posts I used to try and summarize the next section:

Let’s give that explanation part a try!

Understand a System More Deeply: Compositing

Compositing is one of the last steps that a browser takes when turning a web page into pixels on your screen. It’s also an optimization over a more naive implementation.

Very broadly a modern browser renderer process handles:

  • Parsing: turning an HTML string into a Document Object Model (DOM). Loading external resources (images, styles, javascript), and loading, parsing and executing any JS.
  • Style: computing the style for each DOM node. (Which CSS rule won?)
  • Layout: calculating where to draw nodes and how big they should be.
  • Paint Order: what order should we paint elements? Think of how we might paint a real oil painting where we have some background mountains, a person, and a dog as our main subject. One method would be to paint back-to-front, drawing background elements first. Mountains, then the person, then the dog.
  • Paint and Compositing: Determine how to group elements into layers, paint each layer to fill with pixels, and then draw or put together each layer in the right order for a final image. Let’s go over this in more detail below.

What Is Compositing?

Animation of compositing process from

While we might naively fill in pixels on our screen by painting each element in our viewport in paint order, this is slow. What if we break up parts of the page into their own layers that don’t change as much? Using our painting analogy, we might make a layer for the mountains, one for the person, and a last one for the dog. Like in cel animation, after we paint each layer, we can reposition each layer independently without needing to repaint the entire scene. For browsers this is very useful in scenarios like smooth animation or scrolling.

Determining what should be in a layer is non-trivial. These are internal implementation details and may vary by browser and change over time, but roughly a browser will create a new layer when it has:

  • 3D or perspective transform CSS properties
  • <iframe>, <canvas> or <video> elements
  • CSS animations and accelerated CSS filters
  • It has a descendant that is a compositing layer
  • It has a sibling with a lower z-index which has a compositing layer (in other words the layer overlaps a composited layer and should be rendered on top of it)

Layers can also get pretty large too, which can waste resources if the browser viewport only intersects a small part of it. We can optimize for this by subdividing a layer in a process called tiling. Going back to our painting analogy, think of how we might portion out squares of a large wall mural, in some prioritized order, for multiple artists to draw.

In the browser, determining what layers to create and how to put it back together again is usually split out to a compositor thread, which may in turn also create child threads to give small pieces of work to the Graphics Processing Unit (GPU). The GPU is great at small tasks like painting pixels for polygons, hence the term hardware acceleration that gets thrown around.

On Performance

Another way of thinking about this is that each compositing layer acts as a pixel cache. This is of interest to us as web developers because some types of updates to a web page can skip parts of the expensive rendering process.

From most to least expensive:

  • Layout Change: If we make an element bigger, smaller, or change its position on the page we can’t skip any steps of the rendering process. (Using cel animation as an analogy, think of needing to throw out all of our existing cels and needing to ink, color and reposition them.)
  • Paint Change: If we update a paint property like background, or color, we can skip layout. (Using cel animation as an analogy, we can repaint the existing cels, then reposition them).
  • Compositor change: If we only update a compositor supported property: transform or opacity. We can skip layout and paint. When done correctly we can see very smooth animations and scrolls. (Using cel animation as an analogy, no need to ink or repaint cels, we can simply reposition the layers for a different final image).

Why can’t we make everything a layer?

Have you tried promoting another layer? Maybe all of them? Is this perhaps a bad idea?

We can’t make everything a layer since the tradeoff is memory use and overhead for managing each of those layers! Done haphazardly, we can make our webpage much slower, or even crash! Like with most things we should profile to make sure layers make sense and are kept in check.

Revisit Our Browser Tools

After we understand a system more deeply, let’s check to see if browser have any tools to help track this down! It’s always great to double check what debugging tools are available to us since it makes investigation work go by much more quickly.

Thankfully, Chrome and Safari do have a layers tab in devtools which list these layers, their memory use, and why it created a layer.

Using the Layers Tool

So right out of the box, we can see a few suspicious things:

  1. We have a number of layers, some of them are very big!
  2. Some layers are caused by some position value, like “position: fixed”
  3. Others are caused by “–webkit-overflow-scrolling:touch”

Let’s update the clues list

  • Glitches only display while scrolling in Safari?
  • The black flashing is from a bad browser paint?
  • Maybe related to images (since it’s easier to reproduce with that content)?
  • Possibly related to layer compositing issues?
    • We have many layers. Some of them are big
    • Some layers are caused by some position value, like “position: fixed” or “–webkit-overflow-scrolling:touch”

Refine the Search Using New Information

Using the information taken from inspecting the layers, one thing that called out to me was --webkit-overflow-scrolling since this issue only triggered on scroll (that we know about).

What can we do with that? Well, maybe let’s try toggling the value!

And so here we try to override this in dev tools, but with an unhappy surprise! It’s an unsupported property! But somehow a compositing reason? How rude! 

Well how about we change overflow on .interface-interface-skeleton__content?

This works to get rid of the glitches, but it breaks the sidebars. We can’t go with that approach of course, but we now have more information!

Let’s update the clues list

  • Glitches only display while scrolling in Safari?
  • The black flashing is from a bad browser paint?
  • Maybe related to images (since it’s easier to reproduce with that content)?
  • Possibly related to layer compositing issues?
    • We have many layers. Some of them are big
    • Some layers are caused by some position value, like “position: fixed” or “–webkit-overflow-scrolling:touch”
    • –webkit-overflow-scrolling:touch can no longer be set or unset. This is added automatically when overflow is set to scroll or auto. In other words, any scroller now uses compositing with hardware acceleration and we can’t turn it off.

Hidden Assumption

Sometimes we have assumptions on our clues list that are incorrect and can even limit our thinking sometimes. Let’s take a closer look at this one:

The black flashing is from a bad browser paint?

If true, this would almost 💯 be a browser bug to isolate and ideally be filed as a bug.

This wasn’t the case! While debugging in the elements pane, I stumbled upon the fact that it was coming from a .edit-post-layout .interface-interface-skeleton__content parent element, and we could make it any color we pleased, like pink! The gray overlay was intended to be used to frame the tablet/mobile and template previews.

This wasn’t a graphics glitch, but possibly an incorrect ordering or compositing problem on scroll.

Let’s update the clues list

  • Glitches only display while scrolling in Safari?
  • The black flashing is from a bad browser paint? The background color is “bleeding” from .edit-post-layout .interface-interface-skeleton__content
  • Maybe related to images (since it’s easier to reproduce with that content)?
  • Possibly related to layer compositing issues?
    • We have many layers. Some of them are big
    • Some layers are caused by some position value, like “position: fixed” or “–webkit-overflow-scrolling:touch”
    • –webkit-overflow-scrolling:touch can no longer be set or unset. This is added automatically when overflow is set to scroll or auto. In other words, any scroller now uses compositing with hardware acceleration and we can’t turn it off.

Reduce the Problem

There’s a lot going on in the WordPress Block Editor. To help isolate the noise, two approaches can work in reducing a problem. We can start turning off larger pieces of logic in the app in exploratory PRs OR create a new base case mimicking conditions from scratch.

I opted to try and create a simple HTML/CSS test case, since I suspected it was still part of a browser bug. It’d be much easier to test our guesses in simpler markup and we’d need a simple test case to use for browser bug reporting in WebKit anyway.

My first attempt I came up with was this. I tried to pick out what I thought were the most important parts of the skeleton interface, along with what I hoped was enough test content to trigger the scrolling glitch.

I had partial success. I could trigger this on my large resolution monitor (and see it stop display the behavior when I set font-size back down to something more reasonable). Others however still couldn’t consistently reproduce.

Spot the Difference

Another game I like to play when debugging is spot the difference, where we go piece by piece and make sure we note where a difference appears in one environment or another. This work can be a bit tedious and sometimes requires turning off your brain, similar to going through git bisect to test which commit caused a failure in production. Once we spot a difference, we can then dig in and question why that is.

So, as I was refining the test case to try and make it more reproducible for more folks, I noticed something. Do you see it?

One of the compositing layers was much bigger in size in WordPress Block Editor than in the test case! In the Block Editor one layer was as tall as all content in that pane! Meanwhile my test case shows a layer that is the size of the current browser viewport.

Let’s update the clues list

  • Glitches only display while scrolling in Safari?
  • The black flashing is from a bad browser paint? The background color is “bleeding” from .edit-post-layout .interface-interface-skeleton__content
  • Maybe related to images (since it’s easier to reproduce with that content)?
  • Possibly related to layer compositing issues?
    • We have many layers. Some of them are big. Our initial test case does not have such large layers, what could be causing it?
    • Some layers are caused by some position value, like “position: fixed” or “–webkit-overflow-scrolling:touch”
    • –webkit-overflow-scrolling:touch can no longer be set or unset. This is added automatically when overflow is set to scroll or auto. In other words, any scroller now uses compositing with hardware acceleration and we can’t turn it off.

Detailed Work

With that new clue in hand, I tried to make a much more accurate base case. I attempted to fully mirror the skeleton interface. It was a lot of divs 😭.

My second basecase attempt was this and from the methodical work a surprising item popped out. A simple div was the cause of the huge layer:

<div tabindex="0" style="position: fixed;"></div>

See how removing it gives us a much more reasonable layer size? With the scrollable browser pane at ~1584px x 588px with the test case, there was also around a 50MB difference in memory usage.

Neat! I also opened a debug PR to see what would happen. In the Block Editor we insert this div in the content to aid in focus related issues while scrolling. When we don’t allow it to insert we can see that it too also gives us a more reasonable layer size.

We have mixed results: I wasn’t able to recreate the text flickering issue anymore, but I could still see the background bleed on scroll in some cases.

And for no apparent reason, doing so also triggered a new fun glitch where the background color from .edit-post-layout .interface-interface-skeleton__content also “bleeds” into the scrollbar element when selecting an image.

Let’s update the clues list

  • Glitches only display while scrolling in Safari?
  • The black flashing is from a bad browser paint? The background color is “bleeding” from .edit-post-layout .interface-interface-skeleton__content
  • Maybe related to images (since it’s easier to reproduce with that content)?
  • Possibly related to layer compositing issues?
    • We have many layers. Some of them are big. Our initial test case does not have such large layers, what could be causing it? Adding a div in the scrollable content with position: fixed, will create a layer of height that equals of the scrollable content height.
      • Removing the fixed div, causes a different scrollbar glitch to appear. Removing the fixed div did not fix the background scroll flashing. Do we have more than one problem?
    • Some layers are caused by some position value, like “position: fixed” or “–webkit-overflow-scrolling:touch”
    • –webkit-overflow-scrolling:touch can no longer be set or unset. This is added automatically when overflow is set to scroll or auto. In other words, any scroller now uses compositing with hardware acceleration and we can’t turn it off.

Surfacing for Air

It is easy and normal for one to get frustrated or even stuck on debugging hard things. When that happens, pause, and surface for air. One great thing to do is recap what we’ve done so far. Reminding ourselves of what we’ve uncovered so far in our clues list, summarizing the pieces, and getting input on what to try next and test assumptions works in wonderful ways to unstick us!

So after writing an internal post at Automattic (which contained content like this post, just stopping at this part) I circled back and tried to decompose the problem. I was pretty sure we were seeing multiple issues instead of just one.


At this point, I was pretty sure that we were looking at at least one browser bug, perhaps several. The symptoms here were interesting enough to distill and report back to WebKit, but knowing if I could isolate a bug and see the fix upstream in a reasonable amount of time is typically out of my control.

As web developers, if functionality is important, we often need to pragmatically find a workaround where we typically re-implement the same functionality but avoid the browser bug, even if we’ve isolated the bug or have found a fix.

Background Bleeding on Scroll

The first workaround I focused on was the background “bleeding” on scroll. While, I had a good lead on the flickering text issue, at the time I didn’t know what functionality I broke by deleting the position:fixed divs and what it was intended to be used for. I asked for help in tracking down that why and moved back with fresh eyes on the background-flashing issue.

I tried several things: like changing which components were toggling the overlay for tablet and mobile previews. Everything I tried wasn’t quite right, and I still saw that color bleed. Taking a break I looked at my clues list again.

Going back to my clues list:

  • Glitches only display while scrolling in Safari?
  • The black flashing is from a bad browser paint? The background color is “bleeding” from .edit-post-layout .interface-interface-skeleton__content
  • Maybe related to images (since it’s easier to reproduce with that content)?
  • Possibly related to layer compositing issues?
    • We have many layers. Some of them are big. Our initial test case does not have such large layers, what could be causing it? Adding a div in the scrollable content with position: fixed, will create a layer of height that equals of the scrollable content height.
      • Removing the fixed div, causes a different scrollbar glitch to appear. Removing the fixed div did not fix the background scroll flashing. Do we have more than one problem?
    • Some layers are caused by some position value, like “position: fixed” or “–webkit-overflow-scrolling:touch”
    • –webkit-overflow-scrolling:touch can no longer be set or unset. This is added automatically when overflow is set to scroll or auto. In other words, any scroller now uses compositing with hardware acceleration and we can’t turn it off.

Well it does look like the common thread here is the compositing layer. In this particular case, the background-color was also being set on a div that was a compositing layer.

I was focusing so much on business logic, what would happen if we changed how the CSS rules were applied? If there was a browser bug, what if we moved the background-color rule to a div that wasn’t a compositing layer?

Moving the background-color to a div that was not a compositing layer did the trick! The workaround PR was super simple in retrospect but took quite a bit of 🔍️ investigation to get there.

Back to Text Flickering

With the background bleeding fix in hand I went ahead and rebased my debug PR with the background fixes. As a bonus, that scrollbar glitch was also fixed!

We already knew that removing the fixed divs would fix the text-flickering and doing so avoided creating a very large compositing layer on the block list wrapper.

Now it was a straightforward matter of understanding why the fixed divs were added and providing an alternative implementation to retain functionality. Thanks to others we learned that the divs were used to help prevent scrolling on tab. The workaround needed here was to remove these fixed divs and re-implement the scrolling on tab behavior in a different way:

Isolate and Report

At this point all issues were resolved in Gutenberg with the workarounds that landed.

With limited time, it’d be pretty common to call it quits once we find a workaround for a browser bug. I was really curious about the root causes here too, so I wanted to isolate good test cases for WebKit bug reporting and hopefully get this fixed for others.

Doing this work took about as much time for me as it took to fix the problems in the WordPress Block Editor. I used all the techniques I noted before: reduce the problem, spot the difference, and lots of tedious detailed work.

One of the goals of isolation is to find the smallest test case possible that reproduces the behavior. If you have a good instinct on where the problem might lie, it might be faster to start from scratch and reproduce the conditions agnostic to your app. If not, it can make sense to start with where you can reproduce an issue, then try to chisel away as much as you can.

I also asked others to verify if they could reproduce a test case before reporting. Sometimes different operating system settings, or different hardware can be needed to trigger an issue. Aiming for a simple and easy to reproduce test case will usually lead to faster fixes in any project.

The Solutions

Background Flashing

We fixed this in the Block Editor by moving a background color to a div that was not a compositing layer.

To isolate the problem, one missing ingredient was it required very quick scrolling (usually from a mousewheel). Webkit maintainers also noted that the artifacts here are from tiled layer flashing. (When we split up a large layer into smaller pieces to paint).

It should look like this:

Text Flickering

This was fixed in the Block Editor by removing two position:fixed divs, so we avoided creating a compositing layer that was very large.

To isolate a test case, I had to brute force this one, by turning off as much as I could in the editor, then perform a binary search on the styles to see what kept the glitch or not. The overall test case is a weird combination of needing flex styles, two fixed divs, an iframe and some extra z-index stacking contexts.

After submitting the issue, WebKit maintainers quickly used the technique to reduce what was needed to reproduce ( ✨). It turns out this was a duplicate bug of this regression which had a patch, and there was an amazing turn around of about a day to get that committed.

See also this comment from Simon Fraser on what was happening:

Compositing backing sharing logic exists to reduce the count of composited layers,
allowing layers that would otherwise get composited to paint into the backing store
of some containing block ancestor (usually a scroller). A "backing sharing sequence"
is a backing-provider layer, and a set of layers contiguous in z-order that can
paint into that shared backing. If a layer becomes composited, it must interrupt
the sequence (because layers later in z-order must render on top).

The bug occurred when a layer became composited between the calls to 
BackingSharingState::updateBeforeDescendantTraversal() and BackingSharingState::updateAfterDescendantTraversal(),
for example because of an indirect reason like overflow positioning...

Bonus: Safari Scrollbar Wrong Color

I happened to stumble on this one while debugging a black scrollbar on a test commit, but thought it would be interesting to isolate. This was fast for me to find through luck (half-a-day), since I’m inherently suspicious of extra z-index contexts.

Somehow the overflow controls container is getting behind the scroller layer. Triggered by the negative z-index child

Simon Fraser

There’s already a committed patch in WebKit for this one! 🎉


So when debugging hard problems, don’t despair! Remember that it’s okay to not know how things work. If we notice a gap in our knowledge, we can take some time to learn how things work.

If we work systematically and keep track of what we know and don’t know so far, we can work toward finding a fix, or help narrow down what the problem might be. Techniques like picking a clue to challenge or investigate, reduce the problem, spot the difference, detailed work and surfacing for air can help move an issue forward.

When time allows, isolating an issue and reporting upstream can both help others and deepen our own understanding of what’s actually happening in a layer we are unfamiliar with.

I’m happily employed by Automattic. If working on these kinds of problems excite you, check out our open jobs page!


How to Unstick a Pull Request

Sometimes pull requests can get stuck during code review. In many cases, it’s not because the changes were unneeded, but because the conversation just appears to… well, stop.

I’ll walk through five common problems pull requests get trapped in and what you can do as a reviewer to help move things along.

The Forgotten Pull Request

Photo by cottonbro on

Symptoms: There might have been some conversation on the pull request but now it’s just dead silence. Reading back you see that the last comment was from six months ago without clear next steps. The pull request is still open, waiting for someone, anyone, to help nudge it.

Try: Ask the author if the pull request is still needed and if they’re interested in working on the problem. What happens next is either the pull request is swiftly closed OR the author tidies it up for a fresh set of eyes and a new review. ✨

What is the problem? What does this do?

Symptoms: The pull request summary is a terse sentence. In other cases, the pull request doesn’t summarize and has too much context. For whatever the reason, as a PR reviewer you’re not quite sure why the PR exists or how to test it.

Try: There’s no need to be a detective 🕵️‍♀️. If something is confusing, go ahead and ask the author! Being clear on why a PR is needed and what is does, is not only good for the reviewer, but it helps provide context to future maintainers on why certain decisions were made.

Ideally we want to know:

  • What problem(s) the PR is addressing.
  • What does the PR change?
  • How can we manually test it?
  • What type of feedback is the PR author looking for?
  • Are there any trade-offs to the solution we should be aware of?

PR Checks are failing

Symptoms: Newer contributors to a project might not realize that their pull request has failing checks. Maybe the linter is not happy with some whitespace, or a test is broken and the author isn’t sure on how to run the suite.

Try: Gently remind folks in a comment that checks have failed. If you have the time, point to documentation on how to run the tests and other contribution guidelines. It can also be helpful to troubleshoot environment issues with them or give them additional hints on how to clear up the issues. Using our best judgment, we can sometimes accept the PR in a less than perfect state and help fix the issues by asking and making changes on the branch directly, or in a follow up PR.

No Clear Next Steps

Photo by Leah Kelley on

Symptoms: We see a PR where other participants have left some previous reviews. It isn’t clear what needs to happen next. The conversation might even still be active, but it’s going on and on and on and on… to the point where GitHub thinks it’s a good idea to start hiding part of the timeline:

Try: Ask the active participants what needs to happen for the PR to either merge or be closed. It might be as simple as someone taking the time to summarize what was discussed and what needs to happen next, for example: fix tests, address design feedback, and update documentation. At other times, a PR working through multiple concerns might need to be split out: like starting a new PR exploring expanding an API, or proposing a framework change in a different medium like chat or long-form writing.

Additional Reviews Needed

Symptoms: You’re not confidant of approving the PR on your own. The changes affect multiple areas. Maybe the PR needs other expertise like design or security feedback that you’re unable to provide. You’ve done a great job already by knowing what you know and what you don’t know.

Try: Leave a review clearly stating what you tested, what feedback you have, and what type of feedback you think the PR needs to land. Manual testing and other partial reviews are still a great help to other reviewers. If you know who’d be great at unblocking the review, ping them in a comment with context. If you’re not sure, leave a note on what type of reviewer expertise is needed and if you have time, help the author by asking other contributors on who’d be a good fit to review.

Give it a try!

If you notice a stuck pull request that fits one of the patterns here, try unsticking it! You don’t need to be the project expert to help move things along. Simple actions like asking good questions, manually testing, or asking for additional review help can make all the difference.

#code, #process

Remote Communication

Sustainable remote communication requires being intentional and mindful of yours and other’s time.

In distributed companies, we can’t bump into each other in hallways, have casual coffee chats, or even be able to offer a proactive helping hand if a teammate looks confused. Every conversation: be it in video, text chat or private message needs to be initiated by someone with intention.

What we trade for potential increased focus and flexibility, we lose in in-person spontaneity. This isn’t all good or bad, just different. Being mindful of this while we pick the communication tools at our disposal is key to communicating clearly and efficiently.

If you find that you’re constantly task switching, are having endless back-to-back meetings, or are spending all of your time in text chat, this post offers a few things to experiment with and try. 

It’s 2020, so put on your own oxygen mask first 

Before we dive in, I want to acknowledge that there is a pandemic raging, climate change making itself ever present, and all the while, we’re still expected to work. Maybe you’re also struggling with additional caregiver responsibilities, or the social isolation of not being able to go outside and see physically visit people without calculating the risks. 

If this remote stuff feels impossible, know that it isn’t a normal time to be working remotely. Long time remote-first workers are feeling the strain too. Please take care of yourself as best as you can and if you’re a manager reading this post, please encourage folks to take some time off and model that behavior by taking that time off yourself.

Types of Communication Mediums

Long-Form Writing

Long-form writing is the binding glue for most well-functioning remote companies. If you’re ever unsure of a medium to pick it’s hard to go wrong by starting with a long-form post.

A post written by one author can start a conversation in the company with many people and teams. The medium is also flexible enough to handle everything from routine meeting notes and team project questions (working through technical, design or product questions) to a well-honed north-star vision that might guide company direction for the following year.


  • Long-form writing forces the author to stop and take the time to frame their thoughts in a succinct and coherent way.
  • Participants don’t need to drop what they’re doing, right now, to focus on a problem that someone raises. They can instead reply when it’s the best time for them. A response within a business day or so, for example, is more than appropriate. This allows for more experimentation with personal schedules. Many find it more efficient to devote one part of their day to reading and responding to posts.
  • It allows folks who are more time-shifted to catch up with what’s going on. This can especially be helpful if you have colleagues in very distant timezones or are working flexible hours.
  • We can eliminate some meetings and better prepare for the remaining ones.
  • With search, we can find out why things have happened in the past without us being there or relying on imperfect human memory.

What type of problems would we pick long-form writing for? Really, just about everything: from a CEO describing company vision to an individual contributor getting into the weeds and needing to work out a solution with feedback from co-workers. 


  • A group post (written by many people) outlining the north star for company direction 5 years out and a specific roadmap for the quarter. The post asks readers for feedback with a given time deadline, e.g. open for the next 2 weeks.
  • A project lead summarizes the why, the challenges, risks and estimated time for a given project.
  • A designer asks for feedback from the team and stakeholders for the latest flow for their new feature. 
  • A developer outlines two implementation options for the feature and asks for feedback from both product and other developers on tradeoffs and a check in on if they understand requirements.
  • A lead writes up routine team meeting notes: highlighting the agenda, who attended, what was asked, what decisions were made, any follow up action items and who was responsible.

Tool requirements

Roughly, when picking out a specific tool to power internal discussions, it should have the following elements:

  • Posts can notify the right number of people – There should be some ability to tag individuals and larger groups like teams or divisions.
  • Searchable –  letting us find out why things have happened.
  • Editable – Mistakes can be fixed and we can go back to summarize conversations and outcomes at the top of the post, or in a summary comment. For example, if a post asks a question and a direction was picked, we should double-back to edit the post to point out what happened.

Internally at Automattic, we use a tool called P2 to power these discussions. Forum software or wiki tools might also work for you. 

Try this when:

  • You need to collaborate with one or more colleagues.
  • As a first step to see if a problem can be described and next steps can be taken asynchronously, without a meeting.
  • If no other communication medium is a good fit!


Documentation is a specialized form of long-form writing where information remains relevant over time and is often maintained by many people.

Tool requirements

  • Searchable –  Information is easily discoverable by anyone in the company
  • Editable – by many people


  • Company Culture Book
  • Onboarding documentation
  • Team Processes
  • On-Call Runbook

Try this when:

  • Knowledge is buried in someone’s head and only one person knows how to do something
  • Information is repeated, either through video chats, text chats, or in time-sensitive posts
  • A one-time post resonates with your co-workers more than expected

Workflow Specific Tools (Issues/PRs/Figma/etc)

As a company you may already be communicating asynchronously with very specific tools for a given workflow! For example, bug and feature requests may be written in issues, while developers may spend a lot of time pull requests and code reviews (aka asynchronous pair-programming).

Try this when:

  • Tools and workflows are already in place for your team or company

Try switching mediums when:

  • A discussion is going in circles. Some signs of this might look like needing to click to expand to see a full discussion, or seeing two participants rapidly commenting to each other. When this happens it may be the case that folks are accidentally talking past each other. Text chat or video calls/screensharing can convey information more quickly between two individuals and may help clear up any misunderstandings.
  • We need to step back to better frame a problem at a higher level. For example, instead of going back and forth on implementation details in code, we might need to ask if it’s worth building said feature at all. To reach the right folks, writing in long-form is usually a good call.

If people do choose to switch mediums, please remember to follow up with a summary update or comment summarizing what was decided. This helps others understand what was decided without needing to reach out to others synchronously.

Text Chat

Synchronous communication is a bit of a two edged sword. On the one hand it lets you talk with folks right now, which can help greatly when working through problems and building camaraderie on a team. It also means that we may leave out folks who are in different timezones or are working flex hours out of conversations.

For some, text chat can be very distracting and draining. Let’s go over some reasons why this might be so.

  • Are you allowed to mute chat for focus time, or after hours?
  • What is the expected response-time for a ping in a public channel or direct message? Talk with your lead to get a clear understanding! For example, since Automattic has no core working hours, on my team it’s okay to respond within a business day if folks are not online or have muted chat for focus time.
  • If you’re not on-call, how well are you silencing these notifications on personal devices after work?
  • Can private messages be taken to a more public channel instead? Too many private messages can feel like the equivalent of someone stopping by your desk and interrupting you. If the subject isn’t confidential and can be discussed in the open (think projects not feedback), consider moving the conversation to a more public area. This helps gives others visibility of what you are spending your time on. It also avoids ever growing group direct messages where some subset of folks have half of the conversation.
  • If someone asks for non-urgent help and you’re in the middle of something, can you schedule a time to chat in text or video later on the calendar?
  • Don’t force others to read text chat backscroll. If an interesting conversation has occurred, and we’d like to share what was talked about, a long form post summarizing this is much more appropriate than having someone else parse a long-winding conversation.

Try this when:

  • You need a bit more bandwidth to quickly discuss an idea with a few people.
  • Team/co-worker bonding! a.k.a all of the emojis and gifs! ✨

Video Chat/Screen Share

Video chats and screen sharing demand one’s immediate attention. I can juggle a few ongoing text chats, but I can manage only one video call at a time. In return for this demand on our attention, we can share a lot of information in a short period of time.

Try this when:

  • We’re building relationships! 1:1s, the weekly team call, watercooler chats and so on!
  • When we quickly need to build consensus or brainstorm next tasks in meetings.

Video calls, for whatever reason, tend to be more draining than meetings done in person. Except for 1:1s, aim to keep meetings small, short and on point. An hour for example tends to be the maximum limit of focus, no matter how much people would like to pay attention. Team process may need to be altered to help accommodate this additional cost. I’d recommend simplifying process as much as possible, and preferring async over sync when looking at communication options.


Regardless of what medium we pick, remember to communicate regularly and often! While working remotely, we often need to work twice as hard to make sure folks know what we’re accomplishing and when we need help.

Communicating in a sustainable way requires being intentional and mindful of yours and other’s time. If this ever feels draining, try switching mediums or start a discussion with your lead on experimenting further with process. It may make all the difference!


Three Important Things

Congratulations! You are in a new management role. Now what?

Remember these three things: don’t fall into the safety of your old job, listen, and provide useful feedback.

These are the three most important insights I gained from working as an Engineering Team Lead at Automattic.

Don’t Fall into the Safety of your Old Job

Stepping into a new role, like managing people or projects, requires different skills than the ones that made you shine as an individual contributor. Recognizing that can be difficult.

It may be very tempting to keep on working on what made you successful earlier. Maybe you want to keep doing individual contributor work, like coding that really impactful, but difficult, feature. When you notice this happening, take a moment to pause. You’re in a different role now! Your biggest responsibility is providing clarity and direction to the team, communicating in all directions, and helping foster personal development.

Taking on both the responsibilities of an individual contributor and leader is unreasonable and unmaintainable. Not letting go of that role is an unintentional signal that you don’t trust the team. It also deprives people of opportunities for them to grow or to really excel at their craft (they may do a better job of it than you and that’s fantastic). The book Multipliers, has an excellent analogy for this. Imagine a coach that gets frustrated with how the game is going, and instead of coaching the team on their next strategy, runs onto the field to score that winning goal themselves. Just think of what the team would feel in addition to the crowd.

That isn’t to say that you never should code or design. I find that it is often the least impactful or important thing you can do to help the team. It’s still useful to keep in touch with your prior skills to understand what needs to happen to be a great individual contributor, but it’s not your number one priority anymore. I still personally code-review and code, but this comes last after all my other responsibilities. If I do take on coding it’s often something small that I know I can complete with my fragmented time or I pair with someone who can still own that larger feature.

As a team lead, you’re still part of the team, you’re just in a different role. It can be lonely at times. There can be very little actionable feedback on how well you’re doing, both good and bad. Rest assured the role you’re playing is just as important as everyone else’s position. It’s natural to fall back into these habits of doing things yourself. But when you notice, gently correct yourself and ask: “Is this where I’m needed most?”

Listen and Take Action

A key skill that is important but difficult to master is listening. Knowing when to take action from that listening will make you super effective.

I will be talking about this with the caveat that I’m still working very hard to listen better and more thoughtfully. One of my biggest fears is that I will mismanage people. I think one of the best ways of avoiding this situation is by listening.

So what do I mean by listening? It’s when I can give the space for people on the team to speak, building safety so they can voice their feelings and thoughts, and to do it on a consistent basis to help build our personal relationships and trust together as a team.

Every team is different, and every team will face different problems, but it never hurts to listen, and to listen on a consistent interval.

Listening is often formalized in the form of processes. It gives us some structure to learn how to listen and hopefully take some constructive action from that.

I’ve found Retrospectives and 1:1s to be both simple in format and useful. Sometimes when implemented poorly people find that these exercises are a waste of time. When used correctly they will help you listen to the people on your team, and will help to clarify what next steps are most impactful for both you and the team to act on.

In the next sections I’ll explain each exercise in more detail.


What is a retrospective? It’s a regular meeting held to discuss three things in a structured way:

  • What worked well for us?
  • What could have gone better?
  • What actions can we take to improve this going forward?

That’s it! This meeting is all about lessons learned and trying to improve process so we can repeat what worked, and try to change the things didn’t in a completely blameless and constructive way.

The key idea here is to start a virtuous cycle where:

  • We build trust so people on the team can truly say what they think and what we probably need to change
  • We take action on that feedback
  • We do this consistently, to show that we care, and to create great outcomes for the team.

Before your team adopts retrospectives as a practice, make sure people are committed to trying to listen and that your team has the agency to take some action.

What Retrospectives May Uncover

Retrospectives have a lot of benefits and will sometimes uncover insights of surprising depth. This may include:

  • Reflections on team dynamics.
  • Daily work done well, or small things that we can all celebrate together.
  • Reality checks of how well a project is running and if we’re working on the right things.
  • How the team is really feeling.
  • Systemic issues beyond what the team can solve alone.

What Can Go Wrong in Retrospectives

Sometimes, it feels like process is a waste of time, because it is. Retrospectives can fail to be useful in a number ways:

  • People interrupt others, or don’t let each person have an opportunity to share. The loudest voice is heard above the others.
  • People don’t feel safe to share their actual thoughts and feelings. There is not enough trust with the people involved, or trust that anything will change.
  • People never follow through on action items for any number of reasons. Maybe the action item is not actionable. Maybe no one was held accountable for it. Maybe it cannot be solved solely by the team and we cannot see a first step.
  • Action is only taken on the easy items but nothing is never done on the hard problems.
  • Tangentially arbitrary work is added as an action item that has little relation to what was discussed.

So in some cases, people may go through the motions, still feel unheard, and no change takes place. We aren’t committed to listening and taking action on it.

The Retrospective Prompt

The format I use is simple, but can be experimented with and modified. If you search for “Retrospectives” you will find other prompts on the internet that may be more suitable for you to start with and iterate on.

It’s really important to note that this entire exercise should be driven by the team, and that everyone has a safe space to share their thoughts and feelings. Yes, even Engineers have a lot of feelings because they are human. My alternative name for this exercise on Engineering teams is Engineering Feelings.

We should also remember to celebrate anything that is working well, in addition to recognizing when things are not. Some retrospectives might be gloomy if the team is facing a difficult challenge. In another week the team might be enthusiastically happy and we may have trouble finding action items.

For retrospectives to work, it is vital to do them on a consistent basis. This gives us a window of time to reflect and act on items. If the time period is too great, I find that two things happen: people don’t remember what actually happened, and action items are not accountable since the next meeting is so far away. My team holds a retrospective every two weeks because that cadence works for us.

Give Everyone 5 Minutes to Write Down Thoughts

Separately let folks write down their thoughts for five minutes (on physical or virtual stickies) with the above prompt for the time interval specified. It’s best if people can’t see what others are writing.

Everyone Shares Their Stickies

Once the stickies are ready, the team shares their thoughts verbally, one by one. People will often expand on what they mean instead of reading the note verbatim. When a person is done reading notes, the next person should start. In my case I usually just call on someone to share next, but other methods should work too.

Resist the temptation to immediately dive into a conversation on how to improve things. We’ll have the opportunity to do so after everyone has shared.

Group Similar Stickies

Group similar stickies together into common categories or themes. It’s okay if everything doesn’t fit. If you have a number of people, vote on the themes you’d like to discuss most.

Go Over Old Action Items From Last Retrospective

Before diving into each category, go over action items from the last meeting. Were they acted on? If not the old action items are automatically added to this retrospective’s notes too. If things keep getting kicked to the next meeting, assign it a higher priority to finish it, or revisit to determine if it’s still necessary.

Discuss Each Category in Detail and Create Action Items

The team can now chat about each category and brainstorm ideas of how to improve trouble spots, or celebrate wins together. If people notice that a note is more of a feeling, dig down into why they feel that way. We may still be able to create an action item out of it!

An action item should be relevant and small enough for a person to accomplish in your retrospective time period. Assign a specific person to a task, so it has a higher chance of being done.

Tasks might range from, “Let’s use a different label on issues we file” to “I need to refine and deliver feedback that the project is going sideways and why”.

Publish Meeting Notes

Regardless of whether you have a physical office, or are completely remote, having some artifact of what themes were discussed and action items were created is a must. We can’t see our progress as a team, if we don’t document it!


One on ones are a formalized way of making sure you talk with each person on your team in a private meeting. Depending on the size of the team this may be weekly, or every two weeks.

What you talk about with each person during this time is entirely up to the participants, but may span a number of subjects, from a check-in of how each person is doing to clarification of company goals or helping spot opportunities for personal growth. Less often, it can also act as a safe space for people to feel heard during difficult moments. Sometimes active listening is all you need to do, without needing to have anything change or be “solved”.

Like with any relationship you build with another person, it takes time, and some of the early sessions may feel awkward, entirely casual (totally okay), or way too in the weeds as you get to know each other.

It’s really helpful to let team members come up with points they want to discuss first. It might even be rare feedback for you which is important to hear and act on. This time is for them! Listen before moving onto your own agenda, like feedback or personal growth, though don’t hesitate to move on if they are dominating the entire meeting.

It is useful to keep shared notes with your reports that outline briefly what was said and any action items created (for you or your report). This shared document acts similarly to the written meeting notes in retrospectives. It will help spot themes or repeated conversations, and add accountability to making sure action items are completed before the next 1:1 meeting.

Like Retrospectives, 1:1s are simply a tool to help listen and to maybe act on what is being said.

Providing Useful Feedback

One of your new responsibilities will be letting others know how they’re doing, why that’s so, and shining light on their strengths or areas where they can improve.

It’s unkind to leave people without this feedback, especially if you’re holding onto constructive feedback, because you’re scared that you won’t be liked, or that you’re personally friends. Similarly, if you focus all your time on folks who are having more trouble, this isn’t fair to the people doing well who could use guidance on how they can grow even faster.

Providing useful feedback is a lot of work, but it is very necessary work to help people grow.

Delivering any feedback, both good and bad, is its own subject and skill to master. There are entire books written on how to listen, craft, and deliver feedback well. I would highly recommending reading a few. After you do this, note that you can read and understand the theory but applying this on the spot, in the heat of the moment, is extremely difficult. I am very much a novice at this. Very briefly, here are a few things I wanted to highlight:

  • Great feedback comes from a place of care. You are taking the time and effort to craft this feedback to help someone improve their behavior and succeed, or are highlighting what they are doing great so they can better understand themselves and maybe even build on their strengths.
  • Giving positive feedback requires as much care as constructive feedback. People need to understand what they’re doing well, in what specific instances and why. They won’t be able to apply this feedback otherwise. In a lot of cases the compliment may even feel insincere. “Keep doing what you’re doing!” Is not helpful. Keep doing what?
  • Constructive feedback is best delivered privately and as timely as possible to the situation you noticed. Be careful to avoid value statements. It isn’t a quality of the person that needs to change say “John Doe is a mean person.” It’s trying to highlight a person’s behavior in a specific instance, explaining the impact that it had on others, and what behavior would be more appropriate in a similar situation, or opening a conversation on what that might look like.
  • Constructive feedback is always difficult to hear and to deliver, so take care that the listener is in a good space to try and listen, and that you are ready to try and deliver that feedback.
  • Despite your best intentions, the burden is on you to deliver feedback in a way that is both respectful and can be understood by the listener. It is very easy to get this wrong and hurt people unintentionally.

Listening To Feedback

In some cases people will be giving you feedback. Feedback given to you is a rare gift, but oftentimes, really listening and understanding that constructive feedback can be difficult.

Many people find that the body enters a fight-or-flight mode. Try and notice what you’re feeling. Do you maybe deflect and become defensive? Do you freeze and are unable to hear the rest of the message? Maybe you just feel bad about it, and miss what is being said that is supposed to help. This is something to keep in mind when you write feedback for others, it can be really difficult to hear what is being said.

More often than not, the feedback you receive may not be well-crafted. It may lack specifics, be a value statement, or you may not even agree with the points being said. Instead of dismissing this, maybe take some time to understand what the person really means by it. After all, the easier move is to say nothing if they didn’t care about you. With more conversations, you may be able to find that step of personal growth for yourself.


So, as a new manager, remember these three things: don’t fall into the safety of your old job, listen, and provide useful feedback.

Don’t fall into the safety of your old job. As a manager your role isn’t the same as an individual contributor and relies on different skills. The team needs you to provide direction and clarity. Don’t be the coach that runs onto the field!

Listening is a powerful tool. Over time, team trust and cohesion may be earned by listening well and sometimes taking appropriate action from that listening. Retrospectives and 1:1s are simply tools to help make this happen.

Useful feedback will help accelerate the growth of each team member. Individuals will better understand their strengths, areas of improvement, why that is, and what they could try instead.

Code Reviews 📚

The following is a collection of my thoughts about what makes a good code review. This is a repost from the internal Calypso blog with a few modifications made from feedback. I have also included a few tips to structure PRs in a reviewer friendly way. It’s my hope that this post will help encourage folks to get excited about code reviews.

Frame of Mind

At the start of my career, I didn’t understand why good code reviews were helpful. This was partly because I hadn’t seen a good code review yet. At best someone might have rubber stamped my change, and at worst code gatekeepers nitpicked irrelevant details in the patch leaving both parties in a foul mood.

My thoughts on reviewing changed drastically once I realized I was approaching this with the wrong frame of mind. We are rewarded by what effort we put into it, and as part of that participants must share some common understandings to avoid an unproductive review.

At Automattic, I think we already have a very strong reviewing culture. The following are a few points I personally remind myself, before starting a review.

We all share ownership of the code. It is not yours, or mine, it’s ours. Always welcome improvements and share knowledge freely.

Many programing decisions are opinions. There is often no one right answer. Discuss tradeoffs in a productive way and move on quickly. Stick with project convention for stylistic things like tabs vs spaces, even if you don’t agree with it. Changing convention can be done outside of the PR as a larger discussion with the group.

We can always learn new things. No matter how much one knows, we can always learn more. Folks can expose you to new ideas. Explaining concepts you’re familiar with can help improve your understanding of it.


Another huge part of a successful code review is good communication. We’re all nice folks at Automattic, but text communication is tricky. It is very easy to misinterpret feedback about code as something more personal. I think we’ve done a very good job at avoiding this, but here are a few techniques to lesson confusion.

  • Avoid separating code ownership. Do not assign ownership of the code with words like “my code” or “your code”. Doing so makes code reviews feel more like a personal judgement. We all share ownership of the code. Remember that the code is also a product of many constraints (time, familiarity with the codebase, etc.) and is not a personal reflection about the author. Even the best developers will produce code from time to time that has some issues to work through.
  • Assume best intent, stay positive. Avoid sarcasm and negative descriptors like “terrible” or “dumb” that may be misread.
  • Avoid demands, offer suggestions instead. “What about moving this into it’s own file?” It’s also helpful to phrase these as questions. Often times the reviewer may be missing context on why a particular suggestion will not work.
  • Authors should respond to suggestions. “Great catch! Updated in 565acae.” “We went ahead with the original approach because of timing concerns.”
  • Be explicit. “Let’s do change X because of reason Y”
  • Say if something is a blocker or optional. “Due to security concerns we should update this method before shipping.” “This is optional but I think this reads better if we move this into it’s own method.”
  • If something is confusing, ask. “What is the reasoning behind these changes?”  “I don’t understand what’s happening on this line, could you please explain?
  • Let the author know when you appreciate a change. “Thanks for taking on this task!” “I really ❤️ how this new workflow feels, I left a few notes on some things we can improve.” “This PR drops our build size by 500kb! Great work!”
  • Explain next steps, or complete the review. “I noted a few blockers I’d like to see resolved before we 🚢” “Changes here look great. 🚢 when you’re ready.”
  • Keep up momentum. If a PR looks stalled ask if anything needs to be done. This is especially important for OSS contributors. It is usually better to accept a PR that has a few issues left to work through, and fix it up later, than have the OSS person abandon the PR.

Preparing your PR to be Reviewed

If folks are always waiting for a code review it helps to have some empathy for the reviewer too!

  • Explain why. Assume reviewers have little or no context when reading the PR. Explain why we need this PR and what it does. (This is also very useful when looking at past decisions). Screenshots and gifs are appreciated when behavior is complex.
  • Add Step-By-Step Test Instructions. Can someone unfamiliar with the changes test your PR by reading the summary?
  • Keep changes small. Large changes are difficult to review and understand. Try to separate janitorial changes from PRs that change behavior.
  • Note weird things. This includes explaining any odd code workarounds, or buggy behavior. This can save some back and forth between the reviewer and author, and may also expose existing bugs.

Code Review Benefits

When done well, code reviews can help on many different levels.

  • It spreads code ownership.
  • Communicates changes across teams.
  • Serves as a sanity check to verify that requirements make sense.
  • Allows folks to find bugs though both manual testing and in code.
  • Lets all folks involved learn new things!
  • Can also serve as documentation for how something worked, and why certain decisions were made. Perhaps even for a future you!

Anyone can be a Reviewer!

The fact that code reviews work on many levels also means that reviewers don’t need to know all things about a project in order to make a meaningful contribution. Sharpening copy, manually testing, polishing design, or asking questions about confusing things is a great help.

Code Review Challenge

If this isn’t a habit for you yet, I’ll like to challenge you to try reviewing a few PRs from a different team or one that you may have felt intimidated to contribute to.

Here are a few strategies I use to pick PRs to review:

  1. Take a look at one of the oldest PRs on the needs review list. Ping the author with questions if it looks inactive.
  2. If you don’t have a lot of time, choose a tiny PR to look at. These are the fastest to review and test, and usually have the least risk of causing a regression.
  3. Choose something you’re unfamiliar with. Reviewing PRs is a great way to learn, and to keep a pulse of what’s happening on other teams. Don’t be afraid to dive into a section you’ve never looked at before. If you don’t follow, or something is unclear, ask questions! The PR author is usually happy to explain.

Have fun reviewing!

#code, #process

Work and other things

I work for Automattic a completely 100% distributed company with employees from all around the world. Once a year we hold the Grand Meetup, a frenetic week long event where the whole company flies in to blanket an unsuspecting off-season ski resort with our own wifi and friendly Automatticians.

It’s a crazy-fun experience, and one of the rare chances we get to see the folks we work with in person.


My awesome team


While at the meetup, I got a lot of enthusiastic thanks from folks for helping out on reviewing their pull requests. It was extremely flattering, but I also had the nagging thought of “Is it so unusual for cross-team reviews to happen?”

So Many Pull Requests


To give some more context, I primarily work on Calypso, an open source dashboard for reading, writing, and managing all of your WordPress sites in one place.

At any given time there are roughly 3-5 pages worth of PRs with a needs review label. This is pretty overwhelming. I can understand how easy it could be to have tunnel vision and only keep track of your teammate’s work.

To encourage folks to review a bit more, here’s a quick summary of how I break up my day:

Morning – review all the things!

I happen to work in the latest time zone on my team. This means that by the time I’m online, the rest of my team already has a number of in-progress or needs review PRs that are fresh and waiting for me. This is one of my only chances to chat with folks while they’re also awake, which makes it a great time to catch up with folks or have a live video hangout.

My morning time is very interrupt-driven, which is why this is also the best time of day for me to review my team’s PRs. I have a special email filter for Calypso pings. I try to run an inbox-zero approach here, but depending on current project needs I might not be able to get through all of them.


Coffee and Breakfast is also very important


Afternoon – Coding

By the time I’ve finished lunch, the rest of my team should hopefully be relaxing as their day winds down. Slack is mostly quiet in my afternoons, aside from helping out with a random Calypso fire or two that may occur from time to time. I usually have a solid chunk of uninterrupted time, which is great for getting into the flow, loading a complex problem into your brain, and cranking out a decent approach to a problem.

I stop when I’ve hit a good point, where I’m pretty certain I won’t be dreaming about solving the issue in my sleep.


Evening – Trash Pickup or Cross Team Reviews

After hitting a good point with my in progress issue, I usually have a little bit of time left in my day. To wind down, I might pick out a quick janitorial issue to hammer out, or more often I might peruse that giant list of PRs that need to be reviewed:


But how to pick one?

Picking a PR to Review:

Here are some suggested approaches:

  • Take a look at one of the oldest PRs on the list. Ping the author with questions if it looks inactive.
  • If you don’t have a lot of time, choose a tiny PR to look at. These are the fastest to review and test, and usually have the least risk of causing a regression.
  • Choose something you’re unfamiliar with. Reviewing PRs is a great way to learn, and to keep a pulse of what’s happening on other teams. Don’t be afraid to dive into a section you’ve never looked at before. If you don’t follow, or something is unclear, ask questions! The PR author is usually happy to explain.

Always Waiting for a PR Review?

Do you always feel like you’re waiting for a review? A few tips:

  • Add context and reasons of what your PR is trying to achieve in the summary, so someone unfamiliar with the issues can hop in and help.
  • Add step-by-step testing instructions! This makes it easy for people to review your code.
  • Break down huge PRs into manageable chunks. Large diffs are incredibly hard to review and test, and it’s more likely that folks just don’t have that amount of time to sit down and attempt to understand the PR.
    • The might mean adding wip PRs with feature flags
    • Sorting out janitorial things, like updating code-styles into their own PRs
    • Do things in steps. For example, first creating a PR to add a new component, before using it on a page in a second PR
  • Take some time to review a few PRs! Other folks are waiting too!

Night – Relax!

Your day might look very different, but I find that interrupted time is great for PRs, and quiet time is incredible for coding.

Remember to take it easy though and not to overwork yourself. I personally try to work somewhat traditional hours and turn on a do-not-disturb mode on my phone for night time hours. This helps me from from getting sucked back into work mode too late at night. Other folks might be able to handle a much looser schedule, but I am not one of them. 🙂

Anyway, I hope this post was helpful! To my co-workers it was great seeing you at the Grand Meetup, and I can’t wait to see you all again!




Why didn’t that selector work?

CSS Specificity 

Ever get frustrated when a newly added CSS rule just doesn’t work? CSS specificity is how a browser determines which CSS properties will be applied to an element. If this is the first you’ve heard about this, don’t worry, a surprising number of engineers I meet don’t understand how CSS specificity works and can probably help explain why CSS naming conventions like BEM are so popular. (If you follow BEM, your score should typically never go past 0,1,0 or 0,2,0).

In a nutshell:

  1. The selector with the higher specificity score wins
  2. For selectors with the same specificity score, the last one wins (rule closest to the bottom of the stylesheet)
  3. Inline styles added to an element element overwrites any styles in the CSS
  4. !important will override all other specificity.
  5. If you have two matching !important rules, the last one wins.

How do you calculate specificity?

It’s actually pretty simple. CSS specificity is just counting. If you look at the specification:

A selector’s specificity is calculated as follows:

  • count the number of ID selectors in the selector (= a)
  • count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= b)
  • count the number of type selectors and pseudo-elements in the selector (= c)
  • ignore the universal selector

Selectors inside the negation pseudo-class are counted like any other, but the negation itself does not count as a pseudo-class.

Concatenating the three numbers a-b-c (in a number system with a large base) gives the specificity.


*               /* a=0 b=0 c=0 specificity =   0 */
LI              /* a=0 b=0 c=1 specificity =   1 */
UL LI           /* a=0 b=0 c=2 specificity =   2 */
UL OL+LI        /* a=0 b=0 c=3 specificity =   3 */
H1 + *[REL=up]  /* a=0 b=1 c=1 specificity =  11 */
UL OL    /* a=0 b=1 c=3 specificity =  13 */    /* a=0 b=2 c=1 specificity =  21 */
#x34y           /* a=1 b=0 c=0 specificity = 100 */
#s12:not(FOO)   /* a=1 b=0 c=1 specificity = 101 */

Lets break that down a little bit further.

Concatenating the three numbers a-b-c (in a number system with a large base) gives the specificity.

ID Selectors > Class Selectors > Type Selectors

Here’s the tricky part. No matter how many type selectors you have, it’ll never trump a single class selector, and similarly no matter how many class selectors you have, it’ll never trump a single ID selector. Instead of using base-10, you can put a comma between each of the selector types.

So for example the top .foo selector cannot be overridden by a selector which only uses type selectors, no matter how many of them you add to your selector. For convenience, most folks will use base-10 since hopefully your CSS file doesn’t have selectors like this.

html body div ul li span span span span span span

What about inline styles?

Inline styles always overwrite any styles in the CSS. So the div in this case will be red, and this is true no matter how many id selectors we add to the css rule.

<div id="green" style="background:red;"></div>
#green {background: green;}


In this case, our div actually will be green, despite the inline selector, or any CSS rules. It’s common knowledge to avoid using !important when you can use alternative means, because you’ll likely waste a lot of time for your future-self or your co-workers when you can’t override that important rule you just added.

<div id="green" style="background:red;"></div>
#green {background: green !important;}

When using !important is OK

That being said, !important is a tool and there are times when it’s acceptable to use it.

  1. Overriding Media Query Styles – Need to override a rule for a particular target screen-size? You can avoid this in some cases, but if you use a CSS framework like Bootstrap, you already have these rules in place.
  2. Utility Classes – Want that .pull-right to really stick to the right? !important. It’s easily arguable that you don’t need utility classes at all, but this is how you get them to work.
  3. Email Styling – Email styling is like stepping back in time. You use tables everywhere, clients have terrible CSS support, and sometimes the only way to override default client styling is by using !important.

#css, #specificity

compose new post
go to top
go to the next post or comment
go to the previous post or comment
toggle comment visibility
cancel edit post or comment
%d bloggers like this: