Monolith
A monolith is an application that is built, tested and deployed as one unit, whatever its size and however its code is organised inside. Most complaints made about monoliths are really about the missing boundaries inside one — a different problem with a cheaper fix — and a monolith with real module boundaries is the right default for most systems. The page opens with sixteen common complaints and where each one actually lives, then covers the two axes every architecture argument confuses, why one deployable is the default and the best argument against it, drawing and enforcing module boundaries, running a large monolith, where it genuinely runs out, five documented cases, and where it degrades.
- Deploy unit
- One artefact, often run in several roles
- At scale
- GitHub: ~2M lines, 1,000+ engineers, up to 20 deploys a day
- Failure mode
- Eroded module boundaries, not size
What is actually wrong — sixteen complaints about a monolith, and where each one lives
The word “monolith” usually comes up when something hurts. A change takes forty minutes to reach production. Nobody dares to touch billing. A bug in a report took checkout down with it. Each of these complaints names a real cost, and each is usually followed by the same proposal: split the system into services. But the complaints come from three different places. Some live in the code — in the boundaries between its parts, or the lack of them. Some live in the build, test and deploy tooling. Only the last few live in the fact that the whole thing ships as one unit. Splitting the deployment fixes only that last group. Here are sixteen complaints teams make every day, sorted by where they really live. Later sections cite the rows by number.
| # | The complaint | Where it actually lives | Fix it first by | When a separate service is the answer |
|---|---|---|---|---|
| 1 | “A change to invoicing broke the login page” | Boundaries: modules call each other’s internals | Give each module a public interface and fail the build on any other import | Never for this reason: a service with the same coupling breaks over the network instead |
| 2 | “Nobody knows what depends on billing” | Boundaries: the dependency graph exists only in people’s heads | Declare which module may depend on which, and check it in CI | Never: the graph is the prerequisite for any split |
| 3 | “Every feature touches files in ten folders” | Boundaries: code grouped by technical layer, not by what the business does | Regroup by module — move files, do not rewrite them | Never |
| 4 | “Half the codebase reads and writes the orders table” | Boundaries: shared tables and joins across modules | One owning module per table; everyone else asks that module | Only when the owner genuinely needs a different kind of store |
| 5 | “The common module is the biggest one” | Boundaries: a shared drawer that everything depends on and nobody owns | Split it: true base code with no dependencies stays, domain code moves to its owner | Never |
| 6 | “Two teams keep editing the same files” | Boundaries and ownership: modules do not match teams | An owner for every module; move boundaries to match teams | When teams already own modules and still block each other at release (row 15) |
| 7 | “Nobody understands the whole thing any more” | Boundaries: one global namespace | Modules small enough to read alone, each with a page of documentation | Never: nobody understands forty services either |
| 8 | “The test suite takes forty minutes” | Tooling: every change runs every test | Run only the tests of affected modules — the graph from row 2 — in parallel; fix the slowest tests | Rarely: test time is a CI problem |
| 9 | “A build takes forever” | Tooling: no incremental build and no cache | A build cache and module-level compilation | Rarely |
| 10 | “Merging to main is a fight” | Process: long-lived branches | Small pull requests, trunk-based development, a merge queue | Never |
| 11 | “Deploys are scary, so we do them once a week” | Process: big batches and no fast rollback | Deploy smaller and more often; canary releases; feature flags to separate deploy from release | Never: deploy frequency is not an architecture property — GitHub deploys its monolith up to 20 times a day |
| 12 | “A new engineer needs two days to run it locally” | Tooling: a manual setup | One scripted setup or a dev container | Never: forty services are harder to run locally |
| 13 | “Image processing needs sixteen times the CPU of everything else” | The shape: one build — but not necessarily one process pool | Run the same artefact as a separate worker pool on bigger machines | When that work needs different hardware, such as GPUs, or a different runtime |
| 14 | “A memory leak in reports takes down checkout” | The shape: one process, one blast radius | Separate pools per role (row 13), memory limits, timeouts, separate queues | When isolation must be guaranteed: a different availability target, or a compliance scope such as card payments |
| 15 | “Twelve teams wait for one release train” | The shape: one deployable | Rows 10–11 first — most release-train pain is batch size | When teams own modules, deploys are already frequent, and coordination is still the measured bottleneck |
| 16 | “This part would be far better in another language” | The shape: one runtime | Usually nothing — a second language is a second toolchain, a second hiring market and a second set of upgrades | When the case is technical and measured: machine learning in Python, a hard latency target, a library only one ecosystem has |
Two axes every architecture argument confuses
A monolith is an application whose code is built, tested and deployed as one unit. That is the whole definition. It says nothing about how big the codebase is or how many people work on it. It also says nothing about whether the code inside is well organised. A ten-person product and GitHub are both monoliths in exactly this sense.
The word became an insult more or less on 25 March 2014. That day James Lewis and Martin Fowler published the article that named microservices, and they defined the new style by contrast with the “monolithic application”. Within a few years “monolith” meant old, slow and tangled. But the tangled condition is older and has its own name. Brian Foote and Joseph Yoder called it the big ball of mud in 1997: a system with no recognisable structure, where everything is connected to everything. That is a property of the code, not of the deployment. A big ball of mud split into twenty services is still a big ball of mud — only now its connections cross a network.
So there are two questions, not one. How many deployables does the system have? And how well is its code divided into modules — parts with a clear job, a published interface and nothing reaching into their insides? The answers give four kinds of system. The table below shows what each one costs. Sam Newman’s vocabulary for the left column is worth knowing: a single-process monolith is any monolith, and a modular monolith is a single-process monolith whose modules are real. The bottom-right cell has a name too, and it is the one this page is mostly trying to keep you out of.
| Kind | What changes together | A call between parts | Consistency across parts | Moving a boundary |
|---|---|---|---|---|
| Big ball of mud | Anything with anything | A function call | One database transaction | Hard: nobody knows what depends on what |
| Modular monolith | One module | A function call through an interface | One database transaction, where you allow it | A refactor the compiler checks |
| Distributed monolith | Several services, released in a fixed order | A network call that can time out | A saga — or, often, nothing | A migration across repositories |
| Microservices | One service | A network call through a versioned API | A saga with compensating steps | A versioned API migration |
Why one deployable is the right default — and the best argument against it
The case for starting with one deployable is not nostalgia. A single process removes a whole category of problems that would otherwise have to be solved before the first line of business logic. Every row in the table below is work that a set of services needs and a monolith does not. None of it shows up on a product roadmap, and none of it is ever finished.
| Concern | Inside one process | Across a network |
|---|---|---|
| A call | A function call: nanoseconds, and it either returned or threw | About half a millisecond inside one data centre, and it can time out, fail halfway, or succeed after you gave up |
| Consistency across two parts | One database transaction | A saga, an outbox, compensating actions — and explaining “eventually” to the business |
| Moving a boundary | A refactor the compiler and the IDE check | A versioned API change across two repositories, two pipelines and two deploys in the right order |
| Debugging one request | One stack trace | Distributed tracing that someone has to build and keep working |
| Running it locally | One process and a database | Many services, their databases and a message broker — or a shared test environment |
| Deploying | One pipeline | One pipeline per service, and a compatibility matrix between their versions |
The deeper argument is about knowledge, and Martin Fowler made it under the name “MonolithFirst” on 3 June 2015. He reported two observations from the projects he had seen. Almost all the successful microservice stories started with a monolith that grew too big and was broken up. Almost all the systems built as microservices from scratch ended up in serious trouble. His explanation was that service boundaries are expensive to move and hard to get right, and that you get them wrong when you do not yet understand the domain — which, at the start of a product, you do not. A monolith lets you move boundaries cheaply while you are still learning where they belong. It defers the expensive commitment until there is evidence for it.
Six days later Fowler published the strongest reply on his own site. Stefan Tilkov’s “Don’t start with a monolith” argued the opposite from experience just as long. Splitting an existing monolith, he wrote, is extremely hard, because over the years every part comes to depend on shared libraries, shared domain objects and a shared database. And the start of a new system is exactly the time to think about carving it into pieces, because it is the last time that is cheap. Tilkov attached one condition: you should know the domain you are building for really, really well. The two essays are still the best short statement of the trade-off.
The most quoted defence of the style came from a small team. In “The Majestic Monolith” (29 February 2016) David Heinemeier Hansson described Basecamp: twelve programmers, millions of users, and a back catalogue of apps they had promised to maintain. His line was that every time you turn a collaboration between objects into a collaboration between systems, you accept “a world of hurt”. The argument is about proportion. A distributed system is a tax paid in people. Twelve programmers cannot afford to spend four of them on it.
Drawing module boundaries, and making them hold
Draw modules around what the business does, not around what kind of code a file is. Orders, billing, catalogue and users are modules. Controllers, services and repositories are not — they are layers, and a layer touches every feature. Two quick tests help. Can you describe the module’s job in one sentence without the word “and”? And does a typical feature change stay inside one module? In domain-driven design the same idea is called a bounded context: a part of the system inside which every term means one thing. “Order” means something different to the warehouse and to billing, and that difference is where the boundary goes.
A module owns four things: its code, its tables, its public interface and the events it publishes. Other modules may call the interface and listen to the events — nothing else. What crosses the boundary should be plain data and ids, not live objects. Passing an ORMObject-Relational Mapping — objects instead of SQLA library that hands you language objects where the database has table rows, so you write little or no SQL. The convenience costs control: the query it generates can be far heavier than the one you would have written yourself, and you only find out under load.Full article →All terms entity from one module to another looks harmless, but it hands over the first module’s whole storage layout: the receiver can now load related rows lazily and save changes the owner never sees. The same applies to the database. Each table has exactly one owning module, and no other module joins against it or holds a 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 into it. That is the rule the diagram at the top of the page shows being broken.
Dependencies between modules should form a graph without cycles: base code at the bottom, business modules above it, and nothing at the bottom that knows about anything above. Shopify’s experience adds a correction that is easy to miss. Its engineers found that developers sort code into modules by what its name suggests, not by how it runs. A model called “shop billing settings” lived in the billing package because of its name — but it also held the flag that marks a shop as fraudulent, which every request needs. It belonged at the base of the graph, available to everything. So check where a piece of code is used, not what it is called, before deciding which module owns it.
Making the boundaries hold
Nothing in a monolith enforces a boundary unless you make it. Nothing stops a developer importing a class from another module at two in the morning, and nothing tells them it was wrong. Between services the network enforces the boundary by being inconvenient. Inside a monolith you have to supply that inconvenience on purpose — in the language, in the build, or in a check that fails CI/CDContinuous Integration / Continuous Delivery — automatic build, test and deliveryEvery commit is built and put through the tests automatically (CI), and a build that passes travels to the servers the same way (CD). The point is not automation for its own sake: it is that a breakage shows up in minutes instead of on release day.Full article →All terms. What is available depends on the ecosystem:
| Ecosystem | What the language or build gives you | A check that fails CI |
|---|---|---|
| Java, Kotlin | Package-private classes; Maven or Gradle modules; Java modules (JPMS) with explicit exports | ArchUnit rules; Spring Modulith, which also verifies module structure and documents it |
| .NET | One project per module; internal types are invisible to other assemblies | NetArchTest or ArchUnitNET rules |
| Go | An internal/ directory can only be imported from inside its parent — enforced by the toolchain | Usually nothing more is needed |
| Ruby on Rails | Every constant is global; engines give some structure | Packwerk, Shopify’s dependency checker |
| PHP | Namespaces, with no visibility between them | Deptrac rules |
| Python | Only a convention: a leading underscore | import-linter contracts |
| TypeScript, JavaScript | Workspace packages whose exports field lists the public entry points | Nx module-boundary rules, dependency-cruiser, ESLint import rules |
The check with the best return is the one that fails the build. A dozen lines asserting that nothing outside billing may import billing.internal turn a rule everyone agrees with and nobody follows into a red build with a file and a line number. Without such a check, module boundaries last exactly as long as the person who cares about them stays on the team. For an existing codebase the usual way in is to record every current violation in a to-do file and fail only on new ones. Packwerk calls this declaring bankruptcy. It works, with one caveat Shopify published itself: a checker finds problems far faster than a team fixes them, so the to-do file needs an owner and a budget, or it only grows.
Running a large monolith: scaling, deploys and tests
A monolith scales out like anything else. If the application keeps no state between requests in its own memory — sessions in a database or a cache, files in object storage — you run as many copies as the load needs behind a load balancerspreads requests across serversA server or managed service that receives incoming requests and spreads them across several copies of an application, skipping any copy that fails its health check. It is also one more network hop with its own timeouts, so its settings and the application’s have to agree.All terms. Stack Overflow published its numbers for 9 February 2016: 209 million requests and 66 million page loads in a day, served by one ASP.NET application on eleven web servers, with an average question page rendered in 22.71 milliseconds. Its engineers added that the site could by then run on a single web server, and had done so by accident a few times. The limit they worked on was performance inside the application, not the number of deployables.
When a busy monolith runs out of room, the first thing to run out is usually the database, not the application. The answers are the same ones a set of services would need: caching, read replicas for queries that can tolerate slightly old data, and in the end partitioning the data by customer or region. Splitting the code into services does not remove this limit. It gives each service a smaller database and a new problem — the queries and transactions that used to span them.
One build does not have to mean one pool of identical processes. The same artefact can start as a web server, as a background worker for one queue, as a worker for a CPUCentral Processing Unit — the processorThe processor that executes a program’s instructions. Calling a task “CPU-bound” means it is limited by computation rather than by waiting on disk or network — and the two need opposite fixes, so the distinction is worth making early.All terms-heavy queue on bigger machines, or as the one scheduler that must never run twice. Each pool gets its own instance count, machine size, memory limit and restart policy. That answers rows 13 and 14 without a second codebase. Image processing gets sixteen times the CPU without the web servers getting any of it, and a memory leak in report generation restarts the report workers, not checkout — the 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 of a fault shrinks to one pool. Prime Video reached the same pattern from the other side in 2023: having merged a distributed service into one process, it ran several copies of it, each with a different subset of the work.
Deploy frequency is a property of batch size and automation, not of architecture (row 11). GitHub described its Rails monolith in 2023: nearly two million lines of code, more than a thousand engineers working on it every day, and deploys as often as twenty times a day. Even the framework is kept current continuously — every Monday a scheduled job opens a pull request that moves the application to the latest commit of Rails itself, and it ships within the week. What makes that possible is the machinery around the code: a merge queue that tests changes in the order they will land, canaryrelease to a few firstSending the new version to a small share of traffic first and watching before the rest follows. It buys the one thing a full release cannot: a failure that costs you one percent of users instead of all of them.Full article →All terms releases to a small share of traffic first, and feature flaga switch around new codeA switch that decides at run time whether a piece of code is active, so it can ship to production turned off and be turned on for a chosen audience later. It separates deploying from releasing, and it costs something: every live flag is a branch in the code that has to work in both states.Full article →All terms so that deploying code and switching a feature on are two separate decisions.
Tests are where module boundaries pay a second time. With a declared dependency graph (row 2), CI can work out which modules a change could affect and run only their tests — Shopify gave exactly this as one of the goals of its components. Without the graph, every change runs every test, and the suite grows with the codebase until people stop running it locally. The table lists what grows with a monolith and the fix that does not need a new architecture.
| What grows | Symptom | The fix inside the monolith |
|---|---|---|
| Test time | CI takes longer than a coffee, then longer than lunch | Affected-module test selection, parallel runners, quarantine for flaky tests |
| Build time | Every change rebuilds everything | A build cache; compiling modules separately |
| Merge conflicts | Main is red because two green changes collided | A merge queue; small pull requests |
| Deploy risk | Releases get bigger, rarer and scarier | Deploy every change; canary first; flags for unfinished features |
| Boot time | An instance takes minutes to start, so scaling up lags behind load | Load code lazily; keep start-up work out of the request path |
| Memory per process | Every instance carries every feature’s caches | Separate pools per role, each with its own limit |
Where a monolith genuinely runs out
There are real limits, and being honest about them is what separates an argument from a preference. They are rows 13–16, plus one that no row captures well: the number of teams. Melvin Conway observed in 1968 that a system’s structure ends up mirroring the communication structure of the organisation that builds it. With three teams, one deployable is a convenience. With thirty, the deployable becomes a meeting everyone has to attend. The table turns each limit into something you can measure before deciding — because each one has a cheaper answer to try first.
| Limit | The signal you can measure | Try first | When extracting a service pays |
|---|---|---|---|
| Scaling profile (row 13) | One kind of work dominates CPU or memory, and everything is provisioned for it | A separate process pool for that role | It needs different hardware or a runtime the monolith does not have |
| Isolation (row 14) | Incidents in which one feature took down another; an auditor’s scope that covers the whole application | Pools, timeouts, separate queues, memory limits | A part needs a different availability target, or must leave the compliance scope — card data under PCI DSS is the classic case |
| Release coordination (row 15) | Lead time dominated by waiting for other teams’ changes | Smaller batches, feature flags, module ownership | Teams own clean modules, deploy daily, and still block each other |
| Runtime (row 16) | A measured gain from another language, not a preference | A library, or a small helper process beside the monolith | Model serving, GPU work, a hard latency target |
| Team count | Dozens of teams; ownership disputes over shared files; a build nobody owns | Module ownership and a platform team for the build and deploy machinery | The modules are already clean, and the shared database and release are what teams trip over |
Extracting one service to solve one measured constraint is not the same decision as adopting microservices. The first is a single migration with a clear reason and a clear end. The second is an operating model for the whole organisation. How to do the first safely — a facade in front of the monolith, one capability moved at a time, the data split last — is the subject of the Monolith → Microservices page, and what the second costs is on the Microservices page.
Five cases, and what each one was missing
The five cases cover both directions. Two teams moved from services back to one process, one project merged its own microservices into a single binary, one company spent years making a giant monolith modular, and one site served hundreds of millions of requests a day from a single application. Each is documented by the engineers who did the work.
| Case | What happened | What it shows |
|---|---|---|
| Stack Overflow, 2016 | 209 million requests a day from one ASP.NET application on eleven web servers | The limit was performance inside the application, not the number of deployables |
| Segment, 2018 | Over 140 single-destination services merged back into one service and one repository | Services without tooling to test and ship shared changes across all of them cost more than they isolate |
| Istio 1.5, 2020 | Four control-plane services merged into one binary, istiod | Parts that are always released together and run by one team gain nothing from separate deployment |
| Prime Video, 2023 | A serverless monitoring pipeline hit a hard limit at about 5% of expected load; one process cut its cost by over 90% | Data that crosses a network on every frame pays for it on every frame |
| Shopify, 2019–2024 | A monolith built by over a thousand developers made modular with components and Packwerk | Boundaries drawn by domain names do not match how code runs, and a checker finds violations faster than teams fix them |
Segment’s story is the most quoted, because it went both ways. Its product forwarded customer events to more than a hundred partner 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. One shared queue meant that one slow partner delayed everyone, so the team gave every destination its own service and queue — a reasonable fix for a real problem. Then each destination got its own repository, to stop one broken test from blocking all the others. Shared libraries followed, and their versions drifted apart across dozens of repositories, because updating all of them was a risk nobody had time for. Each service had its own load pattern and needed scaling by hand. Eventually three full-time engineers spent most of their time just keeping the system alive. The team merged over 140 services into one, pinned 120 dependencies to a single version, and replaced live calls in tests with recorded traffic, so the whole suite ran in milliseconds instead of up to an hour. Improvements to the shared libraries went from 32 in the year before to 46 in the year after. The post also names what it cost: one bug can now crash every destination, and in-memory caches are spread across more than 3,000 processes.
Shopify is the long version, and the most useful one, because it is a modular monolith told from the inside. In 2019 its engineers explained why they chose to make the monolith modular rather than split it: a codebase worked on for over a decade by more than a thousand developers, with checkout and shipping-rate code free to call each other. They sorted thousands of files into a couple of dozen components, one per area of commerce, and in September 2020 released Packwerk to check dependencies between them in every pull request. In February 2024 the same team published a retrospective, and it is unusually frank. Packwerk’s privacy checks, which marked code public or private, were removed, because teams began polishing interfaces instead of fixing dependencies. When the team cleared every violation of one base package, it found a bug in Packwerk that showed it was probably the first user ever to finish a to-do file. Making that one package actually boot and pass its tests in isolation took many months. And components drawn around business domains did not match how the code runs, so they now group smaller packages drawn by function.
Where it degrades
A monolith fails slowly and quietly. Nothing breaks on the day a boundary is crossed for the first time; the cost arrives a year later, when nothing can be changed alone. Almost every failure is one of these:
- Joins and foreign keys across module tables, which couple modules through storage instead of interfaces (row 4).
- Packages by layer — controllers, services, repositories — so every feature’s surface is the whole codebase (row 3).
- A
commonorutilsmodule that everything depends on and nobody owns (row 5). - Module rules agreed in a document rather than checked in CI, so they last a quarter.
- A to-do file of known violations with no owner, which only grows.
- ORM entities passed between modules, handing over one module’s storage to another.
- A test suite slow enough that people stop running it locally, which removes the fast feedback that made the monolith cheap (row 8).
- Weekly releases defended as caution, when the size of the batch is the risk (row 11).
- One process pool for every kind of work, so the heaviest job sets the size of every machine (row 13).
- A split to fix coupling, producing a distributed monolith that keeps the coupling and adds the network.
The honest limit is people. A monolith keeps the cost of a boundary close to zero, and that is both its strength and its weakness: a boundary that costs nothing to cross is crossed whenever someone is in a hurry. Every tool on this page exists to make crossing it cost something again. Even with all of them, a monolith cannot give two teams separate databases, separate release days or separate on-call rotas — and at some number of teams, those are what the teams need. Below that number, the cost of a monolith is discipline; above it, the cost of services is engineers. Most products never reach the number, and the ones that do reach it with a modular monolith extract faster than the ones that arrive with a ball of mud.
The practical check takes an afternoon. List the modules and the team that owns each; if you cannot, start there. Search the queries for joins between tables owned by different modules. Generate the dependency graph and look for cycles. Find out whether any check fails CI when a module imports another module’s internals. Measure the test suite and ask whether it runs only what a change affects. Count the deploys in the last month. And for each complaint you hear about “the monolith”, find its row in the table at the top of this page before anyone opens a proposal to split it.
When to use it
Use it when
- The product is new and the domain boundaries are still being learned.
- One team, or a few teams that can release together without pain.
- Transactional consistency across features matters, and “eventually” would be a business problem.
- You would rather spend your complexity budget on the domain than on infrastructure.
- You are prepared to enforce module boundaries in the build, not in a document.
Avoid it when
- Many teams that already own clean modules still block each other on one release, and the cost is measured.
- One component needs different hardware or a different runtime, and a separate process pool is not enough.
- Parts of the system have genuinely different availability targets or compliance scopes.
- The code is already a ball of mud — then the first job is modules, and splitting it now would only distribute the mud.
Sources and method
What this page stands on
- 17 sources
- Checked October 13, 2026
Practices and where the terms come from
what a monolith is, the argument over starting with one, and the boundary tools- ArticleMartin Fowler — MonolithFirst (opens in a new tab)martinfowler.com · Jun 3, 2015
- ArticleStefan Tilkov — Don’t start with a monolith (opens in a new tab)martinfowler.com · Jun 9, 2015
- ArticleJames Lewis, Martin Fowler — Microservices (opens in a new tab)martinfowler.com · Mar 25, 2014
- ResearchBrian Foote, Joseph Yoder — Big Ball of Mud (opens in a new tab)PLoP 1997 · laputan.org · 1997
- ArticleDHH — The Majestic Monolith (opens in a new tab)Signal v. Noise · Feb 29, 2016
- BookSam Newman — Monolith to Microservices (opens in a new tab)O’Reilly · 2019
- DocumentationSpring Modulith (opens in a new tab)spring.io
- DocumentationArchUnit (opens in a new tab)archunit.org
- DocumentationShopify/packwerk (opens in a new tab)GitHub
The cases
the section “Five cases, and what each one was missing”- ArticleNick Craver — Stack Overflow: The Architecture - 2016 Edition (opens in a new tab)nickcraver.com · Feb 17, 2016
- ArticleAlexandra Noonan — Goodbye Microservices: From 100s of problem children to 1 superstar (opens in a new tab)Segment · Twilio · Jul 2018
- ArticleCraig Box — Introducing istiod: simplifying the control plane (opens in a new tab)istio.io · Mar 19, 2020
- ArticleMarcin Kolny — Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%Prime Video Tech · Mar 22, 2023
- ArticleKirsten Westeinde — Deconstructing the Monolith (opens in a new tab)Shopify Engineering · 2019
- ArticleUnder Deconstruction: The State of Shopify’s Monolith (opens in a new tab)Shopify Engineering · Sep 16, 2020
- ArticleGannon McGibbon, Chris Salzberg — A Packwerk Retrospective (opens in a new tab)Shopify Engineering · Feb 7, 2024
- ArticleAdam Hess — Building GitHub with Ruby and Rails (opens in a new tab)The GitHub Blog · Apr 6, 2023
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.