惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Cloudflare Blog
U
Unit 42
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
腾讯CDC
罗磊的独立博客
博客园 - 聂微东
博客园_首页
雷峰网
雷峰网
云风的 BLOG
云风的 BLOG
Jina AI
Jina AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
D
DataBreaches.Net
The GitHub Blog
The GitHub Blog
人人都是产品经理
人人都是产品经理
Y
Y Combinator Blog
量子位
Microsoft Azure Blog
Microsoft Azure Blog
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
月光博客
月光博客
T
The Exploit Database - CXSecurity.com
Google DeepMind News
Google DeepMind News
H
Help Net Security
O
OpenAI News
Blog — PlanetScale
Blog — PlanetScale
S
Security Affairs
S
Security @ Cisco Blogs
Microsoft Security Blog
Microsoft Security Blog
T
The Blog of Author Tim Ferriss
AI
AI
MongoDB | Blog
MongoDB | Blog
G
Google Developers Blog
MyScale Blog
MyScale Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
Hugging Face - Blog
Hugging Face - Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Schneier on Security
Cloudbric
Cloudbric
H
Heimdal Security Blog
J
Java Code Geeks
N
News and Events Feed by Topic
Hacker News - Newest:
Hacker News - Newest: "LLM"
宝玉的分享
宝玉的分享
有赞技术团队
有赞技术团队
S
SegmentFault 最新的问题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
爱范儿
爱范儿
I
Intezer
GbyAI
GbyAI

Graphite blog

Introducing Code Tours: a new way to review Introducing Cursor Cloud Agents in Graphite Building the future of software development with Cursor Reimagining the PR Page: Designing for speed and focus Graphite changelog [11-20-2025] Graphite changelog [11-04-2025] Graphite changelog [10-16-2025] The future of engineering is collaborative (and already here) Meet Graphite Agent: the next evolution of AI code review Introducing frozen branches: A safer way to build on your teammates’ work Graphite changelog [09-17-2025] How we sped up code search for Graphite Chat Introducing Graphite Chat AI is writing code—here's why it also needs to review that code How I got Claude to write code I could actually ship How we built the first stack-aware merge queue (and why it matters) How we organize our monorepo to ship fast Graphite brings stacking to Tower Code review tooling: Should you build or buy? Making AI code review available to everyone Introducing: The new Graphite + Linear integration Graphite raises $52M and launches Diamond to reimagine code review for the age of AI Why AI will never replace human code review How stacked PRs unblock distributed development teams Graphite is going to Developer Week 2025 Beating the end of year code freeze How Graphite’s eng team ships code remarkably fast Why we chose Anthropic's Claude to power Graphite Reviewer AI code generation will remain fragmented How we redesigned Graphite's landing page in-house Introducing Graphite Reviewer: your AI code review companion How AI code review reduces review cycles to improve developer productivity What if you could get instant feedback on your code? The new developer toolchain Not Rocket Science - How Bors and Google’s TAP inspired modern merge queues Graphite's State of code review 2024 How Google migrated billions of lines of code from Perforce to Piper Going from 0 to 1: How to write better unit tests when there are none Speed up your merges: Parallel CI is now generally available for teams using Graphite’s merge queue Down for less than four minutes a month: how AWS deploys code BitKeeper, Linux, and licensing disputes: How Linus wrote Git in 14 days Graphite is now free for startups and open source projects Launch week wrap-up (May 2024) Reduce CI costs for Buildkite and GitHub Actions Cheaper CI & faster merging with batching How Google does code review The technical learning curve at a startup is gentler than you might think Graphite will now automatically rebase your partially-merged stacks Multiple engineers can now seamlessly collaborate on the same stack of PRs Do you ever outgrow GitHub? From the 80's to 2024 - how CI tests were invented and optimized Graphite changelog [4/10/2024] 🎺 Graphite changelog [4/25/2024] 🐸 How Stack Overflow replaced Experts Exchange How GitHub monopolized code hosting Graphite changelog [3/27/2024] 🤝 The core principles of building a good AI feature Onboarding roulette: deleting our employee accounts daily Graphite changelog [3/13/2024] 🚁 Why Facebook doesn’t use Git How to recreate the Phabricator code review workflow Types of code reviews: Improve performance, velocity, and quality What's the best GitHub pull request merge strategy? Phabricator vs GitHub vs Graphite: How do they stack up? Improving team velocity through better pull request practices Building trust as a software engineer Keeping code simple: moving fast by avoiding over-engineering What's better than GitHub pull request filters? The Graphite pull request inbox 7 Best Phabricator alternatives for PR stacking + code review [2024] Accurate eng estimations: predicting and negotiating the future Tracking and understanding GitHub PR stats: A step-by-step guide 8 pull request best practices for optimal engineering What’s next for Graphite Graphite Q1 Launch week: Stacking with the tools you love Graphite Q1 Launch week: Making stacking seamless Accelerating code review The Mom Test How to use stacked PRs to unblock your entire team Graphite Q1 launch week 2024 The practical and philosophical problems with AI code review Empirically sup code review best practices Call site attribution: how to pinpoint rogue SQL queries throttling your performance Every engineer should understand git reflog Post mortem: we took 124 seconds from you, here's 378 back Your GitHub pull request workflow is slowing everyone down Optimizing CI/CD workflows for trunk-based development Why we use AWS instead of Vercel to host our Next.js app How large pull requests slow down development 3 key lessons in application server optimization Trunk-based development: why you should stop using feature branches Git was built in 5 days Why large companies and fast-moving startups are banning merge commits How long should your CI take? Experimenting with AI code review CRA to AppRouter in 5 Steps: A case study with Graphite Graphite Changelog [10/18/2023] The comprehensive guide to writing the best PR title of all time How 10,000 Developers All Contribute to the same Repo
Moving fast breaks things: the importance of a staging environment
Greg Foster · 2024-02-18 · via Graphite blog

