Monolith → Microservices
Migrating a monolith to microservices means moving an existing system’s capabilities into separately deployed services one at a time, without stopping the system. The migration that works is not a rewrite with a cutover date but a sequence of small extractions behind a facade, each justified by a measured constraint and each with a way back; the hard part is the data. The page opens with one extraction as sixteen steps, then covers what to extract first, six ways to move code without stopping, splitting the data, the model and the teams, running the migration and knowing when to stop, five documented cases, and where it degrades.
- Pattern
- Strangler fig (Fowler, 2004)
- Unit of work
- One extraction, with a way back at each step
- Hardest part
- Splitting the data
One extraction, start to finish — sixteen steps, how you know each is done, and the way back
A migration that works is not one big project. It is the same small project repeated: take one capability out of the monolith and put it into a service, with the system running the whole time. Each repetition has the same sixteen steps, in the same order. The column that matters most is the last one. It says how to undo each step, and for almost all of them the answer takes minutes. Later sections cite the steps by number.
| # | Step | Done when | The way back |
|---|---|---|---|
| 1 | Name the constraint and measure it — “payments waits 21 days for unrelated releases” | A number, a target and a date are written down | Nothing to undo. If there is no number, stop here: the work is modules, not services |
| 2 | Choose the capability to extract | Its job fits in one sentence without “and”, and few modules call it | Choose another — nothing has moved yet |
| 3 | Stand up the platform minimum: pipeline, tracing, logs, alerts, an on-call rota | An empty service reaches production through the pipeline and shows up in a trace | Nothing user-facing has changed |
| 4 | Put a facade in front of the monolith that forwards everything unchanged | All traffic passes through it, with errors and latency unchanged | Point traffic back at the monolith |
| 5 | Draw the module boundary inside the monolith: one public interface | A CI check fails on any new import of the module’s internals | A revert — it is still one deployable |
| 6 | Give the module its own schema: no joins or foreign keys across the line | A query check finds no SQL that crosses the boundary | A revert — it is still one database |
| 7 | Route every access through the interface, reports and batch jobs included | Nothing outside the module touches its tables | A revert |
| 8 | Make the module’s tests run on their own | Its suite passes with nothing else loaded | Nothing to undo |
| 9 | Build the service with its own store; backfill it and keep it in sync from the monolith’s database log | A reconciliation job reports no drift for a week | Switch the sync off — nothing reads the copy yet |
| 10 | Parallel run: send reads to both, serve the monolith’s answer, log every mismatch | No mismatches for a full business cycle — a week, a month end | Stop the experiment |
| 11 | Move reads to the service — 1%, 10%, 50%, 100% — behind a flag | Errors and latency stay at baseline with every read on the service | Flip the flag — the monolith’s data is still current |
| 12 | Move writes: the service becomes the source of truth and syncs changes back to the old tables | Every write goes through the service, and the reverse sync shows no drift | Flip writes back — the old tables are current, so the monolith can take over again |
| 13 | Point every remaining reader — reports, exports, other modules — at the service or an analytics copy | Query logs show no reads of the old tables for a month | Point the reader back |
| 14 | Stop the reverse sync and drop the old tables, after a backup | The tables are gone and nothing broke | Restore from the backup — the first step with no quick way back |
| 15 | Delete the module’s code from the monolith and the old route from the facade | The code is gone | A revert and a redeploy — no longer a flag |
| 16 | Measure the constraint from step 1 against its baseline, and decide what comes next | The number is published, and the next extraction is chosen — or the migration stops | — |
Before the first extraction: the reason and the candidate
Most migrations begin with a picture of the destination — a diagram of twelve services — and work backwards. That ordering is why so many of them run for two years and end with eight services, a monolith that still holds the important logic, and a team slower than when it started. The ordering that works begins with step 1: a named constraint you can measure today. Each reason people give for a migration either passes that test or points at cheaper work, and the table sorts the common ones.
| The reason given | Does it justify a service? | Check first |
|---|---|---|
| “The code is a mess” | No — a split follows the coupling that already exists | Module boundaries — rows 1–7 on the Monolith page |
| “Deploys are slow and scary” | No — deploy frequency is not an architecture property | The pipeline, smaller batches, feature flags — rows 8–12 there |
| “One part needs far more CPU or memory” | Sometimes | A separate process pool started from the same build |
| “One part needs another language or runtime” | Yes, if the gain is measured rather than preferred | A library, or a small helper process |
| “Card data must leave the audit scope” | Yes | Whether the auditor accepts a separate process and database instead |
| “Teams own clean modules and still block each other at release” | Yes — the case the style exists for | Batch size and flags, to be sure the wait is not just big releases |
| “The platform underneath is end of life” | It forces a move, not an architecture | Whether a port inside the monolith is cheaper |
| “Everyone else has microservices” | No | What the measured constraint is |
Step 2 picks the first candidate, and it should be neither the hardest piece nor the most important one. The first extraction is a rehearsal. Its job is to teach the team the pipeline, the tracing, the on-call rota and the data handover while a mistake is still cheap. So it wants few callers, a clear boundary and a small blast radiushow far the damage spreadsHow much of a system is affected when one thing goes wrong — a bad deploy, a crashed process, a leaked credential, an agent’s mistake. It is kept small with boundaries: separate processes, narrow permissions, staged rollouts. The trade-off is that every boundary which contains a failure also costs something to cross.All terms if it fails. The table scores the usual candidates.
| Candidate | Who calls it | How tangled its data is | If it breaks | Verdict |
|---|---|---|---|---|
| Notifications | Many modules, in one direction | Its own templates and send log | Messages arrive late | First — the classic rehearsal |
| Image processing | Uploads only | Files, not tables | Uploads are slower | First — often the scaling reason too |
| PDF and report generation | A few screens | Reads other modules’ data | Documents arrive late | Early, once it reads through interfaces |
| Search | Most screens | A copy of others’ data, already fed by changes | Results go stale | Early — it is a read model already |
| Authentication | Every request | The users table sits at the centre | Nobody can sign in | Later — once the platform is proven |
| Payments | Checkout and refunds | Money, with strict consistency | Money lost or taken twice | Later — often for compliance |
| Orders, the core domain | Everything | Joined to everything | The business stops | Last, or never |
From the second extraction on, order by the constraint from step 1, not by convenience. And keep each one small enough to land within a few weeks. An extraction that runs for six months has stopped being reversible in any practical sense, because the monolith it came from has moved on underneath it.
Six ways to move code without stopping the system
The best-known pattern has a botanical name. During a holiday in the Queensland rain forests in 2001, Martin Fowler saw strangler figs. The fig starts as a seed in a nook of a host tree, grows down to the ground and up to the light, and can end up standing on its own where the host tree was. A couple of years later he used it as a picture of how his colleagues replaced legacy systems, in a short post called “Strangler Application”. He later renamed it “Strangler Fig Application”, because people kept the violent word and forgot the plant. In August 2024 he rewrote the post, and the new version names four activities that Ian Cartwright, Rob Horn and James Lewis defined for any gradual replacement. Understand the outcomes you want. Decide how to break the problem into smaller parts. Deliver the parts successfully. And change the organisation so that this can go on.
The same post makes an argument teams often resist. A gradual replacement needs transitional architecture: facades, sync jobs and switches that exist only while old and new run side by side, and are deleted afterwards. It looks like waste. Fowler’s answer is that the reduced risk and the earlier value outweigh its cost. Steps 4, 9 and 12 are exactly this kind of code. Budget for them instead of resenting them.
The facade goes in first, before anything is extracted (step 4). With the facade in place, the first extraction is a routing change, and so is its rollback. Without it, the first extraction is a change to every caller, and the rollback is another change to every caller. Sam Newman’s “Monolith to Microservices” (2019) collects the strangler fig and five related patterns for moving code, and each fits a different situation:
| Pattern | How it works | Use it when | What it costs |
|---|---|---|---|
| Strangler fig application | A facade sends each route to the monolith or to the new service | The capability is reached from outside — a URL, a queue, an API call | A proxy to run; it only sees what crosses the edge |
| UI composition | Whole pages, or parts of a page, are served by the new service | The capability has its own screens | Two front-end stacks for as long as it lasts |
| Branch by abstraction | Inside the monolith, an interface lets old and new implementations coexist behind a switch | The capability is called from deep inside the code | An abstraction layer to add, and later to remove |
| Parallel run | Both implementations handle the same request; the old answer is served and the two are compared | Correctness matters and is hard to specify | Double the work, and side effects must be kept out of the new path |
| Decorating collaborator | A proxy passes the request to the monolith unchanged, then triggers new behaviour in the service from the response | New behaviour is added without touching the monolith — points earned on every order | Works only if the response carries enough data |
| Change data capture | The new service reacts to changes read from the monolith’s database log | Nothing in the monolith can be changed or called | It couples the service to the monolith’s schema |
Branch by abstraction: a seam inside the code
A facade only sees what crosses the edge of the system. Much of what you want to extract is called from inside it — a pricing function used by checkout, invoices and the admin pages. Branch by abstraction handles that case. Paul Hammant gave the technique its name while arguing for trunk-based developmenteveryone merges into one branch dailyA way of working in which every developer merges small changes into one shared branch at least once a day, hiding unfinished work behind feature flags rather than in long-lived branches. It needs a test suite that answers in minutes and is trusted; without one it only merges breakages faster.Full article →All terms, crediting Stacy Curl with the idea, and Fowler described the steps in January 2014. Put an interface in front of the old code. Move every caller onto the interface, one at a time. Build the new implementation behind the same interface — here, a client that calls the new service. Switch callers over with a flag. When nothing uses the old implementation, delete it, and later the interface too if it no longer earns its place. Every commit along the way keeps the system building and releasable, so no long-lived branch ever has to be merged.
Parallel run: proving the new code before trusting it
A rewrite is only as good as its fidelity to the old behaviour, and the old behaviour is rarely written down. A parallel run (step 10) measures it instead of guessing. Every request, or a sample of them, goes to both implementations. The user gets the old answer. The new answer is compared with it, and every difference is recorded with enough context to debug. GitHub built a library for this, Scientist, and described using it to replace the code that merges pull requesta change proposed for reviewA request to merge one branch into another, where the change is reviewed, discussed and checked by the pipeline before it reaches the main branch; GitLab calls the same thing a merge request. Its cost is waiting: a pull request that sits unreviewed for days is where long-lived branches and painful merges come from.Full article →All terms. After four days of fixing what the experiment found — including a feature missing from the new merge engine altogether — the new code ran for 100% of requests for 24 hours with no mismatches and no slow cases. Tens of millions of merges had been checked before the old path was switched off. Khan Academy did the same at the edge of its system and called it side-by-side testing: its gateway sent most traffic for a request to the monolith and some to the new services, and compared the results until every difference was fixed.
Splitting the data — the migration itself
Extracting the code is a week of work. Extracting the data is the migration. It is also where teams that skipped this section end up with two services on one database — the outcome the whole exercise was meant to avoid. Newman describes three orders: split the database first, split the code first, or do both at once. He warns against doing both at once. In his experience code first is the most common, because it shows results sooner. Its usual failure in practice is stopping there, with the new service still reading the monolith’s tables.
The sixteen steps avoid both traps by moving one thing at a time. The split starts inside the monolith, where it is cheap (steps 6–7). Separate the module’s tables logically — same database, its own schema, no joins or foreign keya column pointing at another table’s rowA column whose value must match the key of a row in another table — an order’s customer id must name a customer who exists. The database refuses any row that breaks the rule, whoever writes it; across services, where no single database can check it, the rule becomes the application’s job.Full article →All terms across the line. This alone surfaces every hidden dependency, because every crossing now fails a check, and each one is fixed while the old system keeps running. Then the data is copied and kept in sync (9), reads move (10–11), writes move (12), the last readers move (13), and only then is anything deleted (14).
GitHub ran exactly this at scale between 2019 and 2021, on the database cluster that held users, repositories, issues and pull requests. First came virtual partitions. Tables that belong together were grouped into schema domains, listed in a plain YAML file in the Rails application. Two linters enforced the groups: one failed any query that referenced tables from two domains, the other any transaction that spanned them. Existing violations were annotated as exemptions, which turned them into a visible backlog. A domain with no exemptions left was ready to move. The physical move used Vitess, or a custom cutover over MySQL replication that switched writes in a few tens of milliseconds, at the quietest hour of the day; one cutover moved 130 of the busiest tables at once. In 2019 the cluster answered 950,000 queries a second. In 2021 the same tables, spread over several clusters, answered 1,200,000 — and the average load on each host had halved.
Not every database splits along clean lines, and Newman’s book names the patterns for the awkward cases. These seven come up in almost every migration:
| Pattern | What it does | When it fits | The catch |
|---|---|---|---|
| Database view | The new service reads the monolith’s tables through a view that hides their layout | A read-only need, early on | Still coupled to the monolith’s database |
| Database wrapping service | A thin service in front of tables nobody can untangle yet | Callers must stop reaching the tables directly | The mess is hidden, not removed |
| Change data ownership | The service becomes the owner; the monolith calls it instead of the tables | The capability has moved and its data should follow (step 12) | Every monolith query on those tables must be rewritten |
| Synchronize data in application | The application writes to both stores while the move runs | No database log is available to sync from | Two writes are not a transaction — reconcile, or drift |
| Tracer write | The new store becomes the source of truth for one slice of data at a time | The data is too large or too busy to move in one step | Two sources of truth until the last slice moves |
| Split table | A table whose columns belong to two capabilities becomes two tables | One “customers” table holds both account and shipping data | Every query that read both halves changes |
| Move foreign-key relationship to code | The join becomes a lookup through the other service; integrity is checked in code | Every cross-boundary key found in step 6 | Slower reads, and orphans the database no longer prevents |
Reports are where a data split is usually found to be unfinished (step 13). A report that joins six modules’ tables worked for years because everything sat in one database. After the split there are two honest answers. Either the report calls the services that own the data, or it reads an analytics copy fed by events or by the database logs. Newman recommends a schema built for reporting rather than queries against the operational stores. Either way, find every reader before step 14. The one monthly export nobody remembered is a classic cause of a rollback on the first of the month.
The model and the teams: the anti-corruption layer and Conway’s law
The new service will need data from the old system, and the old system’s model is usually shaped by fifteen years of accidents. If the new service speaks that model, it inherits it permanently, and there was little point extracting it. Eric Evans named the defence in “Domain-Driven Design” in 2003: an anti-corruption layer. It is a translation boundary — a thin piece of code that speaks the legacy model on one side and the new service’s model on the other, and belongs to the new service. It is deliberately ugly, deliberately isolated and deliberately temporary. When the legacy source finally goes away, one component is deleted, instead of a model being unpicked from every file. Here is what it typically translates:
| In the legacy model | What the layer does | In the new service |
|---|---|---|
A status column with eleven meanings, some set only by one batch job | Maps each code to a named state, and rejects the codes it does not understand | Explicit states with the transitions allowed between them |
| A customer record that is also the shipping address | Splits it into two concepts | A customer, and the addresses they ship to |
| Times stored as local-time strings | Parses them with the server’s historical time zone | UTC timestamps |
| Amounts stored as floating-point numbers | Rounds once, explicitly, and logs what it rounded | Integer minor units plus a currency code |
Melvin Conway observed in 1968 that a system’s structure ends up mirroring the communication structure of the organisation that built it. In a migration this is not a curiosity; it is the strongest force in the room. If four teams share ownership of the extracted service, it will grow four internal factions and a coordination meeting, and it will not be independently deployable whatever the diagram says. Used deliberately, the same force becomes a tool. Jonny LeRoy and Matt Simons named it the inverse Conway maneuver in 2010: change the team boundaries to the ones you want the services to have, and the architecture follows.
It also sets the honest upper limit on the migration. You cannot end with more services than you have teams to own them, and a service nobody owns rots faster inside a distributed system than it ever did inside the monolith. So step 3 includes an on-call rota on purpose. The owning team runs the new service from the day it takes its first request — Amazon’s rule, in Werner Vogels’s words, “You build it, you run it.”
Running the migration: capacity, pace, and when to stop
The migration competes with feature work for the same people, and that competition is decided by whoever is in the room when the quarter is planned. A migration run in the background by whoever has spare time takes three years and never finishes. A migration run as a total freeze on features loses the argument at the first commercial deadline. What survives is a stated share of capacity — one team, or a fixed fraction of every team’s quarter — with each extraction landing as a visible result rather than as progress on a plan.
Stop the old code growing while you move it. Khan Academy’s rule during its rewrite was that every new feature written in the old Python code needed the personal approval of its head of engineering, because every new line was one more line to port. The same project measured its progress by what had moved, not by what had been built: the share of traffic served by the new backend, the number of APIApplication Programming Interface — contract between programsThe rules one program follows to call another: which requests it may send, in what shape, and what comes back. To the caller it is a contract — everything behind it stays a black box, which is exactly what lets the other side be rewritten without breaking you.Full article →All terms fields completed, and the scope that grew as the work was understood. The table lists the measures that tell a working migration from a stalled one.
| Measure | Healthy sign | Warning sign |
|---|---|---|
| The constraint from step 1 | Moves within weeks of each extraction | Unchanged after the third extraction |
| Lead time in the extracted area | Shorter than before | Longer — changes now wait for two pipelines |
| Features that touch the monolith and a service together | Rare, and getting rarer | Most features |
| Share of traffic on the new path | Rises step by step | Stuck at the same share for months |
| Services per owning team | A few | More services than people |
| Incidents that cross the boundary | Falling | Rising with each extraction |
Nothing in the pattern requires the monolith to disappear, and treating its disappearance as the goal is how a migration outlives its justification. Each extraction was justified by a constraint. When the remaining pieces are not blocking anything, the remaining extractions have no argument behind them except symmetry. A permanent hybrid — a few services around a well-modularised core that still holds most of the domain — is a common and respectable end state. In most organisations it is also the point of best return: the parts with a real need for independence have it, and the rest are not paying for a network they never needed. SoundCloud’s engineers said as much in 2015: some of what the monolith did was so small and stable that it might be cheaper to keep it there forever.
Measure the migration against the constraint you named in step 1, not against the number of services. If payments now ships twice a week instead of once a month, that extraction paid for itself, and you can say so with a number. If the service count went from one to nine and lead time did not move, nine services are not progress. They are nine deployment pipelines.
Five cases, and what each one was missing
The five cases span twenty years, from the migration that shaped the word to one that finished in 2021. Each is told by the people who did the work, and each shows a different step of the sixteen carrying the weight.
| Case | What happened | What it shows |
|---|---|---|
| Amazon, 2001–2006 | The Obidos monolith could no longer scale; shared databases had left no isolation and no clear owner | The rule that came out of it was about data and ownership, not size |
| Netflix, 2008–2016 | A database corruption stopped DVD shipping for three days; seven years later the last data-centre pieces were shut down | Billing and customer data moved last, and that took years on its own (steps 12–14) |
| SoundCloud, 2012–2015 | A value-stream map showed features took two months, mostly waiting; new features went to services, old ones stayed slow | Measuring the constraint first (step 1) — and that new services alone do not fix old code |
| GitHub, 2015–2021 | Rewrites proven by parallel runs; the main database split by schema domains and linters, then moved | Logical separation before physical (steps 6, 10) |
| Khan Academy, 2019–2021 | A Python 2 monolith ported to 26 Go services; 95% of traffic on the new backend by 30 August 2021 | A forced move done incrementally, with side-by-side testing and a freeze on old-code growth |
Amazon’s is the story the vocabulary came from. In a 2006 interview Werner Vogels described Obidos, the application that had held all of Amazon’s business and display logic since the company started. For years the scaling effort went into the databases behind it, until in 2001 the front end itself could no longer scale. The deeper problem was sharing. The databases were shared by many teams and processes, so nothing had isolation and nothing had a clear owner, and both the front end and the back end were stuck. The answer was a service-oriented architecture with one rule above the others: data is encapsulated with the logic that operates on it, reachable only through the service’s published interface, with no direct database access from outside. It grew into hundreds of services. Read the rule again and notice what it does not mention. It says nothing about how small a service should be. The constraint was ownership, and so was the fix.
Netflix started in August 2008, when a major database corruption meant it could not ship DVDs for three days. The lesson it drew was to move away from vertically scaled single points of failure such as one relational database in its own data centre. It chose not to lift the existing systems into the cloud unchanged, because that would have moved all the old problems with them. Instead it rebuilt: from a monolithic application to hundreds of microservices, with a denormalised data model on NoSQLdatabases that are not SQL tablesAn umbrella name for databases that do not store relational tables queried with SQL: key-value, document, wide-column and graph stores. Most of them place each record on a machine by a key chosen at design time, which makes queries carrying that key cheap at any size and most other queries expensive or impossible.Full article →All terms stores. Every customer-facing system had moved before 2015. Billing and customer data took the time after that, and the last data-centre pieces were shut down in early January 2016 — seven years after the start. The part that took longest was not the services. It was the data with the strictest rules attached to it.
Where it degrades
A migration rarely fails on one day. It fails by stalling: half the steps done for many capabilities, and all the steps done for none. Almost every stalled migration shows several of these:
- A big-bang rewrite beside the running system, where all the risk arrives on one date and the old system keeps moving.
- No named constraint, so success is counted in services created (step 1).
- Code extracted and the database left shared — a distributed monolithservices that must still ship togetherA system split into services that still cannot be deployed independently — they share a database, must be released in a fixed order, or call each other synchronously for every request. It keeps the coupling of a tangled monolith and adds network failures, which is why it is worse than either a monolith or real microservices.Full article →All terms with extra steps (steps 9–14 skipped).
- Starting with the hardest, most central capability, so the team learns the mechanics where mistakes cost most (step 2).
- No facade, so every extraction and every rollback is a change to every caller (step 4).
- Extractions that run for months, which stop being reversible because the monolith moves underneath them.
- Dual writes with no reconciliation, so the two stores drift apart silently.
- A parallel run that also performs the side effects, doubling what it was meant to observe (step 10).
- The new service speaking the legacy data model, which imports the mess it was meant to escape.
- Old tables dropped before every reader has moved, found by the monthly report (steps 13–14).
- New features still landing in the old code, so the work to port grows faster than the port.
The honest limit is that the method makes a migration safe, not short. Every step on this page exists to keep the system working and the way back open, and each of them costs time: a facade, a sync, a parallel run, a month of logs before a table is dropped. Netflix took seven years. A team that needs the result by a fixed date cannot buy it with a better pattern, only with a smaller scope. Most of the time the right scope is smaller than the plan — a handful of extractions, each paying for itself, around a monolith that stays.
The practical check takes an afternoon. Ask for the constraint behind the migration and its number today. List the extractions in progress and the step each one has reached; more than two in progress at once is a warning. Check whether a facade sits in front of the monolith. For each extracted service, find out whether its old tables still exist, and who still reads them. Search for code that writes to two stores in a row. Count the new features added to the monolith last quarter. And for the next extraction, write down its way back before writing any code.
When to use it
Use it when
- You can name a specific constraint the current shape imposes, and measure it.
- Automated deployment and tracing already work, so an extracted service can be run from its first day.
- There is a team ready to own the extracted service in production.
- Delivery has to continue during the migration — a facade keeps every step reversible.
- A permanent hybrid is an acceptable end state rather than a failure.
Avoid it when
- The reason is tangled code — make modules inside the monolith first; it is cheaper and a prerequisite anyway.
- Nobody can be spared to own the new services, so they will be shared and therefore coupled.
- The plan is a rewrite with a cutover date rather than a sequence of reversible steps.
- The data cannot be split, because the domain genuinely needs transactional consistency across the boundary.
- The migration would be measured in services created rather than in the constraint removed.
Sources and method
What this page stands on
- 14 sources
- Checked October 13, 2026
Practices and where the terms come from
the strangler fig, the extraction patterns and the inverse Conway manoeuvre- ArticleMartin Fowler — Strangler Fig (opens in a new tab)martinfowler.com · Aug 22, 2024
- ArticleMartin Fowler — BranchByAbstraction (opens in a new tab)martinfowler.com · Jan 7, 2014
- BookSam Newman — Monolith to Microservices (opens in a new tab)O’Reilly · 2019
- BookEric Evans — Domain-Driven Design (opens in a new tab)Addison-Wesley · 2003
- ArticleMelvin E. Conway — How Do Committees Invent? (opens in a new tab)Datamation · Apr 1968
- ArticleJonny LeRoy, Matt Simons — Dealing with creaky legacy platformsCutter IT Journal · Dec 2010
The cases
the section “Five cases, and what each one was missing”- ArticleJim Gray — A Conversation with Werner Vogels (opens in a new tab)ACM Queue · Jun 30, 2006
- ArticleYury Izrailevsky, Stevan Vlaovic, Ruslan Meshenberg — Completing the Netflix Cloud Migration (opens in a new tab)Netflix · Feb 12, 2016
- ArticlePhil Calçado — How we ended up with microservices (opens in a new tab)philcalcado.com · Sep 8, 2015
- ArticleVicent Martí — Move Fast and Fix Things (opens in a new tab)The GitHub Blog · Dec 15, 2015
- ArticleJesse Toth — Scientist: Measure Twice, Cut Once (opens in a new tab)The GitHub Blog · Feb 3, 2016
- ArticleThomas Maurer — Partitioning GitHub’s relational databases to handle scale (opens in a new tab)The GitHub Blog · Sep 27, 2021
- ArticleKevin Dangoor — Technical choices behind a successful rewrite project (opens in a new tab)Khan Academy Blog · Dec 9, 2021
- ArticleMarta Kosarchyn — Beating the odds: Khan Academy’s successful monolith→services rewrite (opens in a new tab)Khan Academy Blog · Dec 9, 2021
Researched with Claude. Finding and cross-checking the primary sources was done with Claude (Anthropic). Selection, writing and fact-checking: the Anatomy of IT Product editors.
Found this useful?
Share it with someone who is working on the same problem.