Graphite's engineering team has a culture of moving extremely fast. This is very much a stylistic choice - some engineering teams move more carefully, but we like to iteratively ship small, fast changes out to users as quickly as possible. That culture is reflected in the nature of the Graphite product itself, which aims to accelerate code changes - and I’d argue this is somewhat a Conway Law-esque result.

The downside to our organization’s need for speed is that I’m also personally responsible for keeping the site online. For years, I was the first to get paged, and oh boy was I paged a lot during those early days.

In its first year, Graphite was plagued with regressions. Our small engineering team and our blistering pace meant that we’d frequently ship a regression to production and then have to scramble to roll back or fix forward. The approach was functional, but we were relying on paper-cut users giving us feedback. Without slowing down our velocity (culturally unattractive), or getting ten times better at unit testing (tricky because of our strong integration to GitHub’s API), there was little we could do to catch regressions before they impacted users.

Dogfooding our changes before production

Something needed to change. We needed a reliable way to catch unknown unknowns without imposing extra work on developers. Around the end of 2022, after much debate, I chose to build a Graphite staging environment where we’d bake all deployments automatically, pre-production.

While I could have simply duplicated our application servers and created a second Postgres cluster, I decided to go whole-hog and create an entirely separate AWS account, complete with duplicate load balancers, VPCs, S3 buckets, ECS containers, and more. I used a third “tooling” AWS account to house what little was shared - in particular, an AWS code pipeline that deployed changes to both staging and production.

After creating a staging account, I tried dogfooding Graphite on the staging servers for a week. Initially, I was blocked by a few hardcoded url bugs - but eventually, I got the experience workable. Because Graphite used GitHub production as our shared database, I was still able to interface with teammates’ PRs, even if we were working across two separate Postgres clusters.

With me happily doing all my work on the staging cluster, I next added a banner visible only to employees on prod, reminding them that they should instead be using the staging version of the site to help us dogfood the stream of daily changes. This nudging banner proved to be a perfect balance of driving employees to use staging for daily work while still making production easily accessible when necessary.

At this point, I had two duplicate environments, with employees on one and all other Graphite users on the other. Each new release however, would deploy to both environments simultaneously, meaning that unexpected regressions would hit external users at the same time as employees.

💡 How did I do all this without breaking deployments for our team you may ask? I did indeed break deployments - but I tactically did it over a long weekend. While my partner was out of town on a work trip, I was left alone re-watching Scott Pilgrim vs. The World on one screen while trial-and-error running hundreds of deployments on another. The stress of knowing I had to reassemble our deployments before the work week pushed me to land the first pass all at once. After a few sleepless, Scott Pilgrim filled nights, I eventually got things into a stable state.

With everything in place, the final step was to reap the actual benefits of the project. With begrudging team buy-in, I updated our AWS code pipeline to sequence staging deployments, followed by a one-hour wait stage and, finally, an automatic promotion to production. We now had a buffer before production deployments.

I didn’t need to wait long to test the new capability. A few days after adding the staging bake, we released a regression. A bug broke our diffing algorithm, preventing the site from loading pull requests. Half of our site was inaccessible - Nick on our team noticed within 60 seconds of deploying. Previously, this would have taken us half an hour to roll back or longer to fix forward, meaning real external engineers would be blocked from reviewing or merging code. What before would have been a post-mortem-worthy incident was now a minor distraction.

We calmly navigated to AWS and paused our code pipeline, disabling automated promotion from staging to production. With deployments paused, we could take all the time we needed to debug, fix, and re-deploy to staging without stressing that external users would be having a bad time. Folks immediately felt the benefit.

Subsequently, we found ourselves pausing the deployment pipeline for accidental regression about one-to-two times a week. Our rate of production regression dropped by three-quarters, and engineers were able to continue iterating just as fast as before.

We are heavy users of the pause-deployments capability…

Early concerns with our staging bake period

I’d be lying if I said everything was perfect off the bat.

Waiting an hour

Teammates initially complained about the extra hour they needed to wait to see their changes out in the wild. Their fears were slowly assuaged as folks shifted their focus to the staging environment, which still received new builds just as quickly as before.

Two DBs to migrate

Folks also were initially annoyed at needing to maintain two environments. In particular, Postgres migrations became a spot of possible drift - someone might apply a DB schema migration to staging but forget to apply it to prod and vice-versa. Also, to migrate both was net-more work than previously required. We considered building some auto-migration system, but felt there were too many edge cases in our DB to ever guarantee safety there. In fact, I’ve come to see running migrations twice as more of a feature than a bug. While doing so indeed takes more work, it gives engineers a chance to test out applying a risky migration to the site before repeating in production. It only took a few scary DB operations before the sentiment on the team shifted to appreciating having an additional DB to stage migrations on.

Pausing deployments for too long

Being able to pause the deployment pipeline isn’t a perfect catch-all. While it buys time to fix a regression, it also blocks folks across the team from shipping new code. Because pipelines are linear, no one can get new changes out while an issue is being triaged, meaning it’s somewhat unhealthy to lock for more than 12 hours. For one, you block unrelated bug fixes from deploying, which makes it difficult to handle two different incidents at the same time. Secondly, locking deployments for too long risks unleashing a tidal wave of changes onto production. Any regressions beyond this point become hard to correlate back to a specific PR as the accumulated changeset grows to be unwieldy.

Maintaining realistic data on staging

Lastly, we were initially concerned that seeding test data into our staging environment would be a tricky maintenance burden. In practice, this proved to be no issue at all. Because Graphite as a service does not share data across organizational boundaries, we could simply shift all our internal usage to the staging environment and populate it with an organic, long-lived dataset for just our org.

Later improvements to our deployment process

Over the following year, the team improved upon my initial setup. First, we added an “emergency deployment pipeline” that could be manually triggered. This pipeline would build the latest code artifact but skip both staging and bake stages, instead deploying straight to production as fast as possible. We only trigger this pipeline on rare occasions, but it’s nice to have a break-glass way of fixing forward as quickly as possible.

Secondly, we added a manual “skip-bake” command that would call AWS and automatically finish whatever bake period was running. This command has proven useful in times when an engineer is confident that the staging environment is healthy and simply wants to roll their change out to all users faster than would otherwise be possible.

Thirdly, we added code to programmatically lock staging-to-prod promotions outside of weekdays, 9-6 working hours. This small change has saved more on-call sleep than anything else we’ve done. Best of all, deployments continue releasing to staging on weekends or nights, which helps off-hour engineers feel like they can keep releasing code without endangering users.

Lastly, we updated our employee-only page within Graphite to include GUI-based deployment controls. We added the ability to see what SHA each environment was running on and a button to lock deployments during an incident. These changes added quality-of-life improvements to on call where locking our deployment promotion became muscle memory each time we suspected a regression had deployed.

Inspiration from Airbnb

I take no credit for being the first to invent a staging bake period, nor even self-discovering the idea. Rather, everything I’ve built at Graphite has been inspired by my time working on Airbnb’s Continuous Delivery team. There, I worked to help migrate thousands of microservices onto the open-source Spinnaker and create a default pattern for all deployments to release with automated canary analysis. While not every service used this pattern, the majority adopted it, and I witnessed firsthand the major reduction in incidents. You can read more about the amazing work my old teammates completed here.

That being said, not everything has been the same between Airbnb and Graphite. While Airbnb had a cross-service staging environment, there was no continuous human traffic on it. That made test data a real struggle while I was there. We tried various processes of automatically generating data and mirroring production traffic, but in practice, everything we tried at Airbnb paled in comparison to the steady stream of dogfood activity I see today at Graphite.

While Graphite’s deployments have Airbnb beat when it comes to staging traffic, Airbnb had a more sophisticated blue-green prod rollout. Without realistic staging traffic, gradual canary releases to production became important. Each production deployment would gently ramp traffic up to the new release before cutting over, and Spinnaker would call Datadog’s API to monitor for any spikes in errors. If a pre-specified metric regressed, Spinnaker would automatically halt the production rollback and cut traffic back to the previously safe service version. We don’t have this sophistication at Graphite (yet) - if we don't spot a regression through our organic staging usage within an hour, we still promote the bug to prod and wreak havoc on unwitting users.

How you can implement your own staging bake

If you’ve enjoyed my story about how we implemented a staging bake at Graphite and are interested in doing something similar on your team. Here’s what you’ll need:

Get a pipeline

First off, you’ll need some kind of deployment pipeline. Automatically deploying the top of your main branch to your servers is not enough. You need a system that allows you to build an artifact and progress it through a sequence of stages. You need that system to support manual pausing, waiting, and resuming, as well as the ability to continue deploying to staging even if the prod-promotion transition is paused. You could hand code this system or self-host Spinnaker - though these are options that are too time-intensive for a smaller startup. In my experience, the only pre-built tool I’ve found that fits my needs is AWS’ CodePipeline, though there might be something better out there. If you know of a good one, please let me know!

Get data

Secondly, you need a way to populate your staging environment with realistic data. There are three options I know of:

  1. Write custom logic to generate fake data in the environment

  2. Clone and sanitize production data

  3. Have some set of real people living on staging creating the data.

In my opinion, the third option is the best, followed by option two. I’d recommend against the first option - creating test data is a Sisyphean maintenance burden that never quite lives up to the real thing. Back in 2018, I created a system for generating fake test data at Airbnb, and I was never quite satisfied with the outcome.

Get traffic

Thirdly, you need activity on the staging database. Having data there is not enough; you need something to trigger user flows and peck at APIs. At Graphite, this is our own engineering team using our application to create all our own PRs, reviews, and merges. At Airbnb, it was a mixture of API traffic replayed against specific services, coupled with begging engineers to “poke staging” and make sure their most recent merge looked functional. In someone else’s application, it might be QA testers or even a free tier of users. If possible, I’d strongly recommend finding a way for your traffic to be real-time humans.

Monitor for issues

Lastly, you need a way to detect and alert on regressions. At Airbnb, this was mostly done through Datadog monitors and automated server monitoring for statistical regressions. At Graphite, this is an engineer on our team who noticed that the comment button no longer works and reported it on our internal Slack. We also have the added benefit that the person spotting the regression is the same person who’s qualified to pause the pipeline and debug the issue - though I respect that not all products benefit from being so thoroughly dogfooded by the engineering team creating them.

If you can think of an answer to each of these requirements, I’d strongly recommend you consider implementing a staging bake. The upfront cost of setting one up will pay back tenfold, and anecdotally, I can assert that the maintenance cost is lower than any other approach I know to catch regressions.

Reflections

Choosing to build a staging bake process at Graphite took me longer than it should have. I was more afraid of drift than I should have been. I was worried that the maintenance cost of maintaining dual Postgres instances, each needing the same schema migrations, would become cripplingly annoying. I wondered if engineers on the team would ever become comfortable with the added wait time. In the end however, none of these things were true, and I’d say every member of our engineering team appreciates the staging-bake’s existence.